代码之家  ›  专栏  ›  技术社区  ›  Suren Aznauryan

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

  •  1
  • Suren Aznauryan  · 技术社区  · 6 年前

    我在书中发现了以下陈述 Groovy In Action,第2版 :

    类别使用仅限于当前线程

    这句话到底是什么意思?

    1 回复  |  直到 6 年前
        1
  •  3
  •   Szymon Stepniak    6 年前

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

    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 线程,我们已经调用 一串转换() 中的方法 主要的 还有螺纹。

    现在让我们稍微更改一下这个示例,然后调用 一串转换() 方法外部 主要的 通过在新启动的线程中调用线程:

    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()
        }
    }
    

    我们使用了 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)
    

    引发异常,因为 一串转换() 的范围中不存在 螺纹-1 线程-它仅存在于 主要的 线

    但是让我们假设我们必须在 螺纹-1 线程范围。我们可以通过定义 use(StringUtils){} 内部 螺纹-1 块,例如。

    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()
        }
    }
    

    现在一切正常-类别使用块在内部定义 螺纹-1 我们打电话 一串转换() 方法。运行此示例会向控制台生成预期的输出:

    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)找到它。 转换() 类中不存在方法 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 在这种情况下使用)。

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

    List used = GroovyCategorySupport.getCategoryMethods(name);
    

    这是在category类中按名称查找方法的最后一部分。稍后,它会检查该方法是否是静态的,以及是否需要具有有效类型的参数( 一串 在这种情况下)。