There are multiple available AOP libraries today, and these need to be able to answer a number of questions:
- Is it compatible with my existing or new application?
- Where can I implement AOP?
- How quickly will it integrate with my application?
- What is the performance overhead?
In this article, we'll look at answering these questions and introduce Spring AOP and AspectJ – the two most popular AOP frameworks for Java.
2. AOP Concepts
Before we begin, let's do a quick, high-level review of terms and core concepts:
- Aspect – a standard code/feature that is scattered across multiple places in the application and is typically different than the actual Business Logic (for example, Transaction management). Each aspect focuses on a specific cross-cutting functionality
- Joinpoint – it's a particular point during execution of programs like method execution, constructor call, or field assignment
- Advice – the action taken by the aspect in a specific joinpoint
- Pointcut – a regular expression that matches a joinpoint. Each time any join point matches a pointcut, a specified advice associated with that pointcut is executed
- Weaving – the process of linking aspects with targeted objects to create an advised object
3. Spring AOP and AspectJ
Now, let's discuss Spring AOP and AspectJ across a number of axis – such as capabilities, goals, weaving, internal structure, joinpoints, and simplicity.
3.1. Capabilities and Goals
Simply put, Spring AOP and AspectJ have different goals.
Spring AOP aims to provide a simple AOP implementation across Spring IoC to solve the most common problems that programmers face. It is not intended as a complete AOP solution – it can only be applied to beans that are managed by a Spring container.
On the other hand, AspectJ is the original AOP technology which aims to provide complete AOP solution. It is more robust but also significantly more complicated than Spring AOP. It's also worth noting that AspectJ can be applied across all domain objects.
Both AspectJ and Spring AOP uses the different type of weaving which affects their behavior regarding performance and ease of use.
AspectJ makes use of three different types of weaving:
- Compile-time weaving: The AspectJ compiler takes as input both the source code of our aspect and our application and produces a woven class files as output
- Post-compile weaving: This is also known as binary weaving. It is used to weave existing class files and JAR files with our aspects
- Load-time weaving: This is exactly like the former binary weaving, with a difference that weaving is postponed until a class loader loads the class files to the JVM
For more in-depth information on AspectJ itself, head on over to this article.
As AspectJ uses compile time and classload time weaving, Spring AOP makes use of runtime weaving.
With runtime weaving, the aspects are woven during the execution of the application using proxies of the targeted object – using either JDK dynamic proxy or CGLIB proxy (which are discussed in next point):
3.3. Internal Structure and Application
Spring AOP is a proxy-based AOP framework. This means that to implement aspects to the target objects, it'll create proxies of that object. This is achieved using either of two ways:
- JDK dynamic proxy – the preferred way for Spring AOP. Whenever the targeted object implements even one interface, then JDK dynamic proxy will be used
- CGLIB proxy – if the target object doesn't implement an interface, then CGLIB proxy can be used
We can learn more about Spring AOP proxying mechanisms from the official docs.
AspectJ, on the other hand, doesn't do anything at runtime as the classes are compiled directly with aspects.
And so unlike Spring AOP, it doesn't require any design patterns. To weave the aspects to the code, it introduces its compiler known as AspectJ compiler (ajc), through which we compile our program and then runs it by supplying a small (< 100K) runtime library.
In section 3.3, we showed that Spring AOP is based on proxy patterns. Because of this, it needs to subclass the targeted Java class and apply cross-cutting concerns accordingly.
But it comes with a limitation. We cannot apply cross-cutting concerns (or aspects) across classes that are “final” because they cannot be overridden and thus it would result in a runtime exception.
The same applies for static and final methods. Spring aspects cannot be applied to them because they cannot be overridden. Hence Spring AOP because of these limitations, only supports method execution join points.
However, AspectJ weaves the cross-cutting concerns directly into the actual code before runtime. Unlike Spring AOP, it doesn't require to subclass the targetted object and thus supports many others joinpoints as well. Following is the summary of supported joinpoints:
|Joinpoint||Spring AOP Supported||AspectJ Supported|
|Static initializer execution||No||Yes|
It's also worth noting that in Spring AOP, aspects aren't applied to the method called within the same class.
That's obviously because when we call a method within the same class, then we aren't calling the method of the proxy that Spring AOP supplies. If we need this functionality, then we do have to define a separate method in different beans, or use AspectJ.
Spring AOP is obviously simpler because it doesn't introduce any extra compiler or weaver between our build process. It uses runtime weaving, and therefore it integrates seamlessly with our usual build process. Although it looks simple, it only works with beans that are managed by Spring.
However, to use AspectJ, we're required to introduce the AspectJ compiler (ajc) and re-package all our libraries (unless we switch to post-compile or load-time weaving).
This is, of course, more complicated than the former – because it introduces AspectJ Java Tools (which include a compiler (ajc), a debugger (ajdb), a documentation generator (ajdoc), a program structure browser (ajbrowser)) which we need to integrate with either our IDE or the build tool.
As far as performance is concerned, compile-time weaving is much faster than runtime weaving. Spring AOP is a proxy-based framework, so there is the creation of proxies at the time of application startup. Also, there are a few more method invocations per aspect, which affects the performance negatively.
On the other hand, AspectJ weaves the aspects into the main code before the application executes and thus there's no additional runtime overhead, unlike Spring AOP.
For these reasons, the benchmarks suggest that AspectJ is almost around 8 to 35 times faster than Spring AOP.
This quick table summarizes the key differences between Spring AOP and AspectJ:
|Implemented in pure Java||Implemented using extensions of Java programming language|
|No need for separate compilation process||Needs AspectJ compiler (ajc) unless LTW is set up|
|Only runtime weaving is available||Runtime weaving is not available. Supports compile-time, post-compile, and load-time Weaving|
|Less Powerful – only supports method level weaving||More Powerful – can weave fields, methods, constructors, static initializers, final class/methods, etc…|
|Can only be implemented on beans managed by Spring container||Can be implemented on all domain objects|
|Supports only method execution pointcuts||Support all pointcuts|
|Proxies are created of targeted objects, and aspects are applied on these proxies||Aspects are weaved directly into code before application is executed (before runtime)|
|Much slower than AspectJ||Better Performance|
|Easy to learn and apply||Comparatively more complicated than Spring AOP|
5. Choosing the Right Framework
If we analyze all the arguments made in this section, we'll start to understand that it's not at all that one framework is better than another.
Simply put, the choice heavily depends on our requirements:
- Framework: If the application is not using Spring framework, then we have no option but to drop the idea of using Spring AOP because it cannot manage anything that's outside the reach of spring container. However, if our application is created entirely using Spring framework, then we can use Spring AOP as it's straightforward to learn and apply
- Flexibility: Given the limited joinpoint support, Spring AOP is not a complete AOP solution, but it solves the most common problems that programmers face. Although if we want to dig deeper and exploit AOP to its maximum capability and want the support from a wide range of available joinpoints, then AspectJ is the choice
- Performance: If we're using limited aspects, then there are trivial performance differences. But there are sometimes cases when an application has more than tens of thousands of aspects. We would not want to use runtime weaving in such cases so it would be better to opt for AspectJ. AspectJ is known to be 8 to 35 times faster than Spring AOP
- Best of Both: Both of these frameworks are fully compatible with each other. We can always take advantage of Spring AOP whenever possible and still use AspectJ to get support of joinpoints that aren't supported by the former
In this article, we analyzed both Spring AOP and AspectJ, in several key areas.
We compared the two approaches to AOP both on flexibility as well as on how easily they will fit with our application.