这篇文章将对Groovy的其中一个核心内容MetaClass(MOP)讲解。由于该部分内容较多。所以还是利用一个个例子逐步的阐述。
一、拦截方法调用和参数获取
示例1:
class MyClass{
def hello(){
'invoked hello directly'
}
def invokeMethod(String name, Object args){
return "unknown method $name(${args.join(', ')})"
}
}
def mine= new MyClass()
assert mine.hello() == 'invoked hello directly'
assert mine.foo("Mark", 19) == 'unknown method foo(Mark, 19)'
首先我们在groovy脚本中定义了一个Myclass对象,在groovy中任何的对象都是实现GroovyObject并且继承GroovyObjectSupport的,在GroovyObject的接口中,我们可以看到几个方法首先是getMetaClass方法和setMetaClass方法,metaClass用来支持动态方法和动态参数的调用。另一组方法是getProperty和setProperty方法,这组方法是用来支持动态参数的设定与赋值的。最后还有一个invokeMethod方法,该方法则是用于调用动态方法的。在了解了上述概念后,我们可以理解为MyClass的invokeMethod方法覆盖了GroovyObjectSupport中对应的方法,所以调用未预先定义的foo方法就会进入invokeMethod的实现。而hello方法预先定义则照旧。
示例2:
class MyClass implements GroovyInterceptable{
def hello(){
'invoked hello() directly'
}
def invokeMethod(String name, Object args){
"invoked method $name(${args.join(', ')})"
}
}
def mine = new MyClass()
assert mine.hello() == 'invoked method hello()'
assert mine.foo('Mark',19) == 'invoked method foo(Mark, 19)'
assert mine.&hello() == 'invoked hello() directly'
该例子和示例1的不同在于实现了一个GroovyInterceptable接口,仔细看下这个接口的描述,可知道实现该接口的类被调用方法时都是默认使用invokeMethod方法,而不管该方法时动态生成或时静态生成;第2点不同是如果要再此情况下调用原有定义号的方法,需要变通的使用'.&'操作符。
示例3:
class MyClass{
def greeting = 'accessed greeting directly'
Object getProperty(String property){
"read from property $property"
}
void setProperty(String property, Object newVlaue){
throw new Exception("wrote to property $property")
}
}
def mine = new MyClass()
assert mine.greeting == 'read from property greeting'
try{
mine.greeting = 'hi'
}catch(e){
assert e.message == 'wrote to property greeting'
}
assert mine.@greeting == 'accessed greeting directly'
该示例描述了属性的获取特性,在示例1中已经描述设置和获取属性的方法时继承来的,这里不做赘述。默认的通过Gpath(见Gpath具体概念)来处理属性的值,都是通过调用getProperty和SetProperty来代劳的。同样的如果你真希望直接访问参数的值,可以变通的使用'.@'操作符来达成。
通过Gpath来获得属性值。无论该属性在类中是否有,都是不会出错的执行那2个方法。但是对于类中不存在的属性,忌使用'.@'操作符,会抛出MissingFieldException。
示例4:
class MyClass implements GroovyInterceptable{
def greeting = 'accessed greeting'
def id ='White: '
Object getProperty(String property){
try{
return this.@id + //access field directly
'indirectly ' +
this.@"$property"
}catch(e){
return "no such property $property"
}
}
def hello(Object[] args){"invoked hello with (${args.join(', ')})"}
def id(){'Green: '}
def invokeMethod(String name, Object args){
try{
return this.&id() + //call method directly
'indirectly ' +
this.&"$name"(args)
}catch(e){
return "no such method $name"
}
}
}
def mine = new MyClass()
assert mine.greeting == 'White: indirectly accessed greeting'
assert mine.farewell == 'no such property farewell'
assert mine.hello(1, 'b', 3) == 'Green: indirectly invoked hello with (1, b, 3)'
assert mine.foo('Mark', 19) == 'no such method foo'
该示例是对示例 2,3的一个合并,同时他告诉我们我们可以通过操作符'.@' 或者'.&'后使用双引号中定义变量的方法来动态的获取参数或者动态的方法。
二、MetaClass (描述类的类)
示例5:
public class MyMetaClass extends DelegatingMetaClass{
MyMetaClass(Class thisClass){
super(thisClass)
}
Object invokeMethod(Object object, String methodName, Object[] arguments){
"MyMetaClass: ${super.invokeMethod(object, methodName, arguments)}"
}
}
class A{
def bark(){'A: invoked bark()'}
def invokeMethod(String name, Object args){
"A: missing $name(${args.join(', ')})"
}
}
def amc = new MyMetaClass(A)
amc.initialize()
def a = new A()
a.metaClass = amc
assert a.bark() == 'MyMetaClass: A: invoked bark()'
Thread.start {
assert a.bark() == 'MyMetaClass: A: invoked bark()'
}
assert new A().bark() == 'A: invoked bark()'
assert a.bleet() == 'A: missing bleet()'
该示例的代码较长,主要的意思是我们可以通过任意Groovy对象中的metaClass属性来为改变该对象的方法调用的行为。Groovy为我们提供了DelegatingMetaClass 来让我们实现该功能。
具体的做法是:首先创建一个自定义的MetaClass类继承于DelegatingMetaClass,同时实现构造方法,以及自定义的方法调用行为(以后的内容将介绍,我们不仅可以改变方法行为)。然后我们可以通过自己创建的metaClass子类来包装你想改变行为的目标类。此处为类A。然后再创建目标类实例,对目标类中得metaClass属性进行设置。随后在该实例上的方法调用将会按照该实例的metaClass属性所对应的处理器,进行处理,该例中是再完成本身方法调用后在前面添加MyMetaClass字符串。
但是值得注意的是,1.对于另外新创建的A实例,如果没有进行metaClass属性赋值将按照原方法定义执行;2.动态定义的
方法不受metaClass方法行为改变的影响。见该示例的最后一行。3.上述的特性在新线程内同样有效。
示例6:
InvokerHelper.instance.metaRegistry.setMetaClass(A, amc)
该示例只是对示例5的补充,我们曾提到,我们可以通过为目标类实例设置metaClass属性来让metaClass的行为对该实例生效。但是如果要在类范围内生效的话,就需要通过上面的代码进行注册。这样注册过后,将对目标类的所有,任何时候创建的示例,都赋予metaClass的行为。
这个示例还有一点需要注意。试想这么一种情况,先创建了一个目标类实例,再用示例6的语句注册,再创建一个目标类的实例。metaClass的行为将在哪个对象上生效呢。答案是两者都生效。这点非常关键。一旦进行了类范围metaClass注册,那对于已创建和新创建的对象都生效。
注:对于高版本的groovy InvokerHelper的instance不存在。可以直接调用metaRegistry。
示例7:
import org.codehaus.groovy.runtime.InvokerHelper;
public class MyMetaClass extends DelegatingMetaClass{
MyMetaClass(Class theClass){
super(theClass)
}
Object invokeConstructor(Object[] arguments){
[]
}
}
class A{}
def amc = new MyMetaClass(A)
amc.initialize()
InvokerHelper.metaRegistry.setMetaClass(A,amc)
def a = new A()
assert a.class == ArrayList
assert (a<<1<<2<<3).size() == 3
在之前的示例已经略有提过metaClass不仅可以改变预定义的方法行为。在该示例中就以改变构造行为为例。该metaClass将构造行为变成创建一个数组对象。随后的操作是一目了然的。
在此值得一提的是,invokeConstructor方法时从metaClass的接口MetaObjectProtocol继承过来,其他的方法可能
是从不同的继承树而来的,所以可以参见DelegatingMetaClass中得方法签名。我们可以发现有很多invokeXXX和getXXX的方法,也就是说我们可以对这些metaClass行为做更改。主要的我们可以看到有getProperty方法invokeConstructor方法,当然我很兴奋的发现还有一个invokeMissingMethod方法,这个方法似乎就是对我们示例5中不能处理动态定义方法的metaClass行为的一个有用的补充了。
三、ExpandoMetaClass
下面这个示例内容较多,在给出例子之前,需要先澄清一些细节,你可能会想我们之前人为的为groovyobject设置metaClass来改变类实例的行为,那默认不设置情况下这个metaClass是什么呢? 其实无论是你设置或者没有设置metaClass它的metaClass都是HandleMetaClass类型的,但是区别在于,里面有一个getAdaptee方法返回的是DelegatingMetaClass中的MetaClass类型的delegate属性,默认情况下,这个delegate会事MetaClassImpl类型的,但是一旦你自己设置了目标类实例的metaClass属性那这个delegate属性就是你设置的了。我们下面要将的这个话题,也就是这一章的标题ExpandoMetaClass(MetaClassImpl的一个子类)正是又一个与MetaClassImpl以及自定义的MetaClass平行的delegate只是这个delegate,支持动态创建方法等等的特性。接着我们先引入示例
示例8:
class A{
String text
}
def a1= new A(text: 'aBCdefG')
assert a1.metaClass.adaptee.class == MetaClassImpl
A.metaClass.inSameCase = {-> text.toUpperCase()}
//triggers conversion of MetaClass of A to ExpandoMetaClass
//then adds new instance method 'inUpperCase' to class
//A.metaClass { }
//
def a2 = new A(text:'hiJKLmnOp')
assert a2.metaClass.adaptee.class == ExpandoMetaClass
//MetaClass of A changed for instances created after conversion trigger only
assert a2.inSameCase() == 'HIJKLMNOP'
//new method not available
assert a1.metaClass.adaptee.class == MetaClassImpl
try{ println a1.inSameCase();}
catch(e){assert e in MissingMethodException}
A.metaClass.inLowerCase = {-> text.toLowerCase()}
assert a2.inLowerCase() == 'hijklmnop'
//replace the method definition with another
A.metaClass.inSameCase = {-> text.toLowerCase()}
assert a2.inSameCase() == 'hijklmnop'
//add static methods
A.metaClass.'static'.inSameCase = {it.toLowerCase()}
assert A.inSameCase('qRStuVwXyz') == 'qrstuvwxyz'
代码的前几行印证了,默认的delegate(即HandleMetaClass中得metaClass属性)是MetaClassImpl。然后我们调用了A.metaClass { }方法(该方法位于DefaultGroovyMethods中)使得返回的HandleMetaClass中得delegate是
ExpandoMetaClass类型的。这样我们就能够利用该类型的metaClass为我们做事了。同样的
A.metaClass.inSameCase = {-> text.toUpperCase()}方法只是在注册metaClass为ExpandoMetaClass的同时
为其动态添加一个实例方法。同时我们可以在最后几行的代码中获知,动态添加静态方法也是可行的。
几点需要注意的:1.只有在切换delegate为ExpandoMetaClass后创建的目标对象才能支持切换时所提供的动态方法。2.动态方法的添加存在覆盖关系。
示例9:
class A{
}
A.metaClass.character = 'Cat in the Hat'
def a1 = new A()
assert a1.character == 'Cat in the Hat'
def ourProperties = Collections.synchronizedMap([:])
A.metaClass.setType = {String value -> ourProperties["${delegate}Type"] = value }
A.metaClass.getType = { -> ourProperties["${delegate}Type"]}
a1.type = 'Hatted Cat'
assert a1.type == 'Hatted Cat'
def a2 = new A()
A.metaClass.constructor = { -> new A()}
try{
a2 = new A()
}catch(Error e){
assert e in StackOverflowError
}
A.metaClass.constructor = {String s -> new A(character :s)}
a2 = new A("Thing One")
A.metaClass.'changeCharacterToThingTwo'= {-> delegate.character = 'Thing Two' }
a2.character= 'Cat in the Hat'
a2.changeCharacterToThingTwo()
assert a2.character == 'Thing Two'
['Hatted Cat', 'Thing', 'Boy', 'Girl', 'Mother'].each{p->
A.metaClass."changeTypeTo${p}"= {-> delegate.type= p}
}
a2.changeTypeToBoy()
assert a2.type == 'Boy'
a2.'changeTypeToHatted Cat'()
assert a2.type == 'Hatted Cat'
该示例的内容只要是示例8的一个补充,我们不仅可以动态添加方法,同时还可以动态添加属性和构造方法。
示例10:
ExpandoMetaClass.enableGlobally()
//call 'enableGlobally' method before adding to supplied class
List.metaClass.sizeDoubled = {-> delegate.size() * 2 }
//add method to an interface
def list = [] << 1 << 2
assert list.sizeDoubled() == 4
该示例比较简介,旨在告诉我们我们不仅可以对自定义的groovy对象进行属性方法等的动态添加,同样的我们可以对非自定义的Groovy提供的对象进行动态处理。处理方法和自定义对象的方法时完全一样的,唯一的区别在于,我们需要额外定义ExpandoMetaClass.enableGlobally(),然而笔者发现笔者使用的1.8.1版的groovy如果去掉了该声明也是可以工作
所以请各位读者按照个人版本做尝试。
示例11:
class Bird{
def name = 'Tweety'
def twirp(){ 'i taught i saw a puddy cat' }
}
Bird.metaClass.invokeMethod = {name, args->
def metaMethod = Bird.metaClass.getMetaMethod(name, args)
metaMethod?metaMethod.invoke(delegate,args): 'no such method'
}
def a = new Bird()
assert a.twirp() == 'i taught i saw a puddy cat'
assert a.bleet() =='no such method'
Bird.metaClass.getProperty = {name->
def metaProperty = Bird.metaClass.getMetaProperty(name)
metaProperty?metaProperty.getProperty(delegate): 'no such property'
}
def b = new Bird()
assert b.name == 'Tweety'
assert b.filling == 'no such property'
该示例主要说明的是我们不仅可以用Expando特性来添加方法属性和构造方法,同样的我们可以对已经存在的方法进行覆盖。最后还要强调一下Bird.metaClass返回的是ExpandoMetaClass我们这里覆盖的getMetaMethod和
getProperty以及invokeMethod和getProperty方法都是对ExpandoMetaClass类和它父类对应的方法进行覆盖。
至此,Groovy的MetaClass内容已经介绍完了。该部分内容在groovy中非常重要。谨此记录下来作为日后参考,同时希望对大家有帮助。
分享到:
相关推荐
0,下载Grails( http://dist.codehaus.org/grails/grails-bin-0.4.2.zip ,请留意朝花夕拾——Groovy & Grails中的“最新版本”提示)并解压到自己指定位置(我的位置是D:\D\MY_DEV\grails) 1,设置环境变量GRAILS...
groovy基础语法.pdf
groovy基础语法.doc,学习groovy入门级资料
groovy基础教程源码,很全面tjureykjetyukmjyteytdeyhnjfgnjsfdghfdxhgsffgv bnvbn
NULL 博文链接:https://key232323.iteye.com/blog/1336400
Groovy简介 Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python、Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。由于其运行在 JVM 上的特性,...
groovy环境的mock工具,用于辅助单元测试.
Groovy 正则表达式 Groovy正则表达式语法 背景项目使用Gradle作为自动化构建的工具, 闲暇之余对这个工具的使用方式以及其配置文件依赖的Groov
Groovy 调用 Java 类groovy 调用 Java class 十分方便,只需要在类前导入该 Java 类,在 Groovy 代码中就可以无缝使用该
赠送jar包:groovy-3.0.9.jar; 赠送原API文档:groovy-3.0.9-javadoc.jar; 赠送源代码:groovy-3.0.9-sources.jar; 赠送Maven依赖信息文件:groovy-3.0.9.pom; 包含翻译后的API文档:groovy-3.0.9-javadoc-API...
apache-groovy-3.0.8.zip apache官网的groovy3.0.8版本,希望大家多多下载,apache-groovy-3.0.8.zip apache官网的groovy3.0.8版本,希望大家多多下载,apache-groovy-3.0.8.zip apache官网的groovy3.0.8版本,希望...
GroovySparql.zip,Groovy SPARQL提供了一个易于使用的Groovy API,用于W3C标准SPARQL EnthPosits Groovy SPARQL——一个易于使用的Groovy API,用于与SPARQL端点和RDF数据进行交互
Making Java Groovy is a practical handbook for developers who want to blend Groovy into their day to day work with Java It starts by introducing the key differences between Java and Groovy and how you...
groovy入门经典,groovyeclipse 插件
Java调用Groovy,实时动态加载数据库groovy脚本,java读取mongoDB的groovy脚本,加载实时运行,热部署
本文适合于不熟悉 Groovy,但想快速轻松地了解其基础知识的 Java开发人员。了解 Groovy 对 Java 语法的简化变形,学习 Groovy 的核心功能,例如本地集合、内置正则表达式和闭包。编写第一个 Groovy 类,然后学习如何...
Groovy轻松入门—搭建Groovy开发环境 Groovy轻松入门—搭建Groovy开发环境
groovy-2.3.6-installer windows安装版本
什么是 Groovy? Groovy 是 JVM 的一个替代语言 — 替代 是指可以用 Groovy 在 Java 平台上进行 Java 编程,使用方式基本与使用 Java 代码的方式相同。在编写新应用程序时,Groovy 代码能够与 Java 代码很好地结合,...