AspectJ 加载时间编织器未检测到所有类 [英] AspectJ Load time weaver doesn't detect all classes
问题描述
我在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
甚至没有命中那个.而且我相信应该为所有加载的类调用该方法,无论它们使用什么类加载器.这开始看起来像:
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:
- 我的调试出现问题.可能
Lang
在 Eclipse 报告它的时候没有加载 - Java 错误?牵强,但我想它确实发生了.
- A problem with my debugging. Possibly
Lang
is not loaded at the time when Eclipse reports it is - Java bug? Far-fetched, but I suppose it does happen.
<小时>
下一个线索:我打开了 -verbose:class
并且看起来好像 Lang
is 被过早加载 - 可能在转换器加载之前添加到仪表.奇怪的是,我的 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 定义, 搜索带有 @Configuration
注释的 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 加载时间编织器未检测到所有类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!