java – AspectJ加载时间weaver不检测所有类

我在“aspectj”模式下使用Spring的声明式事务(@Transactional注释).它在大多数情况下可以完全像它应该的,但对于一个它不是.我们可以称之为朗(因为这是它实际调用的).

我已经能够将问题精确到加载时间织布工.通过在aop.xml中打开调试和详细日志记录,它列出了正在编织的所有类.确实没有在日志中提到有问题的类Lang.

然后我在Lang的顶部放置一个断点,导致Eclipse在加载Lang类时挂起线程.这个断点在LTW编织其他类时被击中!所以我猜测它试图编织Lang并且失败,并且不输出,或者其他一些类有一个引用,强制它在它实际上有机会编织之前加载它.

我不确定如何继续调试这个,因为我不能以较小的规模再现它.有关如何继续的建议?

更新:其他线索也欢迎.例如,LTW如何实际工作?似乎有很多魔法发生.有什么选择可以从LTW获得更多的调试输出?我目前有:

<weaver options="-XnoInline -Xreweavable -verbose -debug -showWeaveInfo">

我忘记了以前提到的:spring-agent正被用于允许LTW,即InstrumentationLoadTimeWeaver.

根据Andy Clement的建议,我决定检查AspectJ变压器是否甚至通过了类.我在ClassPreProcessorAgent.transform(..)中放置一个断点,似乎Lang类甚至没有达到该方法,尽管它与其他类(Jetty的WebAppClassLoader的一个实例)由同一个类加载器加载.

然后我继续在InstrumentationLoadTimeWeaver $FilteringClassFileTransformer.transform(..)中放置一个断点.甚至没有一个被打到了郎.而且我相信应该调用所有加载类的方法,而不管他们使用什么类加载器.这开始看起来像:

>我的调试问题在Eclipse报告的时候,Lang可能没有加载
> Java bug很抱歉,但我想这确实发生了.

接下来的线索:我打开了-verbose:class,看起来好像是被提前加载了 – 可能在变压器被添加到Instrumentation之前.奇怪的是,我的Eclipse断点没有抓住这个加载.

这就意味着春天是新的嫌疑人. ConfigurationClassPostProcessor中似乎有一些加载类来检查它们.这可能与我的问题有关.

ConfigurationClassBeanDefinitionReader中的这些行会导致读取Lang类:

else if (metadata.isAnnotated(Component.class.getName()) ||
        metadata.hasAnnotatedMethods(Bean.class.getName())) {
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    return true;
}

特别地,metadata.hasAnnotatedMethods()在类上调用getDeclaredMethods(),它加载该类中所有方法的所有参数类.我猜测这可能不是问题的结束,因为我认为这些类应该被卸载. JVM是否可以由于不可知的原因缓存类实例?

最佳答案
好的,我已经解决了这个问题.从本质上讲,这是一个与一些自定义扩展相结合的Spring问题.如果有人遇到类似的事情,我会尽力解释一下发生了什么.

首先,我们在我们的项目中有一个自定义BeanDefintionParser.这个类有以下定义:

private static class ControllerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    protected Class<?> getBeanClass(Element element) {
        try {
            return Class.forName(element.getAttribute("class"));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Class " + element.getAttribute("class") + "not found.", e);
        }
    }

// code to parse XML omitted for brevity

}

现在,问题发生在所有bean定义被读取并且BeanDefinitionRegistryPostProcessor开始启动之后.在这个阶段,一个名为ConfigurationClassPostProcessor的类开始查找所有的bean定义,搜索用@Configuration注释的bean类,或者使用@Bean的方法.

在读取bean的注释的过程中,它使用AnnotationMetadata接口.对于大多数常规的bean,使用了一个名为AnnotationMetadataVisitor的子类.但是,解析bean定义时,如果您已经覆盖了getBeanClass()方法来返回类实例,就像我们已经使用的那样,而是使用了StandardAnnotationMetadata实例.当调用StandardAnnotationMetadata.hasAnnotatedMethods(..)时,它调用Class.getDeclaredMethods(),这又导致类加载器加载用作该类中的参数的所有类.以这种方式加载的类不能正确卸载,因此永远不会编织,因为这发生在AspectJ变压器注册之前.

现在我的问题是我有一个这样的课:

public class Something {
    private Lang lang;
    public void setLang(Lang lang) {
        this.lang = lang;
    }
}

然后,我使用我们自定义的ControllerBeanDefinitionParser解析了一些类Something的bean.这触发了错误的注释检测过程,这引发了意外的类加载,这意味着AspectJ从来没有机会编织Lang.

解决方法是不要覆盖getBeanClass(..),而是覆盖getBeanClassName(..),根据文档是最好的:

private static class ControllerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    protected String getBeanClassName(Element element) {
        return element.getAttribute("class");
    }

// code to parse XML omitted for brevity

}

一天的教训:除非你真的是这样,否则不要重写getBeanClass.实际上,除非你知道你在做什么,否则不要尝试编写你自己的BeanDefinitionParser.

鳍.

转载注明原文:java – AspectJ加载时间weaver不检测所有类 - 代码日志