AspectJ usage example

Posted by bibby on Wed, 09 Oct 2019 01:11:21 +0200

1. Enabling AspectJ annotation support in Spring

  • 1 To use AspectJ annotations in Spring applications, you must include AspectJ class libraries under classpath: aopalliance.jar, aspectj.weaver.jar, and spring-aspects.jar
    maven Introduces

    <dependency>
    <groupId>org.springframework</groupId>  
    <artifactId>spring-aop</artifactId>  
    <version>4.1.9.RELEASE</version>  
    </dependency>
    
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.1.9.RELEASE</version>
    </dependency>
    
    <dependency>
    <groupId>org.aspectj</groupId>  
    <artifactId>aspectjrt</artifactId>  
    <version>1.6.8</version>  
    </dependency>  
    
    <dependency>  
    <groupId>org.aspectj</groupId>  
    <artifactId>aspectjweaver</artifactId>  
    <version>1.6.8</version>  
    <scope>runtime</scope>  
    </dependency>    

     

  • 2 Add aop Schema to the root element.
  • 3 To enable AspectJ annotation support in the Spring IOC container, simply define an empty XML element in the Bean configuration file

    <! -- Configuration automatically generates proxy objects for Java classes that match the aspectJ annotations - > < aop: aspectj-autoproxy > </aop: aspectj-autoproxy >  
  • 4 When the Spring IOC container detects elements in the Bean configuration file, it automatically creates proxies for beans that match AspectJ facets.

AspectJ supports five types of notification annotations:

  • @Before Pre-notification, executed before method execution
  • @ After: Post-notification, executed after method execution
  • @ AfterRunning: Returns the notification and executes after the method returns the result
  • @ AfterThrowing: Exception notification, after the method throws an exception
  • @Around : around notification, around method execution

2.1. Use the previous calculator interface and implementation classes Arithmetic Calculator. java, Arithmetic Calculator Impl. Java


@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator{} 

2.2. Adding Scan Annotation and aspectj Support to xml

<?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
        
    <!-- Configuring Packages for Automatic Scanning -->
    <context:component-scan base-package="com.hp.spring.aop.annotation"></context:component-scan>
    
    <!-- Configuration automatically matches aspectJ Annotated Java Class generates proxy objects -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>  

2.3. Write facet classes and define notifications

@Aspect //Annotation Definition Aspect
@Component
public class LoggingAspect {
    //Before advice
    @Before("execution(public int com.hp.spring.aop.annatation.ArithmeticCalculator.*(int, int))")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();
        
        System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
    }
    //Before advice
    @After("execution(* com.hp.spring.aop.annatation.*.*(..))")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends");
    }
    
}

Because @before The same expression as @after So spring supports extracting expressions into a method. The extracted code is as follows:


package com.hp.spring.aop.annotation;
import java.util.Arrays;
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.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
 * You can use the @Order annotation to specify the priority of the section, the smaller the value, the higher the priority.
 */
@Order(2) 
@Aspect
@Component
public class LoggingAspect {
    
    /**
     * Define a method for declaring pointcut expressions. Generally, there is no need to add additional code to this method. 
     * Use @Pointcut to declare pointcut expressions. 
     * Other subsequent notifications use method names directly to refer to the current pointcut expression. 
     */
    @Pointcut("execution(public int com.hp.spring.aop.annotation.ArithmeticCalculator.*(..))")
    public void declareJointPointExpression(){}
    
    /**
     * Execute a piece of code before each method of each implementation class of the com.hp.spring.aop.annotation.ArithmeticCalculator interface starts
     */
    @Before("declareJointPointExpression()")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();
        
        System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
    }
    
    /**
     * Code executed after method execution. Whether or not the method has an exception
     */
    @After("declareJointPointExpression()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends");
    }
    
    /**
     * The method normally ends the executed code
     * The return notification can access the return value of the method!
     */
    @AfterReturning(value="declareJointPointExpression()",
            returning="result")
    public void afterReturning(JoinPoint joinPoint, Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends with " + result);
    }
    
    /**
     * Code that executes when an exception occurs to the target method.
     * Exception objects can be accessed; and notification code can be specified to execute when a particular exception occurs
     */
    @AfterThrowing(value="declareJointPointExpression()",
            throwing="e")
    public void afterThrowing(JoinPoint joinPoint, Exception e){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " occurs excetion:" + e);
    }
    
    /**
     * Circumferential notifications need to carry parameters of ProceedingJoinPoint type. 
     * Circumferential notification is similar to the whole process of dynamic proxy: ProceedingJoinPoint type parameters can determine whether or not the target method is executed.
     * And the circular notification must have a return value, which is the return value of the target method.
     */
    /*
    @Around("execution(public int com.hp.spring.aop.annotation.ArithmeticCalculator.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjd){
        
        Object result = null;
        String methodName = pjd.getSignature().getName();
        
        try {
            //Before advice
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
            //Target implementation approach
            result = pjd.proceed();
            //Return notification
            System.out.println("The method " + methodName + " ends with " + result);
        } catch (Throwable e) {
            //Exception notification
            System.out.println("The method " + methodName + " occurs exception:" + e);
            throw new RuntimeException(e);
        }
        //Post notification
        System.out.println("The method " + methodName + " ends");
        
        return result;
    }
    */
}
  • Section two

package com.hp.spring.aop.annotation;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(1)
@Aspect
@Component
public class VlidationAspect {
    @Before("com.hp.spring.aop.annotation.LoggingAspect.declareJointPointExpression()")
    public void validateArgs(JoinPoint joinPoint){
        System.out.println("-->validate:" + Arrays.asList(joinPoint.getArgs()));
    }
    
}
  • Test class
package com.hp.spring.aop.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
    
    public static void main(String[] args) {
        
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-aspectj.xml");
        ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
        
        System.out.println(arithmeticCalculator.getClass().getName());
        
        int result = arithmeticCalculator.add(1, 2);
        System.out.println("result:" + result);
        
        result = arithmeticCalculator.div(1000, 10);
        System.out.println("result:" + result);
    }
    
}
  1. package com.hp.spring.aop.annotation;

  2. import org.springframework.context.ApplicationContext;

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

  4. public class Main {

  5.  
  6. public static void main(String[] args) {

  7.  
  8. ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-aspectj.xml");

  9. ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");

  10.  
  11. System.out.println(arithmeticCalculator.getClass().getName());

  12.  
  13. int result = arithmeticCalculator.add(1, 2);

  14. System.out.println("result:" + result);

  15.  
  16. result = arithmeticCalculator.div(1000, 10);

  17. System.out.println("result:" + result);

  18. }

  19.  
  20. }

Print out:
com.sun.proxy.$Proxy12
-->validate:[1, 2]
The method add begins with [1, 2]
The method add ends
The method add ends with 3
result:3
-->validate:[1000, 10]
The method div begins with [1000, 10]
The method div ends
The method div ends with 100
result:100

As you can see from the printed logs, like dynamic proxies, you can define various types of notifications.

Topics: Programming Spring Java xml calculator