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
版权声明:本文为博主原创文章,转载请附上博文链接!

浙公网安备 33010602011771号