Groovy MOP与元编程
2017-12-12 本文已影响154人
zhaoyubetter
参考:
《Groovy 程序设计》
元编程(metaprogramming)
意味着编写能够操作程序的程序,包括操作程序自身;Groovy通过MOP
(元对象协议 MetaObject Protocol)实现;
在Groovy中,使用MOP可以动态调用方法,可在运行时合成类和方法;
Groovy 对象
Groovy对象是带有附加功能的Java对象,在一个Groovy应用中,使用三类对象:
- POJO : 普通Java对象
- POGO : 使用Groovy编写的对象(扩展了java.lang.Object也实现了groovy.lang.GroovyObject接口)
- Groovy 拦截器:扩展了GroovyInterceptable的groovy对象,具有方法拦截功能;
GroovyObject类
如果一个类,为Groovy所编译,通过发编译代码可发现,会实现GroovyObject
接口,这样就使Java对象有了一些动态类;
public interface GroovyObject {
Object invokeMethod(String name, Object args);
Object getProperty(String propertyName);
void setProperty(String propertyName, Object newValue);
MetaClass getMetaClass();
void setMetaClass(MetaClass metaClass);
}
getMetaClass()和setMetaClass使创建代理拦截POGO的调用,注入方法等有了可能;
- 对于POJO,Groovy维护了MetaClass的一个MetaClassRegistry;Groovy会去
应用类
去取它的MetaClass
,并将方法调用委托给它;这样在它的MetaClass
上定义的任何拦截器或方法优先于POJO原来的方法; - 对于POGO,则有一个到其MetaClass的直接引用;如果没有实现
GroovyInterceptable
,则会先查找它的MetaClass
中的方法,没有,则查找POGO自身上的方法,没有,则以方法名
查找属性或字段,如果属性或字段是Closure类型的,则调用它;如果找不到,则抛出异常missingMethod Exception
测试代码
@Test // metaClass 测试
void testInterceptedMethodCAllOnPoJO() {
def val = new Integer(3)
val.metaClass.toString = { -> "intercepted" }
assert "intercepted" == (val.toString()) // 被拦截
}
@Test // 方法拦截
void testInterceptableCalled() {
def obj = new AnInterceptable()
assert "intercepted" == obj.existingMethod() // 存在的方法,拦截,执行invokeMethod()
assert "intercepted" == obj.nonexistingMethod() // 不存在的方法,拦截,执行invokeMethod()
}
class AnInterceptable implements GroovyInterceptable {
def existingMethod() {"Hello"} // 返回Hello无效,返回还是 "intercepted"
def invokeMethod(String name, Object args) {"intercepted"}
}
// 拦截存在的某个方法
@Test
void testInterceptedExistingMethodCalled() {
AGroovyObject.metaClass.existingMethod2 = { -> 'intercepted' }
def obj = new AGroovyObject()
assert 'intercepted' == obj.existingMethod2()
assert 'existingMethod' == obj.existingMethod() // 此方法未拦截
}
class AGroovyObject {
def existingMethod() { "existingMethod" }
def existingMethod2() { "existingMethod2" }
def closueProp = { "closure called" }
}
// 属性闭包调用
@Test
void testPropertyThatIsClosureCalled() {
def obj = new AGroovyObject()
assert 'closure called' == obj.closueProp()
}