This demo project shows how to weave aspects into JDK/JRE classes with a Maven build. The basic steps are:
- Put
rt.jar
in the inpath for the AspectJ compiler ajc. This results in binary weaving, i.e. ajc creates new class files, many of which will be unchanged JRE files and some will contain woven aspect code. The aspects themselves will also be compiled into the target directory, of course. - Package all class files (JRE + aspect files) into a new JAR which will from now on serve as a replacement
for the original
rt.jar
. - Start the JVM with the modified
rt.jar
andaspectjrt.jar
on the boot classpath (normal classpath is not enough!), either by replacing the original boot classpath altogether with-Xbootclasspath:<path>
or by just prepending the two JARs to the usual boot classpath with-Xbootclasspath/p:<path>
.
What you can do to the JRE/JDK with AspectJ basically encompasses the full feature set. In some cases you just need to be careful not to create bootstrapping or recursion problems in situations when aspects use JRE code which in turn triggers aspect code etc.
Now you could do steps 1-3 manually from your command line or IDE, but I think it is much nicer to have a
standard Maven build for it. So I prepared a little sample project with a main module and two sub-modules.
One submodules takes care of weaving aspects into rt.jar
, the other one is just a demo showing how to use
the woven JRE from
- unit tests via Maven Surefire (you can use it the same way for integration tests via Maven Failsafe),
- applications (via Exec Maven plugin).
Just call mvn install
. First Maven will build the woven JAR, then run unit tests and subsequently run the
demo application, both of which will only work if JRE weaving was successful.
If you want to learn how to use this approach for your own purposes, you may want to look at:
- StringExtenderAspect: adds a
new method to JDK class
String
- ClassLoaderAspect: adds some log
output to
ClassLoader.loadClass(..)
and overriding methods - POM for JDK weaver module: weaves aspects into
rt.jar
and creates a new, woven JAR - POM for application module: uses woven JAR for running tests and the demo application
- Parent POM: preconfigures AspectJ Maven, MavenDependency and other plugins. E.g. in the
Maven Dependency configuration you can see how to use
<goal>properties</goal>
in order to expose dependency paths as Maven properties. The latter are referred to from the application POM in order to build the boot classpath for tests and application. - Sample unit test: checks if JRE weaving was indeed successful
- Sample application: uses method
String.repeat(int)
introduced by aspect.
Instead of zipping up all class files into a woven rt.jar version, it would be better and more efficient to just package the JRE files which have actually been modified by the AspectJ compiler. Unfortunately there is no compiler option to avoid writing unchanged binaries to the target directory. Andy Clement, if you are reading this, please consider adding this feature for an upcoming AspectJ release. Because if we had that feature, we could just create small JARs containing modified class files + aspects and prepend them to the boot classpath. This is like the normal JRE wearing glasses: Changed class files would be found in the small JAR, original JRE files would be found in the original rt.jar. Of course, you can do this manually by looking at the weave info on the console during the build and then zip up just the minimal number of class files into the woven rt.jar version. I have tried and it works beautifully. I just have not found an elegant way of automating this in a Maven build via resource filtering or whatever.