Nikita, today is your lucky day. I just had a moment and was curious how to make my code snippet from https://www.eclipse.org/aspectj/doc/released/README-187.html, which obviously you found before, work in the context of Spring Boot. I just used my Maven Spring Boot playground project. Depending on which Java version you are using, you either need to make sure that tools.jar from JDK 8 is defined as a system-scoped dependency and also copied into the executable Spring uber JAR, or you need to make sure that the Java attach API is activated in Java 9+. Here is what I did for Java 8:
Maven:
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
<!-- (...) -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>spring.aop.DemoApplication</mainClass>
<!-- Important for tools.jar on Java 8 -->
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
The <includeSystemScope>
option is necessary because otherwise Boot does not know how to find the attach API classes. Just do something equivalent in Gradle and you should be fine.
Java:
You need to know that in order to attach an agent, it must be a file on the file system, not just any resource or input stream. This is how the attach API works. So unfortunately, you have to copy it from the uber JAR to the file system first. Here is how you do it:
public static boolean dynamicallyLoadAspectJAgent() {
String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
int p = nameOfRunningVM.indexOf('@');
String pid = nameOfRunningVM.substring(0, p);
try {
VirtualMachine vm = VirtualMachine.attach(pid);
ClassLoader classLoader = AgentLoader.class.getClassLoader();
try (InputStream nestedJar = Objects.requireNonNull(classLoader.getResourceAsStream("BOOT-INF/lib/aspectjweaver-1.9.4.jar"))) {
File targetFile = new File("aspectjweaver.jar");
java.nio.file.Files.copy(nestedJar, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
vm.loadAgent(targetFile.getAbsolutePath());
}
try (InputStream nestedJar = Objects.requireNonNull(classLoader.getResourceAsStream("BOOT-INF/lib/spring-instrument-5.1.9.RELEASE.jar"))) {
File targetFile = new File("spring-instrument.jar");
java.nio.file.Files.copy(nestedJar, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
vm.loadAgent(targetFile.getAbsolutePath());
}
vm.detach();
}
catch (Exception e) {
LOGGER.error("Exception while attaching agent", e);
return false;
}
return true;
}
Besides, in my case the files were unter BOOT-INF/lib
, not WEB-INF/lib
.
Update: You said you have this follow-up problem somewhere along the line (reformatted for readability):
failed to access class
org.aspectj.weaver.loadtime.Aj$WeaverContainer
from class
org.aspectj.weaver.loadtime.Aj
(
org.aspectj.weaver.loadtime.Aj$WeaverContainer is in
unnamed module of
loader 'app';
org.aspectj.weaver.loadtime.Aj is in
unnamed module of
loader org.springframework.boot.loader.LaunchedURLClassLoader @3e9b1010
)
at org.aspectj.weaver.loadtime.Aj.preProcess(Aj.java:108)
This means that Aj
is unable to find its own inner class Aj.WeaverContainer
. This indicates that they are loaded at different points in time and in different classloaders. When remote-debugging into my sample Boot application starting from an executable JAR, I see that the application classloader is actually the LaunchedURLClassLoader
's parent, i.e. the class loaded in the parent is trying to access another class only available to its child classloader, which is impossible in Java. It only works the other way around.
Maybe it helps not to import and reference AspectJ weaver classes from inside the agent loader. Try commenting out the loadJavaAgent()
and isAspectJAgentLoaded()
methods and also remove import org.aspectj.weaver.loadtime.Agent;
. Then in your application just directly call AgentLoader.dynamicallyLoadAspectJAgent()
and see if this helps. I have some more aces up my sleeves with regard to agent loading, but let's keep it as simple as possible first.