AspectJ加载时间weaver不会检测到所有类 [英] AspectJ Load time weaver doesn't detect all classes

查看:104
本文介绍了AspectJ加载时间weaver不会检测到所有类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在"aspectj"模式下使用Spring的声明式事务(@Transactional批注).在大多数情况下,它的工作原理与应有的情况完全相同,但有一种情况却没有.我们可以称其为Lang(因为这就是它的实际名称).

I am using Spring's declarative transactions (the @Transactional annotation) in "aspectj" mode. It works in most cases exactly like it should, but for one it doesn't. We can call it Lang (because that's what it's actually called).

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

I have been able to pinpoint the problem to the load time weaver. By turning on debug and verbose logging in aop.xml, it lists all classes being woven. The problematic class Lang is indeed not mentioned in the logs at all.

然后我在Lang的顶部放置一个断点,导致Eclipse在加载Lang类时挂起线程.在LTW编织其他类时会遇到此断点!所以我猜想它要么尝试编织Lang并失败并且不输出它,要么某个其他类具有一个引用,迫使它在实际上没有机会编织它之前加载Lang.

Then I put a breakpoint at the top of Lang, causing Eclipse to suspend the thread when the Lang class is loaded. This breakpoint is hit while the LTW weaving other classes! So I am guessing it either tries to weave Lang and fails and doesn't output that, or some other class has a reference that forces it to load Lang before it actually gets a chance to weave it.

但是我不确定如何继续调试它,因为我无法以较小的比例复制它.有什么建议吗?

I am unsure however how to continue to debug this, since I am not able to reproduce it in smaller scale. Any suggestions on how to go on?

更新:也欢迎提供其他线索.例如,LTW实际如何工作?似乎发生了很多魔术.是否有任何选项可以从LTW获得更多调试输出?我目前有:

Update: Other clues are also welcome. For example, how does the LTW actually work? There appears to be a lot of magic happening. Are there any options to get even more debug output from the LTW? I currently have:

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


我忘了提到它: spring-agent 用于允许LTW,即InstrumentationLoadTimeWeaver.


I forgot tom mention it before: spring-agent is being used to allow LTW, i.e., the InstrumentationLoadTimeWeaver.

根据安迪·克莱门特(Andy Clement)的建议,我决定检查AspectJ转换器是否曾经通过该类.我在ClassPreProcessorAgent.transform(..)中放置了一个断点,尽管Lang类与其他类(由Jetty的WebAppClassLoader实例)由相同的类加载器加载,但看来它甚至从未达到该方法.

Based on the suggestions of Andy Clement I decided to inspect whether the AspectJ transformer is ever even passed the class. I put a breakpoint in ClassPreProcessorAgent.transform(..), and it seems that the Lang class never even reaches that method, despite it being loaded by the same class loader as other classes (an instance of Jetty's WebAppClassLoader).

然后我继续在InstrumentationLoadTimeWeaver$FilteringClassFileTransformer.transform(..)中放置一个断点.连Lang都没有被击中.而且我相信应该为所有 all 加载的类调用该方法,而不管它们使用的是哪种类加载器.看起来开始像这样:

I then went on to put a breakpoint in InstrumentationLoadTimeWeaver$FilteringClassFileTransformer.transform(..). Not even that one is hit for Lang. And I believe that method should be invoked for all loaded classes, regardless of what class loader they are using. This is starting to look like:

  1. 我的调试有问题. Eclipse报告可能未加载Lang
  2. Java错误?牵强,但我想它确实会发生.


下一个提示:我打开了-verbose:class,似乎是Lang 正在过早加载-可能是在将变压器添加到Instrumentation之前.奇怪的是,我的Eclipse断点无法捕获此负载.


Next clue: I turned on -verbose:class and it appears as if Lang is being loaded prematurely - probably before the transformer is added to Instrumentation. Oddly, my Eclipse breakpoint does not catch this loading.

这意味着Spring是新的犯罪嫌疑人. ConfigurationClassPostProcessor中似乎有一些处理程序会加载类以对其进行检查.这可能与我的问题有关.

This means that Spring is new suspect. there appears to be some processing in ConfigurationClassPostProcessor that loads classes to inspect them. This could be related to my problem.

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

These lines in ConfigurationClassBeanDefinitionReader causes the Lang class to be read:

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是否可能出于不可知的原因在缓存类实例?

In particular, metadata.hasAnnotatedMethods() calls getDeclaredMethods() on the class, which loads all parameter classes of all methods in that class. I am guessing that this might not be the end of the problem though, because I think the classes are supposed to be unloaded. Could the JVM be caching the class instance for unknowable reasons?

推荐答案

好的,我已经解决了这个问题.本质上,与一些自定义扩展一起,这是一个Spring问题.如果有人遇到类似问题,我将尝试逐步解释正在发生的事情.

OK, I have solved the problem. Essentially, it is a Spring problem in conjunction with some custom extensions. If anyone comes across something similar, I will try to explain step by step what is happening.

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

First of all, we have a custom BeanDefintionParser in our project. This class had the following definition:

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定义,以搜索带有或具有@Bean的方法.

Now, the problem occurs after all bean definition have been read and BeanDefinitionRegistryPostProcessor begins to kick in. At this stage, a class called ConfigurationClassPostProcessor starts looking through all bean definitions, to search for bean classes annotated with @Configuration or that have methods with @Bean.

在读取bean的注释的过程中,它使用AnnotationMetadata接口.对于大多数常规bean,使用称为AnnotationMetadataVisitor的子类.但是,在解析bean定义时,如果像我们一样重写了getBeanClass()方法以返回类实例,则将使用StandardAnnotationMetadata实例.调用StandardAnnotationMetadata.hasAnnotatedMethods(..)时,它将调用Class.getDeclaredMethods(),这又导致类加载器加载该类中用作参数的所有类.以这种方式加载的类无法正确卸载,因此永远不会被编织,因为这是在AspectJ转换器注册之前发生的.

In the process of reading annotations for a bean, it uses the AnnotationMetadata interface. For most regular beans, a subclass called AnnotationMetadataVisitor is used. However, when parsing the bean definitions, if you have overriden the getBeanClass() method to return a class instance, like we had, instead a StandardAnnotationMetadata instance is used. When StandardAnnotationMetadata.hasAnnotatedMethods(..) is invoked, it calls Class.getDeclaredMethods(), which in turn causes the class loader to load all classes used as parameters in that class. Classes loaded this way are not correctly unloaded, and thus never weaved, since this happens before the AspectJ transformer registered.

现在,我的问题是我上了这样的课:

Now, my problem was that I had a class like so:

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

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

Then, I had a bean of class Something that was parsed using our custom ControllerBeanDefinitionParser. This triggered the wrong annotation detection procedure, which triggered unexpected class loading, which meant that AspectJ never got a chance to weave Lang.

解决方案是不覆盖getBeanClass(..),而是覆盖getBeanClassName(..),根据文档,这是更可取的:

The solution was to not override getBeanClass(..), but instead override getBeanClassName(..), which according to the documentation is preferable:

private static class ControllerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

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

// code to parse XML omitted for brevity

}

每日经验:除非您确实如此,否则请勿覆盖getBeanClass.实际上,除非您知道自己在做什么,否则不要尝试编写自己的BeanDefinitionParser.

Lesson of the day: Do not override getBeanClass unless you really mean it. Actually, don't try to write your own BeanDefinitionParser unless you know what you're doing.

芬.

这篇关于AspectJ加载时间weaver不会检测到所有类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆