groovy是一门具有元对象协议(Meta Object Protocol)或称 MOP的语言。在运行时向一个对象传递方法,或者消息时,这个协议使对象可以作出影响它自己的状态或者行为的特定选择。简单的说我们可以在运行时改变、增减类或者对象的方法、属性等,让其行为在运行时进行改变。这个在java里看起来四虎有些不可思议,但在groovy里可以简单的实现。
我们先看一张图,然后通过例子来理解一下。
主要关注这几个方法。
invokeMethod //调用
class Person {
def salary
def develop() {
println 'develop'
}
def methodMissing(String name, def args) {
println "methodMissing $name,$args"
return null
}
def propertyMissing(String name) {
return null
}
@Override
Object invokeMethod(String name, Object args) {
System.out.println "invokeMethod $name,$args"
return null
}
}
new Person().develop()
new Person().develop1123()
new Person().salary1
结果:
develop
methodMissing develop1123,[]
propertyMissing salary1
在属性丢失或者方法丢失的时候没有调用invoke方法,如果把
- methodMissing
- propertyMissing
这两个方法去掉
结果就变成了:
develop
invokeMethod develop1123,[]
Caught: groovy.lang.MissingPropertyException: No such property: salary1 for class: Person
结论:在方法丢失时,在没有methodMissing方法会调用invoke方法,在属性丢失没有propertyMissing会直接抛出异常。
class Person implements GroovyInterceptable {
def salary
def develop() {
println 'develop'
}
//方法丢失
def methodMissing(String name, def args) {
println "methodMissing $name,$args"
return null
}
//属性丢失
def propertyMissing(String name) {
println 'propertyMissing'
return null
}
@Override
Object invokeMethod(String name, Object args) {
System.out.println "invokeMethod $name,$args"
return null
}
}
new Person().develop()
new Person().develop1123()
new Person().salary1
结果:
invokeMethod develop,[]
invokeMethod develop1123,[]
invokeMethod println,[propertyMissing]
可以看到,所有的方法都被invokeMethod拦截了,注意在invokeMethod方法中,我使用了java的日志输出方式,这是因为如果直接使用println就会抛出StackOverflowError的异常,很容易就能想到这是因为方法循环调用的原因。如何绕过拦截,这时候就需要使用metClass了。我们把invokeMethod方法修改成这样。
@Override
Object invokeMethod(String name, Object args) {
// System.out.println "invokeMethod $name,$args"
metaClass.invokeMethod(this,'println', "invokeMethod $name,$args")
return null
}
结果和之前完全一致,不会抛出异常,这样使用就成功绕过了拦截。
我们把Person类的metaClass的invokeMethod方法来修改一下
class Person implements GroovyInterceptable {
def salary
def develop() {
println 'develop'
}
//方法丢失
def methodMissing(String name, def args) {
println "methodMissing $name,$args"
return null
}
//属性丢失
def propertyMissing(String name) {
println 'propertyMissing'
return null
}
@Override
Object invokeMethod(String name, Object args) {
// System.out.println "invokeMethod $name,$args"
metaClass.invokeMethod(this, 'println', "invokeMethod $name,$args")
return null
}
}
Person.metaClass.invokeMethod = {
name, args ->
System.out.println "metaclass invokeMethod $name,$args"
}
new Person().develop()
new Person().develop1123()
new Person().salary1
结果:
metaclass invokeMethod println,[invokeMethod develop,[]]
metaclass invokeMethod println,[invokeMethod develop1123,[]]
metaclass invokeMethod println,[invokeMethod println,[propertyMissing]]
metaClass的优先级更高了,直接把前面的拦截了。
我们可以利用metaClass来修改本身类的方法,在运行时修改类的行为。
class Person implements GroovyInterceptable {
def salary
def develop() {
println 'develop'
}
}
def p =new Person()
Person.metaClass.develop={
println 'static newDevelop'
}
p.metaClass.develop={
println 'instance newDevelop'
}
p.develop()
def newPerson=new Person()
newPerson.develop()
p.metaClass.salary=100
println p.salary
println p.@salary
p.metaClass.newSalary=50
println p.newSalary
println p.@newSalary
结果:
instance newDevelop
static newDevelop
100
null
50
Caught: groovy.lang.MissingFieldException: No such field: newSalary for class: Person
结论:使用类的metaClass会影响之后新建对象的行为,使用实例的metaClass只会改变当前对象的行为。直接为metaClass指定新的属性,只是产生新的方法,不会产生新的属性。
最后注意一点,如果要这样使用,需要使用闭包的delegate来拿到对应的metaClass,比如这里的delegate也就是p。
def p=new Person()
p.metaClass.invokeMethod = {
name, args ->
System.out.println "metaclass invokeMethod $name,$args"
def method = delegate.metaClass.getMetaMethod(name, args)
if (method) {
method.invoke(delegate, args)
}
}
方法调用流程就先总结这些,欢迎评论指正。
参考资料: