为什么使用MOP修改Groovy metaClass的静态成员行为不符合预期
Groovy动态元类扩展问题解答
问题1:静态元类扩展仅支持getXxx格式闭包的原因
Groovy的ExpandoMetaClass对实例上下文和静态上下文的实现逻辑存在差异:
- 实例侧的ExpandoMetaClass有独立的属性存储空间,可直接赋值动态属性,也可以通过
getXxx闭包映射为属性访问 - 静态上下文的实现没有设计独立的属性存储结构,所有静态扩展内容统一按方法处理,只有符合JavaBean getter规范的
getXxx格式闭包,才会被Groovy的属性访问语法自动映射为xxx属性调用,所以无法像实例侧一样直接添加静态属性,只能通过定义getter闭包模拟静态属性。
问题2:无需创建实例提前切换ExpandoMetaClass的方案
报错原因分析
你之前手动切换元类抛出NoClassDefFoundError的核心原因是时序问题:在创建并注册ExpandoMetaClass之前,你已经通过println DynamicExtendableClass.metaClass触发了类的静态初始化,此时默认元类已经生成,静态初始化块中的扩展操作已经在默认元类上执行,后续替换元类会破坏类初始化的一致性,最终导致类初始化失败。
正确实现方案
有两种不需要创建临时实例的实现方式:
方案1:类内部静态块提前切换元类
在类的静态初始化块最开头先完成ExpandoMetaClass的注册,再执行扩展操作,代码示例:
class DynamicExtendableClass { static { // 优先完成ExpandoMetaClass切换,不要先调用.metaClass触发默认元类创建 def registry = GroovySystem.metaClassRegistry def emc = new ExpandoMetaClass(DynamicExtendableClass, true, true) emc.initialize() registry.setMetaClass(DynamicExtendableClass, emc) // 再执行动态扩展操作 println "static initialiser - adding dynamic properties and methods to metaClass" metaClass.addedProperty = "added property to class metaClass" metaClass.getAddedMethod = { -> "added closure as method" } metaClass.static.getStaticAddedMethod = { -> "added closure as static method" } } static String declaredStaticString = "declared static string" static String getDeclaredMethodStaticString () { "static method returning string" } }
该方案下类加载完成后元类已经是ExpandoMetaClass,不需要创建实例即可直接访问静态扩展内容,创建实例也可以正常访问实例扩展内容。
方案2:外部提前注册元类(无需修改原类代码)
在触发类的任何操作之前先完成ExpandoMetaClass的创建和注册,代码示例:
// 注意:执行以下操作前不要访问DynamicExtendableClass的任何属性、方法、构造器,避免提前触发类初始化 def registry = GroovySystem.metaClassRegistry def emc = new ExpandoMetaClass(DynamicExtendableClass, true, true) // 提前定义所有扩展内容 emc.static.getStaticAddedMethod = {-> "static hello from my emc"} emc.addedProperty = "added property to class metaClass" emc.getAddedMethod = { -> "added closure as method" } emc.initialize() registry.setMetaClass(DynamicExtendableClass, emc) // 注册完成后再触发类操作 assert DynamicExtendableClass.staticAddedMethod == "static hello from my emc" def testInstance = new DynamicExtendableClass() assert testInstance.addedProperty == "added property to class metaClass"
内容的提问来源于stack exchange,提问作者WILLIAM WOODMAN
相关产品推荐
相关产品推荐

