使用 ExpandoMetaClass 进行方法注入
注入概述
之前其实我们已经见到过使用 ExpandoMetaClass 注入方法的示例了,就是使用MetaClass 进行方法拦截,这本质就是方法的注入,只不过注入的方法名(invokeMethod)比较特殊,成为了方法拦截。同样,我们也可以用 ExpandoMetaClass 对类进行其它方法的注入,还拿上面 Integer 的加法的例子:
Integer.metaClass.add = {
int i ->
delegate + i
}
println 1.add(3)
注入的种类
使用 ExpandoMetaClass 方法注入,可以对以下三种方法进行注入:
非静态方法
静态方法
构造器
属性
接下来一一介绍如何注入:
非静态方法注入
这在前面已经见到过了,也是最常用的注入,使用方法:
Foo.metaClass.bar = {}
foo.bar()
Groovy 的设计理念就是让程序的编写更加流程,因此在 DSL 中,可能更常见的一种形式是在调用方法时不写括号,即foo.bar但是没有括号调用时,会将方法的调用当成属性,所以需要对之前的注入进行修改。
Foo.metaClass.getBar = {}
foo.bar
这样的调用方式是否更加优雅呢,在后面的 DSL 中,还会进一步讲解 groovy 的语法糖,让编程更加优雅。
静态方法注入
需要使用 'static' 的特殊字面量注入静态方法
Foo.metaClass.'static'.bar = {}
Foo.bar()
注入构造器
使用 constructor 属性注入构造器
添加一个构造器 <<
替换一个构造器 =
Foo.metaClass.constructor << {
int i ->
Foo foo = new Foo();
foo.i = i
foo
}
构造方法注入特别要注意的是,要确保没有递归调用自身,否则栈溢出。因为我们是想定义构造器,肯定会借助现有的构造器,然后进行属性的改造,但是不要产生递归。如果是想覆盖构造器的话,那么只能在内部使用反射
注入属性
类似以闭包的方式注入方法,属性注入也是支持的,只要在后面 = 具体值 即可。
Foo.metaClass.bar = 1
println foo.bar
一次注入多个方法
Groovy 提供了使用ClassName.metaClass.method = { ... }这样的语法向 metaClass 中添加,既简单又方便,但如果想添加一堆方法,这样的声明就会感觉很费劲。groovy 提供了更简洁的语法,用来减少噪音!!这种方式也是在 DSL 中常见到的。
Foo.metaClass = {
bar1 = {}
bar2 = {}
'static'{
bar3 = {}
}
//针对于不管是覆盖还是注入,在这种语法环境下,都应该使用 =
constructor = {
int i - >
}
constructor = {
int i,int j ->
}
}
再次重申:使用 ExpandoMetaClass 注入的闭包中,delegate 指的是调用该方法的对象,在此基础上,闭包中使用类原本的成员变量,或者方法也是可以的。 |