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帮我们做了一个注解,使用这个注解自动的帮我们进行委托,代码提示也有了
- Groovy方法拦截,注入,合成,委托和动态类
- Groovy动态创建类、方法合成、方法委托
- groovy元编程 方法拦截 动态改变
- JAVA动态代理和方法拦截(使用CGLib实现AOP、方法拦截、委托)
- Groovy方法注入
- 委托代码动态注入
- GROOVY 类拦截器
- 用Groovy源编程(MOP)动态拦截(AOP)方法(比如记录String的concat和toUpperCase方法的耗费时间)
- groovy运行期间动态添加属性和方法
- 动态判定Groovy对象方法和属性是否存在
- groovy运行期间动态添加属性和方法
- 12.1Groovy使用GroovyInterceptable拦截方法
- 12.2Groovy使用MetaClass拦截方法
- Java 动态生成类和实例, 并注入方法
- 13.2Groovy使用ExpandoMetaClass注入方法
- android和html交互--动态注入方法
- DLL注入和API 拦截
- DLL注入和API拦截
- Nginx+Tomcat+Redis实现负载均衡与Session共享之五 — Nginx+Tomcat+Redis实现Session共享
- Android 面试题(二)
- 计蒜客 16959 Our Journey of Dalian Ends(2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 J)
- 2017/9/20工作总结
- 埃及分数
- Groovy方法拦截,注入,合成,委托和动态类
- ABAP 查询性能提高之索引
- 在线免费生成IntelliJ IDEA 15.0注册码
- 第三周 【项目2
- Java 实现导出excel表
- Play从2.6.x开始使用Akka HTTP作为默认服务后端
- B2C电商网站技术面试题分享
- webpack入门第一篇
- sql 的MINUS指令的应用(只适用于oracle,别的数据库可以使用联合连接union join!比较少见!)