简介

Groovy的元编程是指groovy运行时,可以理解成编写代码所执行的时期,也就是Runtime。在比如有解释执行的Js,编译执行的java,还有运行时期执行的代码java反射。

Groovy运行时的处理逻辑如下图:
untitled diagram.png

元编程

按照上图所示,我们可以自己写一个Demo,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Study {
public static void main(String[] args) {
def person = new Personal(name: "Silence", age: 26)
println(person.printName())
println(person.printAge())
println(person.sex)
}
}

class Personal {
public String name
public int age

public String printName() {
return "My name is $name"
}

public String printAge() {
return "My ag is $age"
}

}

输出结果如下:

1
2
3
4
5
6
7
My name is Silence
My ag is 26
Exception in thread "main" groovy.lang.MissingPropertyException: No such property: sex for class: Personal
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:65)
at org.codehaus.groovy.runtime.callsite.GetEffectivePogoPropertySite.getProperty(GetEffectivePogoPropertySite.java:87)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java:329)
at Study.main(Study.groovy:9)

可以看到前两个日志是正常输出的,第三个控制台则提示MissingPropertyException。也就是上图中,所以逻辑都没进去导致异常输出。

我们在看看如果我们复写invokeMethod方法时的输出效果,代码修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Personal {
public String name
public int age

public String printName() {
return "My name is $name"
}

public String printAge() {
return "My ag is $age"
}

@Override
Object invokeMethod(String name, Object args) {
return "this invoke method is $name,this params is $args"
}

}

控制台输出如下:

1
2
3
My name is Silence
My ag is 26
this invoke method is sex,this params is []

然后我们看看重写methodMissing方法后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Personal {
public String name
public int age

public String printName() {
return "My name is $name"
}

public String printAge() {
return "My ag is $age"
}

@Override
Object invokeMethod(String name, Object args) {
return "this invoke method is $name,this params is $args"
}

Object methodMissing(String name, Object args) {
return "this Missing method is $name,this params is $args"
}

}

输出结果如下:

1
2
3
My name is Silence
My ag is 26
this Missing method is sex,this params is []

可以看出,两个方法都重写时,优先调用methodMissing。如果找不到才会调用invokeMethod。

MetaClass

Metaclasses在方法解析中起到核心作用,对于来自groovy代码的每个方法调用,Groovy将找到给定对象的MetaClass,并通过MetaClass#invokeMethod将方法解析委托给Metaclasses,不应该与GroovyObject#invokeMethod混淆,后者恰好是Metaclasses最终可能调用的方法。

注入新字段

现在我们通过metaClass给Personal注入一个新属性看看:

1
2
3
4
5
Personal.metaClass.sex = "男"
def person = new Personal(name: "Silence", age: 26)
println(person.printName())
println(person.printAge())
println(person.sex)

可以看出我们注入了一个sex的字段,输入结果如下:

1
2
3
My name is Silence
My age is 26

当然了,注入静态变量也是类似,加上static即可。

注入新方法

看到我们输入性别的时候,发现好像和上面对比,不工整,那就自己整一个工整的,修改代码:

1
2
3
4
5
6
7
8
Personal.metaClass.sex = "男"
Personal.metaClass.printSex = { ->
"My sex is $sex"
}
def person = new Personal(name: "Silence", age: 26)
println(person.printName())
println(person.printAge())
println(person.printSex())

输出结果可想而之:

1
2
3
My name is Silence
My age is 26
My sex is

这下工整了~。当然了,注入静态方法也是类似,加上static即可。

总结

可以看出,我们可以通过metaClass给类新增变量,新增方法。那么这个和直接在类这种提供有什么区别呢?
举个例子:引入第三方库,其中的某个类有我们想用的方法,但是这个方法又不够全,想给里面添加点自己的方法。通常是extends它进而扩展,但是如果是final 类,任何类不能继承它,只能用它已有的方法。这个时候groovy的动态添加就有了大展身手的地方。不修改、不继承,通过metaclass变相的对原有的类进行了扩展。

参考

groovy中的元编程
Groovy笔记之元编程