Friday, April 25, 2014

Configuring Aspect With Spring AOP

tl;dr Using AOP involves you identifying cross cutting concerns (Advice), extracting them out into a separate module and have them called at certain points in your code execution (Join Point) using certain expressions (Point-cut) to select these points where you want your cross cutting concern logic to run. Configuring thus involve telling Spring where your extracted logic is and the point-cut required to know where to apply them.

This post is part of the Configuring Spring Series.

Develop a sizable application and you would soon find yourself repeating some operations: certain logic; function calls that keeps recurring now and then. An example of such a recurring operation is logging. At different places, through out your application, you may find the need to have some information logged; either for debugging purposes or for auditing or for whatever reasons logging was invented. After a while you would soon find out that the log calls are sprinkled about the place in your code base. The drawback of having code scattered this way is even more if the code being repeated is not as trivial as log calls; Not good.

I have worked on a code base where this was the case. This is generally not advisable for various reasons: One, it fast become tedious having to repeat the same boring logic over and over again. Two, it leads to code entanglement as you have the flow of business logic being interrupted by mundane things that does not add to the business logic at hand. Third, it leads to code scattering. Same logic scatted all over the place. Even though the logic might be encapsulated into a method or function call, the method or function call would still be scattered all about the place. Not too good.

The explicitly of having to repeat these recurring calls might seem like a good thing, but with scale, it becomes not that much of a good thing.

Would it not be nice if it were possible to extract out, these recurring method calls, have it in some sort of separate module and then have it magically called when those operations that require them are reached during your code execution?

These kind of problems/situations is what Aspect Oriented Software Development seeks to solve/mitigate with Aspect Oriented Programming. It allows you to extract operations that cut across different aspect of your application and have them called when needed.

These kind of operations are usually called cross-cutting concerns, because, well they cut across various concerns in your application. :)

Apart from logging, operations that you would most likely run into, that can easily be classified as being cross-cutting concerns includes: Authorization, Error Handling, Performance Monitoring etc.

This post describes the process of configuring Spring framework in other to be able to use AOP in your application. As you might have guessed, the topic of Aspect Oriented Programming is a very broad one. And since these posts are mainly focused on configuration activities in Spring, I would not go into details of programming with Aspects; but instead explain basic concepts needed to get started and provide an overview of how AOP can be configured in Spring Application.

If you are familiar with event driven languages like Javascript (or any UI framework in most languages), you won't be totally wrong if you think the concept of Aspect sounds very familiar to what you do when you define functions as event handlers. And with event delegation, you can have this function registered to be executed when certain operations/event occurs within your application. The same reason why doing this makes sense also applies to why Aspects are recommended.

So let's get started with some of the basic concepts.


Join Points, Advice, Point-cuts, Weaving and Aspects
AOP comes with its own set of jargons. And unfortunately these jargons are far from being intuitive. Join point, point-cut? really? But like most jargons, the idea expressed by these words are very straight forward. Would explain them via examples:

Let us say we are building a banking application and we have identified logging to be a cross cutting concern since various method calls might require us to log certain information. In our example we will need to log things when an account is credited (creditAccountOperation()) or debited ( debitAccountOperation()). We also want to log info when a customer accesses their online banking account. The logging operation does not just log information to the console, but also writes to an audit log and also sends an email.

We have captured all these and put it in a method call we aptly call log().

With that, let us now understand the AOP terminologies.

Join Points
What is the Join Point?

For our log() use case, we realized we wanted to log when debitAccountOperation() and creditAccountOperation() are called. And also when user access methods like userLogin() or userLogout() are called.

Those part of the application where you want certain cross cutting operations, (in our case logging), applied is referred to as Join Points.

Advice
So when debitAccountOperation() or creditAccountOperation() is executed, you want logging to be done. Your logging logic might look thus:

public void log() {
// do logging
// log to console
// write to audit log file
// send an email to admin
}

The log(), the method that would be executed when our Join points are encountered, is, in AOP speaks, referred to as Advice.

Point-cuts
Now we have identified the places in our application (Join-points) at which point we want certain logic (Advices) to be executed. But how do we actually specify these Join-Points and link them to the advice? This is done via point-cuts. e.g

@Before("execution(* *.*AccountOperation(..))")
public void log() {
// do logging
// log to console
// write to audit log file
// send an email to admin
}

The strange notation @Before("execution(* *.*AccountOperation(..))") that you see is what is used to pick out the point at which the log() method should execute. In this case, it would be executed when any method ending with AccountOperation with any number of arguments in any package that returns any value is called. This is the point cut.

In this example, out point cut uses the execution designation which matches joint points that are methods executions. Spring AOP supports some other designation types:

args() - Matches the execution of methods whose arguments are instances of the given types.
@args() - Limits join point matches to the execution of methods where the runtime type of the actual arguments passed have annotations of the given type(s).
execution() - This we already saw. It matches join points that are method executions.
this() - limits matching to join points where the bean reference (Spring AOP proxy) is an instance of the given type.
target()- Limits join point matches to those where the target object is of a given type.
@target()- Limits matching to join points where the class of the executing object has an annotation of the given type.
within()- Limits matching to join points within certain types
@within()- Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP)
@annotation- Matches limited to those where the subject of the join point has the given annotation

As you can see, you have various ways you can define your point-cuts. the powerful thing is also the fact that you can combine these designators using && or || to make your point-cuts as granular as possible. For example

@Before("execution(* *.*AccountOperation(..))") && @annotation(org.mybankingapp.annotations.Loggable)

Weaving
Weaving is the process of applying aspects to a target object to create a new proxied Object. There are various ways this application can be done which would determine when the advice applied would be executed and when the weaving itself would be done.

Talking about when the advice would be executed, looking at the point cut expression above, you notice the @Before annotation? This annotation specifies that the logic in the advice be executed before the target method is executed. But @Before is not the only annotation you have. There are a couple:

@Before: The advice functionality takes place before the advised method is invoked.

@After: The advice functionality takes place after the advised method completes, regardless of the outcome.

@AfterReturning: The advice functionality takes place after the advised method successfully completes.

@AfterThrowing: The advice functionality takes place after the advised method throws an exception.

@Around: The advice wraps the advised method, providing some functionality before and after the advised method is invoked.

And regarding when exactly this weaving is done, it could be at compile time, class load time, or at runtime. Spring AOP implementation does its weaving at runtime where by it generates a proxy using the target specified by the point-cut and intercept calls to it using the advice specified.

The fact that Spring AOP is runtime and proxy driven results into certain limitations. One, you can only do AOP on method invocation and not property modification. And also you can only apply advice to public methods as private methods can not be proxied.

We can visualize how all these works

@Before


@Before("execution(* *.*(..))") //pointcut
public void adviceLogic() {
//advice logic goes in here
}




@After


@After("execution(* *.*(..))") //pointcut
public void adviceLogic() {
//advice logic goes in here
}




@AfterReturning


@AfterReturning("execution(* *.*(..))", returning="returnType") //pointcut
public void adviceLogic() {
//advice logic goes in here
}



@AfterThrowing

@AfterReturning("execution(* *.*(..))", throwing="exceptionType") //pointcut
public void adviceLogic(JointPoint jp, Exception e) {
//advice logic goes in here
}



@Around


@AfterReturning("execution(* *.*(..))", returning="returnType") //pointcut
public Object adviceLogic(ProceedingJointPoint pjp) {
//advice logic goes in here
// if ok to continue then call proceed on proceedingJointPoint
// return the necessary value
}




A more concrete example of these work flow can be illustrated with how the @Around can be used for caching.

You could have certain method calls that are expensive. And instead of having to perform the expensive operations each time, you want to cache the result and only make the call if only the cache has expired.

Without AOP you can implement such thus:

public ExpensiveObj getExpensiveObject(Key key) {
ExpensiveObj value = cacheStore.get(cacheKey(key));

if (value == null) {
value = runVeryExpensiveAlgo();
cacheStore.put(cacheKey(key), value); 
} 

return value;
}

And you would have to have this logic in every method call you want cached. But with AOP using the @Around designator you can have the method call thus:

public ExpensiveObj getExpensiveObject(Key key) {
ExpensiveObj value = runVeryExpensiveAlgo();
return value;
}

As you can see the getExpensiveObject() is clean. It is mainly concerned with what it should do. Which is to run the very expensive Algorithm that returns the needed object.

With this you can then extract the caching logic out thus:

@Around("execution(ExpensiveObj *.*.*()"))
public ExpensiveObj cache(ProceedingJointPoint joinPoint) {

List<Object> args = Arrays.asList(joinPoint.getArgs());
ExpensiveObj value = cacheStore.get(cacheKey(args.get(0)));

if (value == null) {
value = runVeryExpensiveAlgo();
cacheStore.put(cacheKey(key), value); 
} 

return value;
}

Again as this post is not about the nitty gritty of using AOP in spring, it is about the general overview and how to configure. To get into more details, what you would want to look for are articles on How to define Point cuts and apply AOP in spring

Aspect
You might have encounter the word Aspect thrown about the place. What exactly is it? Simple. The aspect is the combination of the point-cut and the advice. Combined in such a way that they can perform the idea behind Aspect Oriented Programming.

Depending on how you configure AOP in spring, the Aspect might come to be via the use of the <aop:aspect/> tag which whose configuration would contain both the point-cut definition and a reference to the advice. Or it could be achieved via annotations; which is the method I prefer and which is what I would quickly show next how to configure.

Configuring Spring AOP


I prefer the annotation driven configuration for Spring AOP which makes use of the <aop:aspectj-autoproxy/> namespace. With this approach you basically put the logic for you advice in a class, annotate the class with @Aspect and wire it up as a bean like you would do normally. And then use <aop:aspectj-autoproxy/> tag to indicate to Spring that the classes annotated with @Aspect should be converted to Aspects.

So using this method, the configuration to apply the log() advice when either editAccountOperation() or debitAccountOperation() is called would look thus:

package bankingApp;

@Aspect 
public Logger {

public Logger(){}


@After("execution(* bankingApp.*.*AccountOperation(..))");
public log() {
// do logging
// log to console
// write to audit log file
// send an email to admin
}
}

And then in your Spring Application context configuration you have:

 <aop:aspectj-autoproxy>
        <aop:include name="loggingAspect" />
  </aop:aspectj-autoproxy>


  <bean id="loggingAspect" class="bakingApp.Logger" />

And you can have more than one <aop:include /> tag within <aop:aspect-autoproxy/> thereby registering as many Aspect as needed.

A look at the Aspect Oriented Progragramming with Spring section in the official documentation won't be a bad idea for a more grounded understanding of how Spring AOP works.

No comments: