I’m a big fan of Spring Boot and I’m also a very big fan Javassist. I came on an interesting issue where everything was working OK from an IDE but not when launched on a test server.
The instrumentation is pretty simple as is consists in the following code:
This code is called from the main
just before calling SpringApplication.run
in order to instrument the class before it gets any chance of being loaded during the context loading. I works fine from an IDE but once deployed on the test server it failed with a nice stack trace:
10:37:56.325 [main] ERROR calling.Class - Cannot perform instrumentation javassist.NotFoundException: com.package.Class
at javassist.ClassPool.get(ClassPool.java:450) ~[javassist-3.18.1-GA.jar!/:na]
The diagnostic is pretty easy: from an IDE, the application runs with a normal classpath containing some classes
directories and the jars for the dependencies. On the server, the application is running with Spring Boot’s Über-jar as classpath. As Javassist relies on this classpath to find the class to instrument he’ll only have access to the Spring Boot loader classes and the classes belonging directly to the application, but not the dependencies that are packed into the Über-jar under the lib
directory.
Solution
The solution is based on the SpringApplicationRunListener
interface which will allow us to call SpringApplication.run
and run Javassist as soon as possible. The code above will be slightly changed:
Additionally, we need create (or update) the META-INF/spring.factories
to declare this new listener:
At this point, this is not enough and the instrumentation is still failing with the same exception. The last tweak will be done in the doBlackMagic
method:
And voilà.