Groovy基础——MetaClass详解

一、拦截方法调用和参数获取

示例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 [email protected] == ‘accessed greeting directly‘

该示例描述了属性的获取特性,在示例1中已经描述设置和获取属性的方法时继承来的,这里不做赘述。默认的通过Gpath(见Gpath具体概念)来处理属性的值,都是通过调用getProperty和SetProperty来代劳的。同样的如果你真希望直接访问参数的值,可以变通的使用‘[email protected]‘操作符来达成。

通过Gpath来获得属性值。无论该属性在类中是否有,都是不会出错的执行那2个方法。但是对于类中不存在的属性,忌使用‘[email protected]‘操作符,会抛出MissingFieldException。

示例4:

class MyClass implements GroovyInterceptable{
	def greeting = ‘accessed greeting‘
	def id =‘White: ‘

	Object getProperty(String property){
		try{
			return [email protected] + //access field directly
					‘indirectly ‘ +
					[email protected]"$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的一个合并,同时他告诉我们我们可以通过操作符‘[email protected]‘ 或者‘.&‘后使用双引号中定义变量的方法来动态的获取参数或者动态的方法。

二、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中非常重要。谨此记录下来作为日后参考,同时希望对大家有帮助。

时间: 2024-09-30 02:01:42

Groovy基础——MetaClass详解的相关文章

Nmap扫描教程之基础扫描详解

Nmap扫描教程之基础扫描详解 Nmap扫描基础扫描 当用户对Nmap工具了解后,即可使用该工具实施扫描.通过上一章的介绍,用户可知Nmap工具可以分别对主机.端口.版本.操作系统等实施扫描.但是,在实施这些扫描工作之前,需要先简单了解下Nmap工具的使用,以方便后面实施扫描.所以,本章将通过使用Nmap工具实施基础的扫描,来帮助用户了解该工具. Nmap扫描扫描概述 在实施基本的扫描之前,需要先了解一些Nmap网络扫描的基本知识,及需要考虑的一些法律边界问题.本节将对网络基本扫描进行一个简单介

jmeter 基础功能详解

jmeter 基础功能详解 thread group:包含一组线程,每个线程独立地执行测试计划. sampler:采样器,有多种不同的sample实现,用来发起各种请求,如http请求,jdbc请求,javaTest请求等等. logic controller:逻辑控制器有多种不同的实现,可以决定每个sample的执行顺序. listener:有多种不同的实现,主要用于统计测试接话运行中的数据并展示,如可以进行图形化方式展示响应时间. timer:定时器,有多种不同的实现,可用作每个请求见的停顿

Linux上命令的使用格式和基础命令详解

一.Linux上命令的使用格式 命令行提示符详解: 用户通过终端的命令行接口来控制操作系统,登陆后如下: [[email protected] ~]# root: 当前登录的用户 @:分隔符 localhost: 当前主机的主机名,非完整格式:此处的完整格式为:localhost.localdomain [[email protected] ~]# hostname localhost.localdomain ~:用户当前所在的目录(current directory),也称为工作目录(work

高性能Web服务之tomcat基础应用详解(一)

Tomcat概述: Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun 和其他一些公司及个人共同开发而成.由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范.因为Tomcat 技术先进.性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目

varnish基础概念详解

varnish基础概念详解 比起squid更加轻量级,大致有以下几个特点: ·可以基于内存缓存,也可以在磁盘上缓存,但是就算存放在磁盘上,也不能实现持久缓存 只要进程崩溃,此前缓存统统失效,无论是在内存还是在磁盘,但是现在已经具备持久缓存功能,但是仍然在实验阶段,经常容易崩溃,而且最大大小不能超过1G 如果期望内存大小超过几十个G,比如图片服务器,纯粹使用内存,性能未必好,这时候可以使用磁盘进行缓存,或SSD X 2 做RAID 避免磁盘损坏,在实现随机访问上 ssd硬盘要比机械硬盘要好的多,如

Tomcat基础配置详解

Tomcat基础配置详解 组件原理图如下: 任何tomcat实例就是一个server,而一个server内部要想能够解析jsp页面转义编译serlet程序,要靠其引擎来实现 而引擎才是真正意义上执行jsp代码的容器,都是tomcat用类来描述这些组件的 同时,为了接受用户的请求,需要基于connector组件,所谓监听的套接字的程序,能够接手用户的请求,被称为连接器 一个server内部可以完全运行N个引擎,无非就是运行多个虚拟机而已 war包的概念 放在网页目录可以直接访问,而部署的时候可以自

halcon基础数据类型详解

#if defined(__CHAR_UNSIGNED__) || defined(__sgi) #define INT1 signed char /* integer, signed 1 Byte */ #define INT1_MIN SCHAR_MIN #define INT1_MAX SCHAR_MAX #else #define INT1 char /* integer, signed 1 Byte */ #define INT1_MIN CHAR_MIN #define INT1_M

Servlet基础知识详解

Servlet基础知识详解 Servlet基础知识详解 Servlet程序执行全过程 Servlet映射路径 Servlet映射练习 Servlet生命周期 为什么要学习Servlet生命周期 Servlet重要的生命周期方法 模拟通过反射构造Servlet对象 Servlet单实例多线程 Servlet留给开发者的init方法 Servlet中核心对象学习 HttpServletRequest对象 HttpServletResponse对象 ServletConfig对象 ServletCon

I2C 基础原理详解

今天来学习下I2C通信~ I2C(Inter-Intergrated Circuit)指的是 IC(Intergrated Circuit)之间的(Inter) 通信方式.如上图所以有很多的周边设备都是用I2C通信方式进行通信的. I2C(Inter-Intergrated Circuit)通信使用Clock Line(SCL:Serial Clock)和Data Line(SDA:Serial Data).数据通过时钟同步经过数据线进行传输.这里生成时钟信号并输出的设备便是Master, 时钟