该代码段摘自《 Groovy in action 2nd》,并作了一些细微修改。
1此代码可以正常工作
<?php
// fix array creation, dont use `->` use `=>`
// and the keys cannot all be the same
$aa=array("a"=>1,"b"=>2,"c"=>3,"d"=>4);
$otherV = '';
foreach($aa as $key => $value){
//concatenate the value into otherV using .=
$otherV .= $value.",";
}
rtrim( $otherV, ','); // remove last comma
echo $otherV;
?>
输出:
package test
class InspectMe {
int outer(){
return inner()
}
int inner(){
return 1
}
}
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
inspectMe.metaClass = proxyMetaClass
inspectMe.outer()
println(tracer.writer.toString())
2,但是此代码的输出不同
before test.InspectMe.outer()
before test.InspectMe.inner()
after test.InspectMe.inner()
after test.InspectMe.outer()
输出:
package test
class InspectMe {
int outer(){
return inner()
}
int inner(){
return 1
}
}
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
proxyMetaClass.use(inspectMe){
inspectMe.outer()
}
println(tracer.writer.toString())
在第二个代码中,TracingInterceptor似乎没有拦截内部方法。 也许这是正常行为,但是在我看来,这就像一个错误。 有人可以解释一下吗?
答案 0 :(得分:2)
我不知道这是否是错误,但是我可以解释为什么会发生这种不同的行为。让我们从字节码级别分析InspectMe.outer()
方法实现的样子开始(我们反编译.class文件):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.BytecodeInterface8;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
public class InspectMe implements GroovyObject {
public InspectMe() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
public int outer() {
CallSite[] var1 = $getCallSiteArray();
return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass() ? this.inner() : DefaultTypeTransformation.intUnbox(var1[0].callCurrent(this));
}
public int inner() {
CallSite[] var1 = $getCallSiteArray();
return 1;
}
}
如您所见,outer()
方法测试以下谓词
!__$stMC && !BytecodeInterface8.disabledStandardMetaClass()
,如果其求值为true
,它将直接调用this.inner()
方法,从而避免Groovy的MOP(元对象协议)层(在这种情况下不涉及元类)。否则,它将调用var1[0].callCurrent(this)
,这意味着inner()
方法是通过Groovy的MOP调用的,该方法具有执行过程中涉及的元类和拦截器。
问题中显示的两个示例提出了一种不同的设置元类字段的方法。在第一种情况下:
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
inspectMe.metaClass = proxyMetaClass // <-- setting metaClass with DefaultGroovyMethods
inspectMe.outer()
println(tracer.writer.toString())
我们正在使用Groovy的MOP层调用inspectMe.setMetaClass(proxyMetaClass)
方法。 DefaultGroovyMethods.setMetaClass(GroovyObject self, MetaClass metaClass)
将此方法添加到InspectMe
类中。
现在,如果我们快速看一下setMetaClass
方法的实现方式,我们会发现一些有趣的东西:
/**
* Set the metaclass for a GroovyObject.
* @param self the object whose metaclass we want to set
* @param metaClass the new metaclass value
* @since 2.0.0
*/
public static void setMetaClass(GroovyObject self, MetaClass metaClass) {
// this method was introduced as to prevent from a stack overflow, described in GROOVY-5285
if (metaClass instanceof HandleMetaClass)
metaClass = ((HandleMetaClass)metaClass).getAdaptee();
self.setMetaClass(metaClass);
disablePrimitiveOptimization(self);
}
private static void disablePrimitiveOptimization(Object self) {
Field sdyn;
Class c = self.getClass();
try {
sdyn = c.getDeclaredField(Verifier.STATIC_METACLASS_BOOL);
sdyn.setBoolean(null, true);
} catch (Throwable e) {
//DO NOTHING
}
}
它在最后调用私有方法disablePrimitiveOptimization(self)
。此方法负责将true
分配给__$stMC
类字段(常量Verifier.STATIC_METACLASS_BOOL
存储__$stMC
值)。在我们的情况下是什么意思?这意味着outer()
方法中的谓词:
return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass() ? this.inner() : DefaultTypeTransformation.intUnbox(var1[0].callCurrent(this));
评估为false
,因为__$stMC
设置为true
。在这种情况下,inner()
方法通过使用metaClass和拦截器的MOP执行。
好的,但是它说明了第一种可以正常工作的情况。在第二种情况下会发生什么?
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
proxyMetaClass.use(inspectMe){
inspectMe.outer()
}
println(tracer.writer.toString())
首先,我们需要检查proxyMetaClass.use()
是什么样的:
/**
* Use the ProxyMetaClass for the given Closure.
* Cares for balanced setting/unsetting ProxyMetaClass.
*
* @param closure piece of code to be executed with ProxyMetaClass
*/
public Object use(GroovyObject object, Closure closure) {
// grab existing meta (usually adaptee but we may have nested use calls)
MetaClass origMetaClass = object.getMetaClass();
object.setMetaClass(this);
try {
return closure.call();
} finally {
object.setMetaClass(origMetaClass);
}
}
这非常简单-在闭包执行时替换metaClass,并在闭包执行完成时将旧的metaClass放回原处。听起来像第一种情况,对吗?不必要。这是Java代码,它直接调用object.setMetaClass(this)
方法(object
类型为GroovyObject
的变量,其中包含setMetaClass
方法)。这意味着字段__$stMC
未设置为true
(默认值为false
),因此outer()
方法中的谓词必须求值:
BytecodeInterface8.disabledStandardMetaClass()
如果运行第二个示例,我们将看到此方法调用返回false
:
这就是为什么整个表达式
!__$stMC && !BytecodeInterface8.disabledStandardMetaClass()
求值为true
,直接执行调用this.inner()
的分支。
我不知道它是否是有意的,但是如您所见,动态setMetaClass
方法禁用原始优化并继续使用MOP,而ProxyMetaClass.use()
设置metaClass保持启用和导致原始优化直接方法调用。我想这个示例说明了在实现ProxyMetaClass
类时没有人想到的一个极端情况。
这两种方法之间似乎存在差异,因为ProxyMetaClass.use()
对于Groovy 1.x是implemented in 2005,并且是上一次in 2009进行了更新。 __$stMC
字段是在in 2011处添加的,DefaultGroovyMethods.setMetaClass(GroovyObject object, Closure cl)
是根据其javadoc于2012年引入的,该文档指出此方法自Groovy 2.0起可用。