Spring AOP – the silver bullet for all our cross-cutting concerns?
Last week, I got the following challenge at work: How to monitor the average response time of every service call? There are several cool monitoring tools out there, such as JAMon, but for now, it is OK to only use some sort of logging mechanism. What is not OK, however, is to clutter the code base with logging statements scattered around every service call.
The motivation for AOP
The application I’m working on shares many of the characteristics of a traditionally designed object oriented system. When designing such a system, an early, crucial step is to decompose that system into smaller components. The motivation for this decomposition is to separate concerns, encapsulating and modulating the design, as well as identify the layers of the application. Our application has a typical outcome of that process: A presentation layer, a business logic layer (where the domain model resides) and a data access / data base layer.
Back to the challenge: If the code for handling logging the average response time for every service call is to be included in all components executing service calls, the logging code will typically be tangled with other code in those components. Such a logging mechanism cannot be decomposed to a single object or component – it is a concern that cannot be separated. Logging is a typical cross-cutting concern, a concern that span multiple objects or components in different layers. Other examples of cross cutting concerns include transaction management and performance profiling. All of which are possible to do in the object oriented programming paradigm, albeit not straight forward. And not to forget, in the traditional OOP way of thinking, we are looking at a significant amount of work writing that ugly code. Wouldn’t it be neat if there was possible to modularize those cross-cutting concerns?
As it turns out, it is. Everybody stand back, aspects to the rescue! Aspect Oriented Programming (AOP) is designed to do just that – letting you modularize cross cutting concerns and weave them into the application at a later point, either compile-time or run-time. Compile time weaving is perhaps the most powerful of the two approaches. This approach manipulates the byte-code, meaning in effect that what you see in your files is not exactly what you get. Your compiled class file is a slightly altered version of your java source file, because the aspect are weaved with your code when compiled. Because compile time weaving alters the compile process, the run-time weaving approach has emerged as a desirable alternative. This will not alter the compile process, but instead put a proxy before the code that is to be advised runtime.
My current project relies on Spring, and luckily for us (and me!), Spring has a built in AOP infrastructure out of the box.
Like all Spring things, it is very well documented. Most of the following content can be found in this documentation. As just stated, Spring AOP takes the run-time weaving approach to AOP. Like most Spring things, it is pretty easy to set up, but remember, what happens under the hood is that Spring generates some kind of proxy for you runtime. (As already mentioned, transaction management is a typical example of a cross-cutting concern. If your application is using the transaction management offered by Spring your application will in fact already be using dynamic AOP proxies behind the scenes.)
The proxy generated by Spring is either a dynamic Java proxy or a CGLib proxy. So what’s the difference? The dynamic Java Proxy approach is the default choice. The proxy generated run time by Spring implements the same interface that the advised service implements. This approach is listed by Spring as the preferred one, because it encourages coding against interfaces. But that implies that there is only possible to advise the methods defined in the interface implemented by the service.
What if you want to intercept a method that is not listed in this interface, or even worse, what if your class doesn’t implement an interface at all? Not to worry though, just tell Spring to unleash it’s CGLib abilities!
Just set the proxy-target-class="true" to true in the aop config, and you’re ready to go. Spring will in this case create a proxy that is an extension of your advised class, NOT an implementation of the same interface that your service implements. In effect, all methods, including those methods not defined in any interface to be intercepted.
Either way, Spring AOP will behave along the lines I have sketched here:
In the sketch, I have a instance of FooService somewhere, called fooService. When I invoke foo() on that instance, I expect foo behaviour from FooService. Which, fortunately, I will get also in the Spring AOP world, albeit not straight away; Spring will create a proxy of the FooService at runtime, so that when I invoke foo on fooService, the bean loaded is actually a proxy. The Spring generated proxy provides hooks for the advice. Pretty neat. The most general advice to create in Spring AOP is an around advice, allowing for doing something both before and after proceeding. Yet there exists several others, and for the record, the Spring team recommends to use the least powerful advice type that solves your task.
But back to the challenge: The around advice fits like a glove for monitoring the average time of service calls! Before proceeding with the foo() call, just grab the current timestamp. And after foo() has completed, take yet another timestamp, and log the difference between the two timestamps.
Limitations of the proxy-based approach?
At this time, Spring AOP looked like a proper silver bullet to target all our cross cutting concerns. Then another challenge emerged: Let’s monitor the execution time of every method! This seemed trivial at first, being able to solve the challenge of logging response time for service calls.
It turned out to be quite a big difference between the two however – they differ in how the advised methods are invoked. In the first challenge, the advised methods are always called from an external caller, so that a proxy could be placed ahead of the advised method. In the second challenge, the advised method could be called internally. Consult the sketch, and imagine that we wanted to monitor the execution of a method bar() that is called from foo():
The fooService instance holds a reference to a FooService proxy. The call fooService.foo() will be intercepted by the advice, but when the target object, the real FooService, is reached, any further method calls cannot be intercepted. The reason is that these calls are not invoked on the proxy, but on the this reference. In short, calls to self cannot be intercepted by a proxy.
Spring AOP offers another AOP syntax – @AspectJ annotations. This allows for more options for advising, but because Spring is still in charge of things, the weaving is still not done compile-time, but run-time. Meaning, we are still dealing with proxies. Meaning, we still cannot monitor such internal method invocations.
The only solution I’ve found to this challenge is to introduce an aspect weaver that can weave compile time. But AspectJ is a stranger to me and the rest of the project, and the project is not to happy about taking the risk of introducing something that alters the compile process. Do you have a solution for for advising class internal method calls without introducing an compile-time aspect weaver such as AspectJ?
Please let me know!