Rick has posted 25 posts at DZone. You can read more from them at their website. View Full User Profile

An Introduction to Spring AOP

04.07.2009
| 79384 views |
  • submit to reddit

Let's actually look at the code for this example.

For this example, we will use a simplified SecurityToken that gets stored into a ThreadLocal variable, 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 = new ThreadLocal<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) throws Throwable {
               
            /* 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 checkSecurity method 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 to checkSecurity. 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). If joinPoint.proceed() is not called, then the wrapped method of the advised object (withdraw or deposit) 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, and aop:around tags as follows:.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="
                http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
                http://www.springframework.org/schema/aop     http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
                ">

       
        <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, the SecurityAdvice 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)

 
Published at DZone with permission of its author, Rick Hightower.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Michal Rembiszewski replied on Thu, 2009/04/16 - 3:12pm

I understand you wanted to give a simple example so the AOP concept is easier to grasp. As a side comment I often find myself trying to decide between using an aspect or a static decorator. While AOP seems more versatile at a first glance, it makes your code much more annoying to debug, both interactively and by making stacktraces look ugly. Therefore I usually use it only if the new functionality is required in more than one class (otherwise it isn't really a cross-cutting concern). In case presented in the example I would go with old-style static decorator which is easy enough to configure using spring.

Walter Bogaardt replied on Tue, 2009/05/26 - 11:17am

Simple to understand article. There is always a decision that has to be made in choosing AOP over other designs in your applications. Architecture can be pragmatic or dogmatic as I've seen so much of as technologies evolve and new terms are slapped. I found it useful for creating container independent implementation of persistable jmx beans, transactional support is another area. Yet in all cases it sometimes makes debugging problems a pain, but once you have a solid aspect usually life is good.

Jeroen Rosenberg replied on Fri, 2010/05/07 - 3:16am

Nice article! Recently I've uploaded a presentation on Spring AOP . Might be interesting for you.

Krishnendu Sardar replied on Wed, 2012/09/05 - 5:02am

Hi Can you please provide link to download the whole project?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.