AOP/Aspect-J Overview and Config

aop-image-1
(1) Overview

AOP (Aspect Oriented Programming) is one of key features in Spring framework beside (DI).
In AspectJ, which is more flexible and powerful compared to Spring AOP, There are following advices are available: (Source Code)


(2) Configure

<annotation-driven />
<context:component-scan base-package="com.ns.spring" />

<!-- AOP -->
<aop:aspectj-autoproxy>
    <aop:include name="aopBefore" />
    <aop:include name="aopAfter" />
    <aop:include name="aopAround" />
</aop:aspectj-autoproxy>

<!-- "id": Unique ID used by programs to identify the class. Unlike "name"  -->
<!--       it cannot be more than one reference from the same Java object   -->
<!-- "prototype": New instance each time when called                        -->
<!-- "singleton": Single instance per Spring IoC container                  -->
<beans:bean id="aopBefore"  class="com.ns.spring.aop.advise.AspectBefore" scope="prototype"/>
<beans:bean id="aopAfter"   class="com.ns.spring.aop.advise.AspectAfter"  scope="prototype"/>
<beans:bean id="aopAround"  class="com.ns.spring.aop.advise.AspectAround" scope="prototype"/>

   <properties>
.
      <org.aspectj-version>1.7.4</org.aspectj-version>
   </properties>
   <dependencies>
.
.
.
      <!-- AspectJ -->
      <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjrt</artifactId>
         <version>${org.aspectj-version}</version>
         <scope>runtime</scope>
      </dependency>
      <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjtools</artifactId>
         <version>${aspectj.version}</version>
      </dependency>
      <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjweaver</artifactId>
         <version>1.8.5</version>
      </dependency>
      <dependency>
         <groupId>cglib</groupId>
         <artifactId>cglib</artifactId>
         <version>2.2.2</version>
      </dependency>
   </dependencies>
</project>

Around Advise

aop-around-advice-header-img1

(1) Overview & (2) Configure
(3) Example to define Point Cut

In APO, Around Advice is the most powerful, and (believed to be) commonly used. It can be BEFORE or AFTER or even BOTH at the same time. To implements Around Advise, AspectJ provides @Around annotation. In order to use it, you need to define a something called “Point Cut“.

In the line 24-25:
aop-around-pointcut-arg1
3.1. This point cut is defined as Any method whose parameter is String

In the line 36-37:
aop-around-pointcut-within1
3.2. This point cut is defined as Any class under the package: com.ns.spring.dao.template

In the line 48-49:
aop-around-pointcut-exec1
3.3. This point cut is defined as Any method starts with “get” under this package: com.ns.spring.dao.templatepl class

In the line 54-55:
aop-around-pointcut-exec2
3.4. This point cut is defined as Any method in GenHbDaoImpl class


(4) Example to use Point Cut as Around Advice

In the line 57:
aop-around-advice-or-img1
4.1. The @Around advice executed if either condition of Point cut is met

In the line 62:
aop-around-advice-and-img1
4.2. The @Around advice executed if both conditions of Point cut are met


(5) What happen when executed

This example calls method “aroundAdvice” and:

  • Any process BEFORE line 73 (line 68 to 71), runs before the method call
  • Any process AFTER line 73 (line 77 to 80), runs after the method call
package com.ns.spring.aop.advise;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Aspect
@Component
public class AspectAround {

	private static final Logger log = LoggerFactory.getLogger(AspectAround.class);

	/** ----------------------------------------------------------- */
	/** Any method whose parameter is String                        */
	/** ----------------------------------------------------------- */
	@Pointcut("args(java.lang.String)")
	public void pointCutStringId(){}
	
	/** ----------------------------------------------------------- */
	/** Any method whose parameter is RtrnTpModel                   */
	/** ----------------------------------------------------------- */
	@Pointcut("args(com.ns.spring.model.ui.RtrnTpModel)")
	public void pointCutRtrnTpParam(){}

	/** ----------------------------------------------------------- */
	/** Any class under this package                                */
	/** ----------------------------------------------------------- */
	@Pointcut("within(com.ns.spring.dao.template.*)")
	public void pointCutWithinDaoTemplate(){}

	/** ----------------------------------------------------------- */
	/** Any method in RmaController class                           */
	/** ----------------------------------------------------------- */
	@Pointcut("execution(* com.ns.spring.RmaController.*(..))")
	public void pointCutAllRmaController(){}

	/** ----------------------------------------------------------- */
	/** Any method starts with "get" under this package             */
	/** ----------------------------------------------------------- */
	@Pointcut("execution(* com.ns.spring.dao.template.*.get*(..))")
	public void pointCutDaoTemplate(){}

	/** ----------------------------------------------------------- */
	/** Any method in GenHbDaoImpl class                            */
	/** ----------------------------------------------------------- */
	@Pointcut("execution(* com.ns.spring.dao.GenHbDaoImpl.*(..))")
	public void pointCutGenDao(){}

	@Around("pointCutGenDao() || pointCutDaoTemplate()")
	public Object aroundDao(ProceedingJoinPoint jpt) throws Throwable {
		return aroundAdvice(jpt, "aroundDao");
	}

	@Around("pointCutStringId() && pointCutWithinDaoTemplate()")
	public Object aroundDaoTemplWStringParam(ProceedingJoinPoint jpt) throws Throwable {
		return aroundAdvice(jpt, "aroundDaoTemplWStringParam");
	}
	
	private Object aroundAdvice(ProceedingJoinPoint jpt, String methodName) {
		StopWatch sw = new StopWatch();
		sw.start();
		log.info("-------------------------(At "+methodName+") BEFORE-------------------------");		
		Object obj = null;
		try {
			obj = jpt.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		log.info("-------------------------(At "+methodName+") AFTER-------------------------");
		sw.stop();

		setLogMsg(jpt, sw);
		return obj;
	}
	
	private void setLogMsg(ProceedingJoinPoint jpt, StopWatch sw) {

		StringBuffer sb = new StringBuffer();
		sb.append("\n At " +getCurrTime());
		sb.append("\n Class: ");
		sb.append(jpt.getTarget().getClass().getName());
		sb.append("\n Method: ");
		sb.append(jpt.getSignature().getName());
		Object[] arr = jpt.getArgs();
		if (arr != null && arr.length > 0) {
			sb.append("\n Param(s): ");
			for (Object element : arr) {
				if (element != null) {
					sb.append(element).append(",");
				}
			}
			// Delete the last comma
			sb.deleteCharAt(sb.length() - 1);
		}
		sb.append("\n ================= Time consumed: ");
		sb.append(sw.getTotalTimeMillis());
		sb.append(" Milli Sec =================");
		log.info(sb.toString());
	}
	
	private String getCurrTime() {
		SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.S");
		return sdf.format(new Date());
	}
}

After Advise

aop-after-advice-header-img1

(1) Overview & (2) Configure
(3) Example

In APO, some process called AFTER the specified point (Point cut). There are three AFTER cases:

  • Just After something (always)
  • Only after error (Exception)
  • Only after no error

The typical example is a LOGGING. This example also does logging. The logging method called AFTER the point cut (After Advise).

To implements After Advise, AspectJ provides @After, @AfterThrowing and @AfterReturning annotation:

In the line 18:
The first example will be execute AFTER “Exception” occurs in GenHbDaoImpl class:
aop-after-error-example2
3.1. The first asterisk specified ANY return type
3.2. The second asterisk specified ANY method (name) in this class
3.3. The two period “..” specified ANY number and type of method parameter

In the line 25: The second example, the only different from the first example is that it will be execute after “NO Exception” case:
aop-after-no-error-example2
3.4.@AfterReturning” means after NO ERROR

In the line 32: The third example is executed always “AFTER” something.
aop-after-example2
3.5. In this case, ANY method in “RmaController” class starts with “backTo

package com.ns.spring.aop.advise;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AspectAfter {

	private static final Logger log = LoggerFactory.getLogger(AspectAfter.class);

	@AfterThrowing(pointcut = "execution(* com.ns.spring.dao.GenHbDaoImpl.*(..))", throwing = "error")
	public Object afterException(JoinPoint joinPoint, Throwable error) {
		log.info("(At afterException) Method name: " + joinPoint.getSignature().getName());
		log.info("(At afterException) Exception : " + error);
		return error;
	}

	@AfterReturning(pointcut = "execution(* com.ns.spring.dao.GenHbDaoImpl.*(..))", returning = "result")
	public Object afterNormalEnd(JoinPoint joinPoint, Object result) {
		log.info("(At afterNormalEnd) Method name: " + joinPoint.getSignature().getName());
		log.info("(At afterNormalEnd) Result: " + result);
		return result;
	}
	
	@After("execution(* com.ns.spring.RmaController.backTo*(..))")
	public void beforeAllRmaCtrlGoTo(JoinPoint pt){
		log.info("\n++++++++++++++++++++ Controller (Back To screen Transition) ++++++++++++++++++++ " +pt.getSignature().getName() + " called!");
		setLogMsg(pt);
	}
	
	private void setLogMsg(JoinPoint jpt) {

		StringBuffer sb = new StringBuffer();
		sb.append("\n Class: ");
		sb.append(jpt.getTarget().getClass().getName());
		sb.append("\n Method: ");
		sb.append(jpt.getSignature().getName());
		Object[] arr = jpt.getArgs();
		if (arr != null && arr.length > 0) {
			sb.append("\n Param(s): ");
			for (Object element : arr) {
				if (element != null) {
					sb.append(element).append(",");
				}
			}
			// Delete the last comma
			sb.deleteCharAt(sb.length() - 1);
		}
		log.info(sb.toString());
	}
}

Before Advise

aop-before-advice-header-img1

(1) Overview & (2) Configure
(3) Example
In APO, some process called BEFORE specified point (Point cut). The typical example is a LOGGING. This example also does logging. The logging method called BEFORE the point cut (Before Advise).

To implements Before Advise, AspectJ provides @Before annotation:
aop-before-exp-return-method3.1-3.3
In the line 16:
3.1. The first asterisk specified ANY return type
3.2. The second asterisk specified ANY method (name) in this class
3.3. The two period “..” specified ANY number and type of method parameter
aop-before-exp-return-method3.4
In the line 23:
3.4. The pointed asterisk specified ANY method name starts from “goTo
aop-before-exp-return-method3.5
In the line 30:
3.5. The method name is specified as “initList

package com.ns.spring.aop.advise;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AspectBefore {

	private static final Logger log = LoggerFactory.getLogger(AspectBefore.class);

	@Before("execution(* com.ns.spring.RmaController.*(..))")
	public void beforeAllRmaCtrl(JoinPoint pt){
		log.info("\n:::::::::::::::: Controller :::::::::::::::: " +pt.getSignature().getName() + " called!");
		Object[] arr = pt.getArgs();
		logMsg(arr);
	}
	
	@Before("execution(* com.ns.spring.RmaController.goTo*(..))")
	public void beforeAllRmaCtrlGoTo(JoinPoint pt){
		log.info("\n................ Controller (Screen Transition) ................ " +pt.getSignature().getName() + " called!");
		Object[] arr = pt.getArgs();
		logMsg(arr);
	}
	
	@Before("execution(* com.ns.spring.RmaController.initList(..))")
	public void beforeRmaCtrl_initList(JoinPoint pt){
		log.info("\n***************** Controller (Specified Method) ***************** " +pt.getSignature().getName() + " called!");
		Object[] arr = pt.getArgs();
		logMsg(arr);
	}
	
	private void logMsg(Object[] arr) {
		if( arr != null && arr.length > 0 ) {
			for( Object obj: arr) {
				log.info("Parameter value -> "+obj.toString());
			}
		}
	}
}