Groovy 类别的使用仅限于当前线程是什么意思?



我在《Groovy In Action, 2nd Edition》一书中找到了以下陈述:

类别使用仅限于当前线程

这句话到底是什么意思?

这意味着您只能在同一线程中调用通过类别类添加的方法。请考虑以下示例:

class StringUtils {
static String transform(String source) {
return source.toUpperCase().reverse().substring(0, source.length() / 2 as int)
}
}
use (StringUtils) {
println "Lorem ipsum".transform()
}

在此示例中,我们通过类别添加String.transform()方法。运行此示例将生成以下输出:

MUSPI

在此示例中,我们在main线程中使用了类别类,并在main线程中调用了String.transform()方法。

现在让我们稍微更改一下这个示例,让我们在新启动的线程中调用String.transform()方法main线程外部调用它:

class StringUtils {
static String transform(String source) {
return source.toUpperCase().reverse().substring(0, source.length() / 2 as int)
}
}
use (StringUtils) {
Thread.start {
println "Lorem ipsum".transform()
}
}

我们在main线程中使用了StringUtil类别类,并从线程调用此方法Thread-1。让我们看看运行它时会发生什么:

Exception in thread "Thread-1" groovy.lang.MissingMethodException: No signature of method: java.lang.String.transform() is applicable for argument types: () values: []
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:58)
at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:49)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:117)
at script$_run_closure1$_closure2.doCall(script.groovy:9)
at script$_run_closure1$_closure2.doCall(script.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
at groovy.lang.Closure.call(Closure.java:414)
at groovy.lang.Closure.call(Closure.java:408)
at groovy.lang.Closure.run(Closure.java:495)
at java.lang.Thread.run(Thread.java:748)

抛出异常,因为String.transform()不存在于线程Thread-1范围内 - 它仅存在于线程main

但是,假设我们必须使此方法Thread-1线程范围内可用。我们可以通过在Thread-1块内定义use(StringUtils){}来实现它,例如

class StringUtils {
static String transform(String source) {
return source.toUpperCase().reverse().substring(0, source.length() / 2 as int)
}
}
Thread.start {
use(StringUtils) {
println "Lorem ipsum".transform()
}
}

现在一切都很好 - 类别使用块在Thread-1内部定义,我们从同一线程调用String.transform()方法。运行此示例会生成到控制台的预期输出:

MUSPI

这就是什么

类别使用仅限于当前线程

在实践中意味着。

但是这种方法是如何调用的呢?

当我们致电:

"Lorem ipsum".transform()

从上面的示例中,以下 Groovy 方法处理transform()方法的调用:

groovy.lang.MetaClassImpl.invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass)

你可以在第 1044 行(Groovy 2.4.12)找到它。transform()方法在第String类中不存在,因此 Groovy 必须在其他地方找到它的实现。在本例中,该方法在第 1055 行中找到:

MetaMethod method = null;
if (CLOSURE_CALL_METHOD.equals(methodName) && object instanceof GeneratedClosure) {
method = getMethodWithCaching(sender, "doCall", arguments, isCallToSuper);
}

此方法最重要的部分是第 1283 行:

if (!isCallToSuper && GroovyCategorySupport.hasCategoryInCurrentThread()) {
return getMethodWithoutCaching(sender, methodName, MetaClassHelper.convertToTypeArray(arguments), isCallToSuper);
} else {
....
}

GroovyCategorySupport.hasCategoryInCurrentThread()检查类别是否在当前线程中使用(在本例中使用ThreadLocal)。

如果您跟踪接下来发生的事情,您将到达getMethods(Class sender, String name, boolean isCallToSuper)所在的MetaClassImpl行 690。在第 706 行中,此方法调用:

List used = GroovyCategorySupport.getCategoryMethods(name);

这是在类别类中按名称实际查找方法的最后一部分。稍后它会检查该方法是否为静态方法,以及它是否需要具有有效类型的参数(在本例中为String)。

相关内容

最新更新