Search

An Introduction to Spring AOP

This article discusses Spring AOP in a tutorial format. It covers some of the newer features of Spring AOP like annotations, improved XML configuration and more. It builds on the DIIntroTutorial. It is meant as a simple introduction to AOP. Enough information so you –can use AOP on a project.
Spring x AOP 
For some, AOP seems like voodoo magic. For others, AOP seems like a cureall. For now, let's just say that AOP is a tool that you want in your developer tool box. It can make seemingly impossible things easy.
The first time that I used AOP was with Spring's transaction management support. I did not realize I was using AOP. I just knew Spring could apply EJB-style declarative transaction management to POJOs. It was probably –three to six months before I realized that I was using was Spring's AOP support. The Spring framework truly brought AOP out of the esoteric closet into the main stream light of day.
You can think of AOP as a way to apply services (called cross-cutting concerns) to objects. AOP encompasses more than this, but this is where it gets used mostly in the main stream.
I've using AOP to apply caching services, transaction management, resource management, etc. to any number of objects in an application. It is not a panacea, but it certainly fits a lot of otherwise difficult use cases.
You can think of AOP as a dynamic decorator design pattern. The decorator pattern allows additional behavior to be added to an existing class by wrapping the original class and duplicating its interface and then delegating to the original. See this articdecorator pattern Decorator Pattern for more detail about the decorator design pattern.
Sample Application Revisited
or this introduction to AOP, let's take a simple example. Let's apply security services to our Automated Teller Machine example from the first DI example from the first tutorial in this series.
Let's say when a user logs into a system that a SecurityToken is created that carries the user's credentials, and before methods on objects get invoked, we want to check to see if the user has credentials to invoke these methods.
In a web application, you could write a ServletFilter,that stored this SecurityToken in HttpSession and then on every request retrieved the token from Session and put it into a ThreadLocal variable where it could be accessed from a SecurityService that you could implement.
Perhaps the objects that needed the SecurityService could access it as follows:
public void deposit(BigDecimal bd) {
/* If the user is not logged in, don't let them use this method */
if(!securityManager.isLoggedIn()){
throw new SecurityViolationException();
}
/* Only proceed if the current user is allowed. */

if (!securityManager.isAllowed("AutomatedTellerMachine", operationName)){
throw new SecurityViolationException();
}
...

transport.communicateWithBank(...);
}
In our ATM example, the above might work out well, but imagine a system with thousands of classes that needed security. Now imagine, the way we check to see if a user is logged is changed. If we put this code into every method that needed security, then we could possibly have to change this a thousand times if we changed the way we checked to see if a user was logged in.
What we want to do instead is to use Spring to create a decorated version of the –ATM? bean. The decorated version would add the additional behavior to the atm object without changing the actual implementation of the atm.
Spring does this by creating what is called an AOP proxy. An AOP proxy is like a dynamic decorator. Underneath the covers Spring can generate a class at runtime (the AOP proxy) that has the same interface as our AutomatedTellerMachine. The AOP proxy wraps our existing atm object and provides additional behavior by delegating to a list of method interceptors. The method interceptors provide the additional behavior and are similar to ServletFilters but for methods instead of requests.
Thus, before we added Spring AOP, our atm example was like Figure

After we added AOP support, we now get an AOP proxy that applies the securityAdvice to the atm as show in figure



You can see that the AOP proxy implements the AutomatedTellerMachine interface. When the client object looks up the atm and starts invoking methods instead of executing the methods directly, it executes the method on the proxy, which then delegates the call to a series of method interceptors called “Advice,” which eventually invoke the actual atm instance (now called atmTarget).
Let's actually look at the code for this example.
For this example, we will use a simplified SecurityToken that gets stored into a ThreadLocalvariable, but one could imagine one that was populated with data from a database or an LDAP server or some other source of authentication and authorization.
Here is the SecurityToken, which gets stored into a ThreadLocal variable, for this example:
package com.arcmind.springquickstart.security;

/**
* @author Richard Hightower
*
*/
public class SecurityToken {

private boolean allowed;
private String userName;

public SecurityToken() {

}



public SecurityToken(boolean allowed, String userName) {
super();
this.allowed = allowed;
this.userName = userName;
}



public boolean isAllowed(String object, String methodName) {
return allowed;
}


/**
* @return Returns the allowed.
*/
public boolean isAllowed() {
return allowed;
}
/**
* @param allowed The allowed to set.
*/
public void setAllowed(boolean allowed) {
this.allowed = allowed;
}
/**
* @return Returns the userName.
*/
public String getUserName() {
return userName;
}
/**
* @param userName The userName to set.
*/
public void setUserName(String userName) {
this.userName = userName;
}
}
The SecurityService stores the SecurityToken into the ThreadLocal variable and then delegates to it to see if the current user has access to perform the current operation on the current object as follows:
package com.arcmind.springquickstart.security;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;


/**
* @author Richard Hightower
*
*/
@Service ("defaultSecurityService")
@Qualifier("manager")
public class SecurityService {

private static ThreadLocal<SecurityToken> currentToken = newThreadLocal<SecurityToken>();

public static void placeSecurityToken(SecurityToken token){
currentToken.set(token);
}

public void clearSecuirtyToken(){
currentToken.set(null);
}

public boolean isLoggedIn(){
SecurityToken token = currentToken.get();
return token!=null;
}

public boolean isAllowed(String object, String method){
SecurityToken token = currentToken.get();
return token.isAllowed();
}

public String getCurrentUserName(){
SecurityToken token = currentToken.get();
if (token!=null){
return token.getUserName();
}else {
return "Unknown";
}
}

}
package com.arcmind.springquickstart.security;

/**
* @author Richard Hightower
*
*/
public class SecurityViolationException extends RuntimeException {

/**
*
*/
private static final long serialVersionUID = 1L;

}
To remove the security code out of the AutomatedTellerMachineImpl class and any other class that needs security, we will write an Aspect to intercept calls and perform security checks before the method call. To do this we will create a method interceptor (known is AOP speak as an advice) and intercept method calls on the atm object.
Here is the SecurityAdvice class which will intercept calls on the AutomatedTellerMachineImpl class.
package com.arcmind.springquickstart.security;



import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

/**
* @author Richard Hightower
*/
@Component
public class SecurityAdvice {

@Autowired(required=true)
@Qualifier ("manager")
private SecurityService securityManager;

public void checkSecurity(ProceedingJoinPoint joinPoint) throwsThrowable {

/* If the user is not logged in, don't let them use this method */
if(!securityManager.isLoggedIn()){            
throw new SecurityViolationException();
}

/* Get the name of the method being invoked. */
String operationName = joinPoint.getSignature().getName();
/* Get the name of the object being invoked. */
String objectName = joinPoint.getThis().getClass().getName();


/*
* Invoke the method or next Interceptor in the list,
* if the current user is allowed.
*/
if (!securityManager.isAllowed(objectName, operationName)){
throw new SecurityViolationException();
}

joinPoint.proceed();
}


/**
* @return Returns the manager.
*/
public SecurityService getSecurityManager() {
return securityManager;
}
/**
* @param manager The manager to set.
*/
public void setSecurityManager(SecurityService manager) {
this.securityManager = manager;
}

}
The checkSecurity method of SecurityAdvice is the method that implements the advice. You can think of advice as the decoration that we want to apply to other objects. The objects getting the decoration are called advised objects.
Notice that the SecurityService gets injected into the SecurityAdvice and the checkSecuritymethod uses the SecurityService to see if the user is logged in and the user has the rights to execute the method.
An instance of ProceedingJoinPoint, namely joinPoint, is passed as an argument tocheckSecurity. The ProceedingJoinPoint has information about the method that is being called and provides control that determines if the method on the advised object's methods gets invoked (AutomatedTellerMachineImpl.withdraw and AutomatedTellerMachineImpl.deposit). IfjoinPoint.proceed() is not called, then the wrapped method of the advised object (withdraw ordeposit) is not called. (The proceed method causes the actual decorated method to be invoked or the next interceptor in the chain.)
To apply an Advice like SecurityAdvice to an advised object, you need a pointcut. A pointcut is like a filter that picks the objects and methods that get decorated. For this example, we will configure the pointcut into the Spring application context with the aop:config, aop:aspect, aop:pointcut, andaop:around tags as follows:.
<?xml version="0" encoding="UTF-8"?>
xsi:schemaLocation="
">


<context:component-scan base-package="com.arcmind.springquickstart"/>

<aop:config>
<aop:aspect id="securityAspect" ref="securityAdvice">
<aop:pointcut id="atmLayer"  expression="bean(atm)"/>
<aop:around  pointcut-ref="atmLayer" method="checkSecurity"/>
</aop:aspect>
</aop:config>

</beans>
Because we have component scan turned on with the context:component-scan tag, theSecurityAdvice get installed in the Spring application context under the default bean name SecurityAdvice. (The default bean name is the simple name of the class). The atm bean is registered using the component scan as well as follows:
@Service ("atm")
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine{

@Autowired (required=true)
@Qualifier ("default")
private ATMTransport transport;
 The aop:pointcut tag defines the pointcut rule using this AspectJ expression bean(atm), which means decorate the methods of the bean named atm. If you want to decorate all beans whose names ends in Service, you would use
bean(*Service)
To see a full description of the AspectJ pointcut language see pointcuts. Note that the bean name pointcut can take you far.
You apply this pointcut and advice around the atm methods using the aop:around tag by referring to the atmLayer. You defined with the aop:pointcut. Then when the atm is looked up with AtmMain (listed below), instead of getting the original atm, you get an AOP proxy, which is like a dynamic decorator.
Figure 3 shows graphically how the objects and advice are found and applied.

Let's complete our example by reviewing the AtmMain main method that looks up the atm out of the Spring applicatoinContext.
Let's review AtmMain as follows:
package com.arcmind.springquickstart.atm;

import java.math.BigDecimal;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.arcmind.springquickstart.security.SecurityService;
import com.arcmind.springquickstart.security.SecurityToken;

public class AtmMain {

public static void simulateLogin() {
SecurityService.placeSecurityToken(new SecurityToken(true,"Rick Hightower"));
}

public static void simulateNoAccess() {
SecurityService.placeSecurityToken(new SecurityToken(false,"Tricky Lowtower"));
}      

public static void main(String[] args) throws Exception {

simulateLogin();

ApplicationContext appContext = newClassPathXmlApplicationContext(
"classpath:./spring/applicationContext.xml");

AutomatedTellerMachine atm = (AutomatedTellerMachine) appContext
.getBean("atm");

atm.withdraw(new BigDecimal("00"));

atm.deposit(new BigDecimal("1000"));

System.out.println("done");

}

}
Before we added AOP support when we looked up the atm, we looked up the object directly as shown in figure  Now that we applied AOP, when we look up the object, we get what is in figure  When we look up the atm in the application context, we get the AOP proxy that applies the decoration (advice, method interceptor) to the atm target by wrapping the target and delegating to it after it invokes the series of method interceptors.
Conclusion
AOP is neither a cureall or voodoo magic but a powerful tool that needs to be in your bag of tricks. The Spring framework has brought AOP to the main stream masses, and Spring 5 has simplified using AOP.
You can use Spring AOP to apply services (called cross-cutting concerns) to objects. AOP need not seem like a foreign concept, as it is merely a more flexible version of the decorator design pattern. With AOP you can add additional behavior to an existing class without writing a lot of wrapper code. This can be a real time saver when you have a use case where you need to apply a crosscutting concern to a slew of classes.
Download Spring examples source code

No comments:

Post a Comment