== Sample Apps Showing How to Use AspectJ Natively with @Aspect
and Spring Boot
image:https://travis-ci.com/lofidewanto/spring-boot-aspectj.svg?branch=master["Build Status", link="https://travis-ci.com/lofidewanto/spring-boot-aspectj"]
AspectJ lets you write aspects using Java annotations @Aspect
and friends. Conveniently, but sometimes confusingly, Spring lets you use the same programming model, and then uses the AspectJ tooling APIs at runtime, so some people can't tell the difference between Spring AOP and AspectJ. You don't need any special tools to use Spring AOP, but it has it's limitations, one of which is performance (you take a hit on startup while all the beans are analysed, and you take another smaller hit when the app is running and one of your aspects is executed through a proxy).
Luckily, it is quite easy to set up AspectJ to weave your code natively (not with Spring), and it is sometimes faster - just more steps to set up. In fact, there are multiple ways of using AspectJ to weave your code, some of which happen at runtime, and one (broadly speaking) that happens at compile time. Compile time is faster (unsurprisingly), but not massively (heuristically, maybe a 20% effect).
NOTE: The projects are updated with AspectJ 1.9.5 (included with Spring Boot 2.2.4) and use OpenJDK 11 now.
NOTE: AspectJ 1.8.13 (included with Spring Boot 1.5.9) has some major performance improvements affecting Spring AOP - the optimizations were suggested by running the benchmarks in this project. If you think Spring AOP might be slowing your app down on startup, upgrade now.
== Contents
== Converting from Spring AOP to AspectJ
You need to tell AspectJ where to find your aspects, and which classes you want it to weave. You also need to tell Spring not to try and weave the same aspects again (in Spring Boot you can do that with spring.aop.auto=false
). The configuration for AspectJ lives in an https://eclipse.org/aspectj/doc/next/devguide/ltw-configuration.html[XML file] aop.xml
, which can be in META-INF
or in a package called org.aspectj
. Here's a simple example:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
WARNING: If the AspectJ config file is in META-INF/aop.xml
then the fat jar built by Spring Boot has it is in the wrong place, and there are nasty looking https://github.com/spring-projects/spring-boot/issues/7587[errors on startup]. The app still works and the aspects are woven, but you can work around it by putting aop.xml
in the org.aspectj
package instead of in META-INF
.
== Load Time Weaving
One of the runtime options is called "load time weaving", and Spring even has an annotation @EnableLoadTimeWeaving
to help get it off the ground. The Spring annotation is rooted in the culture and mechanics of application servers - it only makes sense if you have multiple apps running in the same JVM with class loader isolation. What happens is that it uses a special ClassLoader
that manipulates the classes as they are loaded.
The other option for load time weaving is the AspectJ agent, which you add to the JVM on the command line, and it can look at all the classes and potentially weave them with aspects.
E.g. :
$ cd ltw
$ mvn clean install
$ java -javaagent:$HOME/.m2/repository/org/aspectj/aspectjweaver/[version]/aspectjweaver-[version].jar -jar target/*.jar
You can also use the copied version of aspectweaver-[version].jar in the target/aspectj directory of the Maven project, since it is copied to that directory with Maven Dependency plugin.
$ java -javaagent:target/aspectj/aspectjweaver-[version].jar -jar target/*.jar
If you don't add the agent (and don't provide an implementation of Interceptor.aspectOf()
) you might get a confusing "cyclic dependency" error from Spring on startup. It's not really cyclic, but because the Interceptor
in this sample is an @EventListener
Spring is trying to create it very early and gets confused. The real problem is just that Aspects.aspectOf()
doesn't work unless the agent is attached.
NOTE: @EnableLoadTimeWeaving
doesn't actually make much sense in a Spring Boot app that is going to run in its own process - you can just add the AspectJ agent. Many people confuse "load time weaving" with "runtime weaving" generally (because for a long time it was the only option for many Spring apps). So if someone asks you to implement "load time weaving" in a Spring Boot app, just add the agent.
NOTE: The "timing" library is a dependency of this project so it gets woven at runtime and prints out interesting (but verbose) information about timings for bean initialization. This is in addition to the aspects library.
== Compile Time Weaving
To weave the aspects at compile time (and also optionally use the AspectJ language, as opposed to Java with annotations), you need an additional plugin in your build. If all you need is to weave existing byte code, the plugin configuration is very simple: