Groovy语法学习(七)MOP探索之方法调用流程

groovy是一门具有元对象协议(Meta Object Protocol)或称 MOP的语言。在运行时向一个对象传递方法,或者消息时,这个协议使对象可以作出影响它自己的状态或者行为的特定选择。简单的说我们可以在运行时改变、增减类或者对象的方法、属性等,让其行为在运行时进行改变。这个在java里看起来四虎有些不可思议,但在groovy里可以简单的实现。
我们先看一张图,然后通过例子来理解一下。
这里写图片描述

主要关注这几个方法。

  • methodMissing //方法丢失
  • propertyMissing //属性丢失
  • 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会直接抛出异常。

二、实现GroovyInterceptable接口这三个方法的调用关系。

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
    }

结果和之前完全一致,不会抛出异常,这样使用就成功绕过了拦截。

三、与metaClass结合的调用关系

我们把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)
        }

}

方法调用流程就先总结这些,欢迎评论指正。

参考资料:

猜你喜欢

转载自blog.csdn.net/a568478312/article/details/79912881