Groovy方法拦截,注入,合成,委托和动态类

首先说下调用方法的方式:

class Psrson{

    def name

    def dream() {
        System.out.println 'i have a dream'
    }
}

    1
    2
    3
    4
    5
    6
    7
    8

    第一种方式:
    def p = new Psrson(‘Test’)
    p.dream()

    第二种方式:
    def p = new Person(‘Test’)
    p.invokeMethod(‘dream’,null)

    第三种方式:
    def p = new Person(‘Test’)
    MetaMethod m = p.metaClass.getMetaMethod(‘dream’,null)
    m.invoke(p,null)

方法拦截:

Demo地址:https://github.com/zhaopingfu/listener8

    第一种方式:
    类实现GroovyInterceptable,并重写Object invokeMethod(String name, Object args)

    在这里每次调用该类的方法,都会先走invokeMethod方法,当然,调用一个类中不存在的方法,也会走这个方法,这个时候是不会出现MissingMethod方法的

    Object invokeMethod(String name, Object args) {
        System.out.println 'invoke'
        //是否有这个方法,有就调用,没有就来个提示
        if (metaClass.invokeMethod(this, 'respondsTo', name, args)) {
            metaClass.invokeMethod(this, name, args)
        } else {
            //do some
            System.out.println 'missing method'
        }
    }
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10

    总结:当一个类实现GroovyInterceptable后,在改类的对象上调用任何方法都会先执行invokeMethod方法,在invokeMethod方法里进行方法的分发,注意不要在invokeMethod里调用printl n,因为调用println也会走进invokeMethod,导致崩溃,所以应该调用System.out.println

    第二种方式:使用metaClass

        1、在单个对象上进行拦截

        def p = new Psrson('Test')
        p.metaClass.dream = {
            System.out.println 'replace dream'
        }
        p.dream()
            1
            2
            3
            4
            5

        2、在类上进行拦截

        Person.metaClass.dream = {
            System.out.println 'replace dream'
        }

        举例:
        String.metaClass.plus = {
        CharSequence i ->
            i
        }

        println("123" + "abc")
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11

        这里修改了String上面的+语法,只要,所以打印出来的是abc

    注意:当一个类或者对象覆盖了metaClass的invokeMethod方法后,那么这就相当于这个类实现了GroovyInterceptable,在它上面调用方法都会走到invokeMethod里面来,如果重写了,要手 动的将方法进行分发

    第三种方式:
    重写def methodMissing(String name, def args),def propertyMissing(String name, def arg),def propertyMissing(String name)

    当在一个对象上调用类中不存在的方法或者属性,会走到上面的方法中,看方法名也可以知道是什么意思了

    def methodMissing(String name, def args) {
        println "methodMissing"
        return "没有${name}这个方法"
    }
        1
        2
        3
        4

方法注入:运行时元编程

Demo地址:https://github.com/zhaopingfu/listener9

    第一种方式.category 分类注入:灵活,可控性高,对性能有影响

        1、static方法的方式

        class Req {
            static def get(String self) {
                self.toURL().text
            }
        }

        use(Req) {
            println "https://www.baidu.com/".get()
        }
            1
            2
            3
            4
            5
            6
            7
            8
            9

        这里使用use可以使用为字符串注入的get方法

        2、注解的方式

        class Req {
            static def get(String self) {
                self.toURL().text
            }
        }

        @Category(String)
        class StringUtils {
            def get() {
                toString()
            }

            def toUpperCase() {
                'toUpperCase'
            }
        }

        //谁写在后面执行谁的get方法
        use(Req, StringUtils) {
            println "https://www.baidu.com/".get()
        }
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21

        假如字符串已经有了toUpperCase方法,我们在StringUtils里面也有一个toUpperCase方法,那么在use里调用toUpperCase会调用谁的呢?是这样的,先在StringUtils里面找,如果有就执行,没有就去Req里面找,Req里面也没有,才回去String本身里面去找

    第二种方式.expandoMetaclass

    使用metaClass注入方法可以使用xxx.metaClass.xx <<{} 和xxx.metaClass.xx = {},这里我们推荐使用”=”,因为如果一个类中已经有了一个方法,再使用”<<”注入的话,就会报错

        1、注入对象方法

        str.metaClass.get = {
            println delegate
            delegate.toString().toURL().text
        }
        def str = "https://www.baidu.com"
        println str.get()

        def str1 = new String("https://www.baidu.com")
        println str1.get()
        "https://www.baidu.com".get()
        println "https://www.baidu.com".get()
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11

    这里的str1调用get方法是会报错的,因为他是new出来的,而其他的jvm中已经帮我们做了优化,为了节省内存,因为他们都是一样的,所以他们都可以调用get方法

        2、注入静态方法

        String.metaClass.'static'.printlnClass = {
            println "================="
            println delegate
        }
        "www.baidu.com".printlnClass()
        String.printlnClass()
            1
            2
            3
            4
            5
            6

    和普通方法类似,只要在前面加入一个’static’就可以了,注入静态方法这里直接在String上面,所以可以直接使用

        3、注入构造方法

        String.metaClass.constructor = {
            Calendar calendar ->
                new String(calendar.getTime().toString())
        }
        println new String(Calendar.instance)
            1
            2
            3
            4
            5

        注入构造方法和注入普通方法类似,只不过方法名不能随便起了

        4、上面的方式都有点乱,那么有没有统一注入方法呢,当然有

        String.metaClass {
            get = {
                delegate.toString().toURL().text
            }
            'static' {
                printlnClass = {
                    println "================="
                    println delegate
                }
            }
            constructor = {
                Calendar calendar ->
                    new String(calendar.getTime().toString())
            }
        }
        def str2 = "https://www.baidu.com"
        println str2.get()
        str2.printlnClass()
        println new String(Calendar.instance)
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19

    上面这种写法和前面的结果一样,只是整齐了一点

        5、ExpandoMetaClass的方式

        println String.metaClass
        //这里的String是准备要给那个类型注入方法
        def emc = new ExpandoMetaClass(String)
        emc.get = {
            delegate.toString().toURL().text
        }
        //先初始化,初始化之后才会生效
        emc.initialize()
        println String.metaClass.class
        //修改String的metaClass
        String.metaClass = emc
        println String.metaClass.class
        println "https://www.baidu.com".get()
        String.metaClass = null
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14

    这里调用字符串的get是可以成功的
    注意下

    假如有个java类

    public class Test {

        public void work() {
            run();
        }

        public void run() {
            System.out.println("run");
        }
    }
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10

    之后我们在groovy中动态的修改

    Test.metaClass.run = {
        println 'groovy run'
    }
    new Test().run()
        1
        2
        3
        4

    上面这么写是可以修改的,但是new Test().work()这么写是修改不了的,因为调用work方法是通过静态节点去一层一层调用,但是在work里面调用run()不是动态节点的方式,所以这里调用work()打印出来的还是run

    第三种方式.使用mixin

    mixin和第一种方式基本上一样

    @Mixin(String)
        class Get {
            def get(String url) {
                println 'Get'
                url.toURL().text
            }
        }

    class Post {
        def get(String url) {
            println 'Post'
            url.toURL().text
        }
    }

    new Get().substring(0)

    //混合注入方法
    String.mixin(Get,Post)
    //往metaClass中混合也是一样的
    String.metaClass.mixin(Get)
    println "".get('https://www.baidu.com/')
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22

    这里先调用谁的get方法,也跟第一种方式一样,调用的是最新的那个(最后加入的)

动态类:Expando

Demo地址:https://github.com/zhaopingfu/listener10

def expando = new Expando(name: 'hello', fun1: { "fun1" })
expando.height = 100
expando.fun2 = {
    "fun2"
}

println expando.name
println expando.height
println expando.fun1()
println expando.fun2()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

方法合成:

Demo地址:https://github.com/zhaopingfu/listener10

class Person {
    def methodMissing(String name, def args) {
        println 'missing'
        if (name.startsWith('play')) {
            //生成的class文件,调用方式不一样
//            printf metaClass
            Person p = this
//            println p.metaClass
            p.metaClass."$name" = {
                println "invoke $name"
            }
            "$name"(args)
        }
        return null
    }
}

def p = new Person()
println p.metaClass
p.playGame()
p.playGame()
p.playGame()

刚开始调用playGame方法,不存在会进入methodMissing方法,然后一看是play开头的,然后动态的合成一个方法,然后调用,之后再调用playGame的时候,因为已经注入了playGame方法了,就不会再进入methodMissing方法了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

方法委托:

Demo地址:https://github.com/zhaopingfu/listener10

    第一种方式:手动来

    class Work1 {
        def execute1() {
            println "execute1"
        }
    }

    class Work2 {
        def execute2() {
            println "execute2"
        }
    }

    class WorkManager {
        Work1 work1 = new Work1()
        Work2 work2 = new Work2()
        Work2 work3 = new Work2()

        def methodMissing(String name, def args) {
            WorkManager wm = this
            if (work1.respondsTo(name, args)) {
                wm.metaClass."$name" = {
                    work1.invokeMethod(name, it)
                }
                "$name"(args)
            } else if (work2.respondsTo(name, args)) {
                wm.metaClass."$name" = {
                    work2.invokeMethod(name, it)
                }
                "$name"(args)
            }
            return null
        }
    }

    def wm = new WorkManager()
    wm.work1.execute1()
    wm.execute1()
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37

    Work1和Work2将方法委托给WorkManager,通过WorkManager来调用方法

    第二种方式:简化的手动

    class WorkManager1 {

        {
            delegate(Work1, Work2)
        }

        def delegate(Class... classes) {
            //创建对应的对象
            def objects = classes.collect { it.newInstance() }
            WorkManager1 wm = this
            //注入methodMissing方法
            wm.metaClass.methodMissing = {
                String name, def args ->
                    //查找调用的方法的实现对象
                    def object = objects.find { it.respondsTo(name, args) }
                    if (object) {
                        //动态注入方法
                        wm.metaClass."$name" = {
                            object.invokeMethod(name, it)
                        }
                        "$name"()
                    }
            }
        }
    }

    def wm1 = new WorkManager1()
    wm1.execute1()
    wm1.execute2()
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29

    上面那种方式,每次委托一个对象,都要加一个if else,而这里只需要在静态代码块里天价一下就好了

    第三种方式:注解

    class WorkManager2 {
        @Delegate
        Work1 work1 = new Work1()
        @Delegate
        Work2 work2 = new Work2()
    }

    new WorkManager2().execute1()
    new WorkManager2().execute2()   
        1
        2
        3
        4
        5
        6
        7
        8
        9

    这里Groovy帮我们做了一个注解,使用这个注解自动的帮我们进行委托,代码提示也有了
---------------------
作者:只是丶很孤单
来源:CSDN
原文:https://blog.csdn.net/pf_1308108803/article/details/78043738
版权声明:本文为博主原创文章,转载请附上博文链接!

posted @ 2019-07-22 10:39  天涯海角路  阅读(824)  评论(0)    收藏  举报