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

来源:互联网 发布:用友t6软件下载 编辑:程序博客网 时间:2024/06/08 15:21

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

class Psrson{    def name    def dream() {        System.out.println 'i have a dream'    }}
  • 第一种方式:
    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'    }}

    总结:当一个类实现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()
    • 2、在类上进行拦截

      Person.metaClass.dream = {    System.out.println 'replace dream'}举例:String.metaClass.plus = {CharSequence i ->    i}println("123" + "abc")

      这里修改了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}这个方法"}

方法注入:运行时元编程

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()}

      这里使用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()}

      假如字符串已经有了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()

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

    • 2、注入静态方法

      String.metaClass.'static'.printlnClass = {    println "================="    println delegate}"www.baidu.com".printlnClass()String.printlnClass()

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

    • 3、注入构造方法

      String.metaClass.constructor = {    Calendar calendar ->        new String(calendar.getTime().toString())}println new String(Calendar.instance)

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

    • 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)

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

    • 5、ExpandoMetaClass的方式

      println String.metaClass//这里的String是准备要给那个类型注入方法def emc = new ExpandoMetaClass(String)emc.get = {    delegate.toString().toURL().text}//先初始化,初始化之后才会生效emc.initialize()println String.metaClass.class//修改String的metaClassString.metaClass = emcprintln String.metaClass.classprintln "https://www.baidu.com".get()String.metaClass = null

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

    注意下

    假如有个java类

    public class Test {    public void work() {        run();    }    public void run() {        System.out.println("run");    }}

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

    Test.metaClass.run = {    println 'groovy run'}new Test().run()

    上面这么写是可以修改的,但是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/')

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

动态类:Expando

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

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

方法合成:

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.metaClassp.playGame()p.playGame()p.playGame()刚开始调用playGame方法,不存在会进入methodMissing方法,然后一看是play开头的,然后动态的合成一个方法,然后调用,之后再调用playGame的时候,因为已经注入了playGame方法了,就不会再进入methodMissing方法了

方法委托:

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()

    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()

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

  • 第三种方式:注解

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

    这里Groovy帮我们做了一个注解,使用这个注解自动的帮我们进行委托,代码提示也有了

阅读全文
0 0