Groovy
作为动态语言自然少不了一个重要东西就是元编程(metaprogramming
)
,在groovy 我们根据元对象协议
/MOP
(metaobject protocol
)进行动态编程。
MOP
:在Groovy
每个对象都有一个对应的metaclass
。而我们动态编程就是在metaclass
进行对于原始对象/Class
进行方法或者属性修改或增加。
注意:class
也是一个对象,也就是一个实例会关联两个metaclass
,一个是类的,一个是实例的。优先级是实例metaclass
才到类的metaclass
。
metaclass
在哪?Java
创建的类,那么在Groovy
中MetaClassRegistry
会保存一个对应某个java
类所对应的metaclass
。而对于groovy
创建的类,由于groovy
默认实现GroovyObject
接口(这个接口的实现由groovy
负责)。GroovyObject
接口中存在方法返回一个metaclass
的方法。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);
}
其实MOP
还有一套方法/属性调用机制,我们先看看如何给一个类动态的扩展一个属性或者方法。
给一个String
类扩展一个静态方法sayHello
String.metaClass.'static'.sayHello = {
println("hello world")
}
String.'sayHello'()
String.sayHello()
暂时未找到静态属性注入,还望知道的指点
给一个String
类扩展构造方法,传入一个Integer
然后返回实例
String.metaClass.constructor = { Integer p ->
new String("hello world" + p)
}
println new String(1)
String.metaClass.myName = "XiaoMing"
println "xx".myName
String.metaClass.sayHello = {
println('hello world')
}
"xx".sayHello()
String.metaClass {
myName = "XiaoMing"
cry = {
println("cry")
}
//静态注入
'static' {
sayHello = {
println("hello")
}
run = {
println("I'm runing")
}
}
}
String.run()
"String".cry()
println "String".myName
//1 创建一个ExpandoMetaClass
def eMetaClass = new ExpandoMetaClass(String)
eMetaClass.say = "hello world"
//2 完成注入后调用init初始化。注意初始化后不许再扩展
eMetaClass.initialize()
//3 注入
String.metaClass = eMetaClass
new MyTgroovy().test()
println "sd".say
以上扩展对于后续的所有的String
类都生效,如果我们只想对某个具体的实例进行扩展,而不影响的其他所有String
类实例。我们只需要取出实例的metaclass
进行扩展即可(注意实例和类各自有一个metaclass
)
def strOne = new String("hello")
//以下方法获取实例的类metaclass的代理类,注意是代理类,
//实际类通过strOne.@metaClass获取
//但是你直接用strOne.@metaClass进行扩展会出错,后续讲解为什么
strOne.metaClass.cry = {
println("invoke cry")
}
//同过
strOne.cry()
def strTwo = new String("hello")
//报错
//strTwo.cry()
当你只想对对象没有的方法进行扩展,如果对象存在某个方法那么抛出错误,可以用如下方法.如果对象父级metaclass
别存在此方法那么不覆盖。
class Person {
def test1() {
System.out.println("invoke test1")
return "test1"
}
}
Person.metaClass.test1 <<{
println("say hello")
}
输出:
Exception in thread "main" groovy.lang.GroovyRuntimeException: Cannot add new method [test1] for
..
覆盖父级别的方法情况:
import com.MyGroovy
class Person {
def test1() {
System.out.println("invoke test1")
return "test1"
}
}
Person.metaClass.say <<{
println("say hello")
}
def expando = new ExpandoMetaClass(Person.class)
expando.say << {
println("hello world")
}
expando.initialize()
def person = new Person()
person.metaClass = expando
//这里输出say hello
person.say()
输出:
say hello
以下说groovy官方demo:
class Person {
String name
}
class MortgageLender {
def borrowMoney() {
"buy house"
}
}
def lender = new MortgageLender()
Person.metaClass.buyHouse = lender.&borrowMoney
def p = new Person()
assert "buy house" == p.buyHouse()
方法一适用于方法和属性:
class Person {
def myName = "XiaoMing"
def run2() {
println("invoke origin run2")
}
}
Person.metaClass.run2 = { ->
println("invoke metaclass run2")
}
Person.metaClass.myName = "ZhangSan"
Person.metaClass = null
def person = new Person()
person.run2()
println person.myName
输出:
invoke origin run2
XiaoMing
方法二适用于属性
,方法还是会调用metaclass:
class Person {
def myName = "XiaoMing"
def run2() {
println("invoke origin run2")
}
}
Person.metaClass.run2 = { ->
println("invoke metaclass run2")
}
Person.metaClass.myName = "ZhangSan"
def person = new Person()
person.run2() //invoke metaclass run2
println person.@myName //输出XiaoMing
println person.myName //ZhangSan
假设我们有一个java创建的类Student.java
//Student.java
package com;
public class Student {
public void testOne() {
System.out.println("invoke origin testOne");
//这里还是会输出invoke origin testTwo
testTwo();
}
private void testTwo() {
System.out.println("invoke origin testTwo");
}
}
然后我们在groovy环境下运行
import com.Student
Student.metaClass.testTwo={
println("invoke metaclass test2")
}
new Student().testOne()
失效的原因是Groovy
生成的类内部存在方法动态调用的代码,而java
没有。
类存在一个metaclass
实例,而类的实例自己也有一个metaclass
,当没有进行动态对实例的metaclass
扩展时,类的metaclass
和实例的metaclass
是同一个对象。
class Person {
def test(outMetaclass) {
println(metaClass == outMetaclass)
}
}
def person = new Person()
person.test(person.metaClass)
person.test(person.@metaClass)
println(Person.metaClass == person.@metaClass)
输出:
false
true
true
第一个输出false的原因,是因为groovy在生成类的时候会在代码中生成许多代理函数和属性,而在外部直接person.metaClass
获取的metaclass
是通过动态代理从metaclass
池得到的一个,而内部的得到metaclass
是其内部的一个属性,默认的情况它指向类的metaclass
。
并且注意一个问题metaclass
实现类并不都具有动态扩展能力。
class Person {
}
def person = new Person()
println("动态代理metaclass:" + person.metaClass)
println("内部属性metaclass:" + person.@metaClass)
//报错
[email protected]="XiaoMing"
输出
动态代理metaclass:org.codehaus.groovy.runtime.HandleMetaClass@34c4973[groovy.lang.MetaClassImpl@34c4973[class Person]]
内部属性metaclass:groovy.lang.MetaClassImpl@34c4973[class Person]
Exception in thread "main" groovy.lang.MissingPropertyException: No such property: myName
.....
.....
内部属性得到的metaclass
实现类MetaClassImpl
不具备扩展能力。
如果上面的代码换成动态代理得到的metaclass即可正常正确运行
class Person {
}
def person = new Person()
println("动态代理metaclass:" + person.metaClass)
println("内部属性metaclass:" + person.@metaClass)
//通过
person.metaClass.myName = "XiaoMing"
输出:
略
metaclass
动态注入一个方法/属性后,某个类的实例的属性metaclas
将改变为ExpandoMetaClass
的一个实例,这个实例将会属性metaclass
也支持动态注入方法/属性class Person {
}
def person = new Person()
println("动态代理metaclass:" + person.metaClass)
println("内部属性metaclass:" + person.@metaClass)
//报错 属性metaclass是MetaClassImpl 所以不支持动态注入
//[email protected] = "XiaoMing"
//通过
person.metaClass.myName = "XiaoMing"
println("------------动态注入方法后-----------")
println("动态代理metaclass:" + person.metaClass)
println("内部属性metaclass:" + person.@metaClass)
//因为属性metaClass变为ExpandoMetaClass,所以这里不会报错
[email protected] = "XiaoMing"
metaclass
动态注入一个方法/属性后,那么这个类的实例的属性metaclas
将改变为ExpandoMetaClass
的一个实例,这个实例将会属性metaclass
也支持动态注入方法/属性class Person {
}
println("Person这个类没有动态注入前:" + Person.metaClass)
Person.metaClass.myName = ""
println("Person这个类有动态注后:" + Person.metaClass)
def instance = new Person()
println("instance 属性的metaclass:" + instance.@metaClass)
println("instance 动态代理metaclass:" + instance.metaClass)
//不报错 因为这里属性metaclass在Person metaclass改动的时候变化了
[email protected] = "你好"
metaclass
相关方法respondsTo
respondsTo
传入参数: List respondsTo(Object obj, String name, Object[] argTypes);
第一个参数:查找哪个对象
第二个参数:方法名
第三个参数: 参数类数组
如果存在方法那么返回在list中,如果没有返回空list
class Person {
def name = "XiaoMing"
def run2(String name, int age) {
}
def test() {
println("invoke test ")
println metaClass.respondsTo(this, "run2", String,int)
}
}
def person = new Person()
person.test()
输出:
invoke test
[public java.lang.Object Person.run2(java.lang.String,int)]
对于metaclass
进行扩展的话的那么默认的返回metaclass
的函数,而不会返回原本的,respondsTo
仅会返回一个方法。
class Person {
def name = "XiaoMing"
def run2(String name, int age) {
}
def test() {
println("invoke test ")
println metaClass.respondsTo(this, "run2", String,int)
}
}
Person.metaClass.run2={String name,int age->
println("Person class metacalss invoke run2")
}
def person = new Person()
person.test()
invoke test
[org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod@76f2b07d[name: run2 params: [class java.lang.String, int] returns: class java.lang.Object owner: class Person]]
hasProperty
,getMetaProperty
是否存在某个某个属性class Person {
def myName = "XiaoMing"
def run2(String name, int age) {
}
def test(propertiesName) {
println("invoke test ")
if (metaClass.hasProperty(this, propertiesName)) {
println("存在属性")
} else {
println("不存在")
}
}
}
def person = new Person()
person.test("myName")
person.test("myName2sdd")
输出:
invoke test
存在属性
invoke test
不存在
class Person {
def myName = "XiaoMing"
def run2(String name, int age) {
println("I am $name and $age ")
}
}
def person = new Person()
person.metaClass.invokeMethod(person, "run2","Xiaoming", 13)
getAttribute
和 setAttribute
两个函数用于直接设置/获取字段,而不是通过get/set方法。
class Person {
def myName = "XiaoMing"
void setMyName(String name) {
println("setMyName")
this.myName = name
}
}
def person = new Person()
person.myName="我阿尼"
println("开始调用setAttribute")
person.metaClass.setAttribute(person, "myName", "woaini")
println("调用setAttribute之后")
输出:
setMyName
开始调用setAttribute
调用setAttribute之后
假设你存在Person
类,需要实现一个run
方法,而这个方法在Work
中是存在的,那么你可以使用多个办法完成委托,但是这里使用了一种特别的方式。如下:
class Work {
def run() {
println("I'm Work and running")
}
}
class Person {
@Delegate
Work work = new Work()
}
def person = new Person()
person.run()