首先说下调用方法的方式:
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的metaClass String.metaClass = emc println String.metaClass.class println "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 = 100
expando.fun2 = {
"fun2"
}
println expando.name
println expando.height
println 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.metaClass
p.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帮我们做了一个注解,使用这个注解自动的帮我们进行委托,代码提示也有了