We are using a combination of XML and Annotation configuration in a Spring web application. After upgrading to Spring 5.3.1 (from Spring 4), some classes are not processed by the AspectJ Load Time Weaver. In our XML configuration we have:
<context:component-scan base-package="com.acme.spring"/>
<context:load-time-weaver aspectj-weaving="on" />
But AspectJ weaving only works for a subset of all classes. In particular, it does not work for classes found by component-scan. If the same bean is declared in XML, it works as expected.
We have managed to condense it into a small sample application:
https://github.com/emillundstrm/springaspectjtest
Running mvn jetty:run
you will find that there is no message about com.acme.spring.TestBean being woven.
However, if you remove the @Component
annotation from TestBean and instead declare it in ApplicationContext.xml, this is logged:
[WebAppClassLoader@58437801] debug weaving 'com.acme.spring.TestBean'
[WebAppClassLoader@58437801] weaveinfo Join point 'method-execution(void com.acme.spring.TestBean.run())' in Type 'com.acme.spring.TestBean' (TestBean.java:11) advised by around advice from 'org.springframework.transaction.aspectj.AnnotationTransactionAspect' (AbstractTransactionAspect.aj:66)
[WebAppClassLoader@58437801] debug generating class 'com.acme.spring.TestBean$AjcClosure1'
A workaround is using aspectjweaver.jar as -javaagent
, but that is inconvenient if you don't control the JVM flags.
Comment From: weaselmetal
Ha, came here by chance. Isn't it the case that jetty is one of the servlet containers that doesn't contain a load time weaver and therefore requires a java agent? See this piece of information I read just yesterday: https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/aop.html#aop-aj-ltw-environment-generic
Comment From: emillundstrm
Ha, came here by chance. Isn't it the case that jetty is one of the servlet containers that doesn't contain a load time weaver and therefore requires a java agent? See this piece of information I read just yesterday: https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/aop.html#aop-aj-ltw-environment-generic
If this were the case, there'd be an error similar to:
java.lang.IllegalStateException: ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:spring-instrument-{version}.jar
But this is not the case. There is no error, because Jetty 9.x provides addTransformer.
And it wouldn't explain why it works from XML, but not from annotations.
Comment From: emillundstrm
I believe the difference from beans defined by @Component
and XML stems from this code in ConfigurationClassPostProcessor
:
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> resolve bean class at this point...
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
The XML bean has null configClassAttr
, while the annotation bean has the value "lite"
. This causes the annotated bean class to be resolved, which is seemingly too early for the AspectJ LoadTimeWeaver
to be active.
Comment From: goetzseb
Hey guys.
I stumbled across this searching for a solution to my problem which I have reported today on stackoverflow. Reading the issue description this seems to be the same issue I have. Same situation: upgrade from 4.3.14 to 5.3.3 under Tomcat 9. Only few classes are being woven. But until now I was not able to figure out which one did work.
I can confirm the workaround with the aspectjweaver agent is working, but not applicable for my use case.
Has there been any progress made on this or is there another workaround than the one with the javaagent?
Comment From: andrei-ivanov
AFAIK, this was the workaround, from the days of the application servers, which would deploy multiple applications and you could not install a JVM agent that would affect all, while in the modern days, the agent is the recommended way.
Comment From: goetzseb
For me this is quite a problem as the -javaagent:aspectjweaver.jar
approach rises the server startup to four times the normal duration (60sec. vs 250sec.). The class loading mechanism or the weaving must have changed somewhere between 5.1.20.RELEASE and 5.2.0.RELEASE. But I am not deep enough into the spring framework to investigate this any further.
Comment From: sbrannen
@emillundstrm, thanks for sharing your analysis.
The XML bean has null configClassAttr, while the annotation bean has the value "lite". This causes the annotated bean class to be resolved, which is seemingly too early for the AspectJ LoadTimeWeaver to be active.
If that's the case, this might be a regression caused by the changes in 40c62139ae6f8faa09fc0047ebc8ec65c5a2e809, stemming from the fact that a @Component
class that is picked up via @ComponentScan
is considered to be a candidate configuration class in "lite" mode (i.e., without the @Configuration
annotation and even without any @Bean
methods) and therefore has its class eagerly loaded.
@jhoeller, thoughts?
Comment From: mcolbert-unicon
Ran into this issue yesterday. We upgraded to 5.2.12 recently and noticed yesterday that our app would stop working after a while in our test environment. Threads were waiting on database connections from the connection pool. It turns out that our @Transactional
beans were no longer being advised by org.springframework.transaction.aspectj.AnnotationTransactionAspect
and therefore none of our connections were transactional. Glad we caught it and glad there is a fix coming. We dropped back to 5.1.20 based on the reporting by @goetzseb.
Comment From: sbrannen
Has there been any progress made on this
We will be working on a fix for Spring Framework 5.3.5 and potentially backporting to 5.2.14.
or is there another workaround than the one with the javaagent?
Well, I haven't tried this out, but based on my initial analysis of the regression, it appears that annotating your class with @javax.inject.Named
instead of @Component
or @Service
might be a viable workaround. Note, however, that this workaround would not apply to replacing @Controller
, @RestController
, or @Repository
with @Named
.
@goetzseb, please try annotating some of your component scanned classes with @Named
and let us know if that works for you as an interim solution.
Comment From: sbrannen
I have confirmed that this is a regression introduced in Spring Framework 5.2.0.
Well, I haven't tried this out, but based on my initial analysis of the regression, it appears that annotating your class with
@javax.inject.Named
instead of@Component
or@Service
might be a viable workaround. Note, however, that this workaround would not apply to replacing@Controller
,@RestController
, or@Repository
with@Named
.
Using @Named
instead of @Component
or @Service
works with component scanning.
I have also confirmed that the mere presence or meta-presence of @Component
on a bean will result in the reported behavior, even if the bean is not registered via component scanning.
Comment From: sbrannen
This regression has been fixed in master
and 5.2.x
.
Feel free to try it out in the upcoming snapshots for Spring Framework 5.3.5 and 5.2.14, and please report back on whether it works for you.
Comment From: goetzseb
Great to hear and thank you very much!
Comment From: pdeneve
This regression has been fixed in
master
and5.2.x
.Feel free to try it out in the upcoming snapshots for Spring Framework 5.3.5 and 5.2.14, and please report back on whether it works for you.
@sbrannen Doesn't work for me in either versions. It can be reproduced by cloning this repo, checking out branch 5.3.5
or 5.2.14
and running mvn test -pl spring.ltw
.