I m trying to add aspectJ LTW integration in our Springboot Application. I m trying to use java way of specifying TomcatInstrumenatbleClassLoader and TomcatLoadTimeWeaver instead of xml way.
Refereing Spring Document But when starting my app it errors out like below
org.apache.catalina.loader.WebappClassLoaderBase checkStateForResourceLoading
INFO: Illegal access: this web application instance has been stopped already. Could not load [META-INF/spring.factories]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [META-INF/spring.factories]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1432)
at org.apache.catalina.loader.WebappClassLoaderBase.findResources(WebappClassLoaderBase.java:1003)
at org.apache.catalina.loader.WebappClassLoaderBase.getResources(WebappClassLoaderBase.java:1110)
at org.springframework.core.io.support.SpringFactoriesLoader.loadSpringFactories(SpringFactoriesLoader.java:143)
at org.springframework.core.io.support.SpringFactoriesLoader.loadFactoryNames(SpringFactoriesLoader.java:132)
at org.springframework.boot.SpringApplication.getSpringFactoriesInstances(SpringApplication.java:456)
at org.springframework.boot.SpringApplication.getSpringFactoriesInstances(SpringApplication.java:450)
at org.springframework.boot.SpringApplication.getBootstrapRegistryInitializersFromSpringFactories(SpringApplication.java:294)
at org.springframework.boot.SpringApplication.<init>(SpringApplication.java:285)
at com.org.pack.app.Application.main(Application.java:37)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Exception in thread "main" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [META-INF/spring.factories]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1432)
at org.apache.catalina.loader.WebappClassLoaderBase.findResources(WebappClassLoaderBase.java:1003)
at org.apache.catalina.loader.WebappClassLoaderBase.getResources(WebappClassLoaderBase.java:1110)
at org.springframework.core.io.support.SpringFactoriesLoader.loadSpringFactories(SpringFactoriesLoader.java:143)
at org.springframework.core.io.support.SpringFactoriesLoader.loadFactoryNames(SpringFactoriesLoader.java:132)
at org.springframework.boot.SpringApplication.getSpringFactoriesInstances(SpringApplication.java:456)
at org.springframework.boot.SpringApplication.getSpringFactoriesInstances(SpringApplication.java:450)
at org.springframework.boot.SpringApplication.getBootstrapRegistryInitializersFromSpringFactories(SpringApplication.java:294)
at org.springframework.boot.SpringApplication.<init>(SpringApplication.java:285)
at com.org.pack.app.ImportApplication.main(ImportApplication.java:37)
My code goes as follows:
@SpringBootApplication
@Configuration
@ComponentScan({<Our Depedencies>})
@EnableBatchProcessing
@EnableLoadTimeWeaving
@EnableCaching
public class Application {
/**
* Main method
*
* @param args - arguments
*/
public static void main(String[] args) {
DefaultResourceLoader defaultResourceLoader = new DefaultResourceLoader(new TomcatInstrumentableClassLoader());
SpringApplication application = new SpringApplication(defaultResourceLoader, Application.class);
application.run(args);
// SpringApplication.run(Application.class, args);
}
}
@Configuration
public class MyConfiguration {
/**
* Load Time viewer for tomcat based app
*
* @return - Instance of Intrumentaiton Load TIme Weaver
*/
@Bean
public LoadTimeWeaver loadTimeWeaver() {
return new InstrumentationLoadTimeWeaver();
}
}
Dependencies (excluding other dependencies): Multi-module project Parent Pom
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
Project Root POM
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.74</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.19</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument-tomcat</artifactId>
<version>4.3.30.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.20</version>
</dependency>
Module's POM
<!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument-tomcat</artifactId>
</dependency>
The document also says for tomcat 6.0 or higher the setting is not needed, but getting error like
org.springframework.beans.factory.BeanCreationException: Error creating bean with name '<our entity factory class>' defined in class path resource [com/org/pack/configuration/MyConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loadTimeWeaver' defined in org.springframework.context.annotation.LoadTimeWeavingConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.instrument.classloading.LoadTimeWeaver]: Factory method 'loadTimeWeaver' threw exception; nested exception is java.lang.IllegalStateException: ClassLoader [org.springframework.boot.loader.LaunchedURLClassLoader] 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
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:338)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332)
at com.org.pack.app.Application.main(Application.java:41)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
If you are running your applications as executable JARs on JRE 9+ (Spring Boot ones or other types), probably Agent Embedder Maven Plugin is what you want.
It enables you to embed a java agent in your executable JAR and have it started automatically using the Launcher-Agent-Class
JVM mechanism.
Unique features added by this plugin and unavailable via the original JVM mechanism: You can
Spoiler: I am the author of the plugin and also happen to be the current maintainer of AspectJ.
P.S.: In the case of the AspectJ weaver, as the JVM starts the agent very early, weaving will be active without extra Spring configuration and it should work for all classloaders - no more ClassLoader [...] does NOT provide an 'addTransformer(ClassFileTransformer)' method
errors as seen when hot-attaching the weaver via spring-instrument.jar
.
Firstly, your aspect was faulty, it would never have matched anything. The pointcut
set(@com.example.TestAspect * *)
was faulty, because TestAspect
is not an annotation. The correct
pointcut is
set(@com.example.TestAnnotation * *)
You used (single line, line breaks added for readability)
java
-jar target/aspectjlib-0.0.1-SNAPSHOT.jar
-javagent:/Users/carvind/IssueREproduce/javaagents/spring-instrument.jar
-javaagent:/Users/carvind/IssueREproduce/javaagents/aspectjweaver.jar
There is a typo -javagent
instead of javaagent
in the first agent parameter. But the bigger problem is the argument order. Everything after -jar my.jar
will be treated as program arguments for the main class. I.e., the -javaagent
JVM arguments need to come before -jar
.
You actually do not need both agents either. Your application works with
spring-instument
+ @EnableLoadTimeWeaving
aspectjweaver
without @EnableLoadTimeWeaving
.If you accept my pull request, pull and then rebuild all your modules, you can successfully run the application from the root directory as follows:
$ java -javaagent:IssueREproduce/javaagents/spring-instrument.jar -jar IssueREproduce/aspectjlib/target/aspectjlib-0.0.1-SNAPSHOT.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.1.RELEASE)
2024-01-09 10:03:56.975 INFO 20472 --- [ main] c.e.aspectjlib.AspectjlibApplication : Starting AspectjlibApplication v0.0.1-SNAPSHOT on Xander-UB with PID 20472 (C:\Users\alexa\Documents\java-src\SO_AJ_SprintBootError_77763616\IssueREproduce\aspectjlib\target\aspectjlib-0.0.1-SNAPSHOT.jar start
ed by alexa in C:\Users\alexa\Documents\java-src\SO_AJ_SprintBootError_77763616)
2024-01-09 10:03:56.983 INFO 20472 --- [ main] c.e.aspectjlib.AspectjlibApplication : No active profile set, falling back to default profiles: default
[LaunchedURLClassLoader@1bce4f0a] info AspectJ Weaver Version 1.9.19 built on Wednesday Dec 21, 2022 at 06:57:22 PST
[LaunchedURLClassLoader@1bce4f0a] info register classloader org.springframework.boot.loader.LaunchedURLClassLoader@1bce4f0a
[LaunchedURLClassLoader@1bce4f0a] info using configuration file:/C:/Users/alexa/Documents/java-src/SO_AJ_SprintBootError_77763616/IssueREproduce/aspectjlib/target/aspectjlib-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/META-INF/aop.xml
[LaunchedURLClassLoader@1bce4f0a] info register aspect com.example.TestAspect
2024-01-09 10:03:58.330 INFO 20472 --- [ main] c.e.aspectjlib.AspectjlibApplication : Started AspectjlibApplication in 2.297 seconds (JVM running for 3.071)
[LaunchedURLClassLoader@1bce4f0a] weaveinfo Join point 'field-set(java.lang.String com.example.TestModel1.field1)' in Type 'com.example.TestModel1' (TestModel1.java:7) advised by before advice from 'com.example.TestAspect' (TestAspect.java)
[LaunchedURLClassLoader@1bce4f0a] weaveinfo Join point 'field-set(java.lang.Double com.example.TestModel1.field4)' in Type 'com.example.TestModel1' (TestModel1.java:7) advised by before advice from 'com.example.TestAspect' (TestAspect.java)
set(String com.example.TestModel1.field1)
set(Double com.example.TestModel1.field4)
[LaunchedURLClassLoader@1bce4f0a] weaveinfo Join point 'field-set(java.lang.Integer com.example.TestModel2.field2)' in Type 'com.example.TestModel2' (TestModel2.java:7) advised by before advice from 'com.example.TestAspect' (TestAspect.java)
[LaunchedURLClassLoader@1bce4f0a] weaveinfo Join point 'field-set(boolean com.example.TestModel2.field3)' in Type 'com.example.TestModel2' (TestModel2.java:7) advised by before advice from 'com.example.TestAspect' (TestAspect.java)
set(Integer com.example.TestModel2.field2)
set(boolean com.example.TestModel2.field3)
Or after removing @EnableLoadTimeWeaving
and another rebuild:
$ java -javaagent:IssueREproduce/javaagents/aspectjweaver.jar -jar IssueREproduce/aspectjlib/target/aspectjlib-0.0.1-SNAPSHOT.jar
[LaunchedURLClassLoader@7113b13f] info AspectJ Weaver Version 1.9.19 built on Wednesday Dec 21, 2022 at 06:57:22 PST
[LaunchedURLClassLoader@7113b13f] info register classloader org.springframework.boot.loader.LaunchedURLClassLoader@7113b13f
[LaunchedURLClassLoader@7113b13f] info using configuration file:/C:/Users/alexa/Documents/java-src/SO_AJ_SprintBootError_77763616/IssueREproduce/aspectjlib/target/aspectjlib-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/META-INF/aop.xml
[LaunchedURLClassLoader@7113b13f] info register aspect com.example.TestAspect
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.1.RELEASE)
2024-01-09 10:05:36.669 INFO 9836 --- [ main] c.e.aspectjlib.AspectjlibApplication : Starting AspectjlibApplication v0.0.1-SNAPSHOT on Xander-UB with PID 9836 (C:\Users\alexa\Documents\java-src\SO_AJ_SprintBootError_77763616\IssueREproduce\aspectjlib\target\aspectjlib-0.0.1-SNAPSHOT.jar started
by alexa in C:\Users\alexa\Documents\java-src\SO_AJ_SprintBootError_77763616)
2024-01-09 10:05:36.674 INFO 9836 --- [ main] c.e.aspectjlib.AspectjlibApplication : No active profile set, falling back to default profiles: default
2024-01-09 10:05:37.699 INFO 9836 --- [ main] c.e.aspectjlib.AspectjlibApplication : Started AspectjlibApplication in 1.712 seconds (JVM running for 2.836)
[LaunchedURLClassLoader@7113b13f] weaveinfo Join point 'field-set(java.lang.String com.example.TestModel1.field1)' in Type 'com.example.TestModel1' (TestModel1.java:7) advised by before advice from 'com.example.TestAspect' (TestAspect.java)
[LaunchedURLClassLoader@7113b13f] weaveinfo Join point 'field-set(java.lang.Double com.example.TestModel1.field4)' in Type 'com.example.TestModel1' (TestModel1.java:7) advised by before advice from 'com.example.TestAspect' (TestAspect.java)
set(String com.example.TestModel1.field1)
set(Double com.example.TestModel1.field4)
[LaunchedURLClassLoader@7113b13f] weaveinfo Join point 'field-set(java.lang.Integer com.example.TestModel2.field2)' in Type 'com.example.TestModel2' (TestModel2.java:7) advised by before advice from 'com.example.TestAspect' (TestAspect.java)
[LaunchedURLClassLoader@7113b13f] weaveinfo Join point 'field-set(boolean com.example.TestModel2.field3)' in Type 'com.example.TestModel2' (TestModel2.java:7) advised by before advice from 'com.example.TestAspect' (TestAspect.java)
set(Integer com.example.TestModel2.field2)
set(boolean com.example.TestModel2.field3)
If you compare the log output, you see that aspectjweaver
kicks in earlier than spring-instrument
, which makes it more powerful and universal, if your aspects also weave Spring classes. It depends on the use case, if spring-instrument
is enough or not. Here, both work fine.
Simply use JVM parameter -javaagent:/path/to/aspectjweaver-1.9.x.jar
. When starting Spring or Spring Boot apps from the console, I never managed to get them working without a Java agent on the command line. Your custom weaver bean does not work with all classloaders, as you can see from the error message.
According to the hint in all release notes from AspectJ releases since 1.9.8 or so, you also need --add-opens java.base/java.lang=ALL-UNNAMED
on the JVM command line on JDK 16+. Sorry, but this is not AspectJ's fault. More recent JDKs simply do not provide a proper replacement for the now restricted APIs the load-time weaver needs to do its job.