4. Introduction and use of Spring+AOP

04Introduction and use of Spring AOP

AOP: Aspect Oriented Programming

OOP: Object Oriented Programming

​ Aspect-oriented programming: Based on the new programming ideas on the basis of OOP, the main object of OOP is the class, and the main object of AOP is the aspect, which plays a very important role in processing logs, security management, and transaction management. AOP is an important core point in Spring. Although the IOC container does not rely on AOP, AOP provides very powerful functions to supplement IOC. In layman's terms, it is a programming method in which a certain piece of code is dynamically cut into the specified position of the specified method to run during the program running .

1. The concept of AOP

Why introduce AOP?

Calculator.java

package com.mashibing.inter;

public interface Calculator {

    public int add(int i,int j);

    public int sub(int i,int j);

    public int mult(int i,int j);

    public int div(int i,int j);
}

MyCalculator.java

package com.mashibing.inter;

public class MyCalculator implements Calculator {
    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    public int mult(int i, int j) {
        int result = i * j;
        return result;
    }

    public int div(int i, int j) {
        int result = i / j;
        return result;
    }
}

MyTest.java

public class MyTest {
    public static void main(String[] args) throws SQLException {
        MyCalculator myCalculator = new MyCalculator();
        System.out.println(myCalculator.add(1, 2));
    }
}

​ This code is very simple, it is the basic javase code implementation. At this time, if you need to add the log function, what should you do? You can only add log output in each method, and it will become very troublesome if you need to modify it.

MyCalculator.java

package com.mashibing.inter;

public class MyCalculator implements Calculator {
    public int add(int i, int j) {
        System.out.println("add 方法开始执行,参数为:"+i+","+j);
        int result = i + j;
        System.out.println("add 方法开始完成结果为:"+result);
        return result;
    }

    public int sub(int i, int j) {
        System.out.println("sub 方法开始执行,参数为:"+i+","+j);
        int result = i - j;
        System.out.println("add 方法开始完成结果为:"+result);
        return result;
    }

    public int mult(int i, int j) {
        System.out.println("mult 方法开始执行,参数为:"+i+","+j);
        int result = i * j;
        System.out.println("add 方法开始完成结果为:"+result);
        return result;
    }

    public int div(int i, int j) {
        System.out.println("div 方法开始执行,参数为:"+i+","+j);
        int result = i / j;
        System.out.println("add 方法开始完成结果为:"+result);
        return result;
    }
}

​ You can consider abstracting the log processing and turning it into a tool class for implementation:

LogUtil.java

package com.mashibing.util;

import java.util.Arrays;

public class LogUtil {

    public static void start(Object ... objects){
        System.out.println("XXX方法开始执行,使用的参数是:"+ Arrays.asList(objects));
    }

    public static void stop(Object ... objects){
        System.out.println("XXX方法执行结束,结果是:"+ Arrays.asList(objects));
    }
}

MyCalculator.java

package com.mashibing.inter;

import com.mashibing.util.LogUtil;

public class MyCalculator implements Calculator {
    public int add(int i, int j) {
        LogUtil.start(i,j);
        int result = i + j;
        LogUtil.stop(result);
        return result;
    }

    public int sub(int i, int j) {
        LogUtil.start(i,j);
        int result = i - j;
        LogUtil.stop(result);
        return result;
    }

    public int mult(int i, int j) {
        LogUtil.start(i,j);
        int result = i * j;
        LogUtil.stop(result);
        return result;
    }

    public int div(int i, int j) {
        LogUtil.start(i,j);
        int result = i / j;
        LogUtil.stop(result);
        return result;
    }
}

​ After abstracting in the above way, the code is indeed much simpler, but you should have found that the output information does not contain the specific method name. We want to dynamically obtain the name and parameters of the method during the running of the program. Related information such as, results, etc., can be achieved by using dynamic agents at this time .

CalculatorProxy.java

package com.mashibing.proxy;

import com.mashibing.inter.Calculator;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * 帮助Calculator生成代理对象的类
 */
public class CalculatorProxy {

    /**
     *
     *  为传入的参数对象创建一个动态代理对象
     * @param calculator 被代理对象
     * @return
     */
    public static Calculator getProxy(final Calculator calculator){


        //被代理对象的类加载器
        ClassLoader loader = calculator.getClass().getClassLoader();
        //被代理对象的接口
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        //方法执行器,执行被代理对象的目标方法
        InvocationHandler h = new InvocationHandler() {
            /**
             *  执行目标方法
             * @param proxy 代理对象,给jdk使用,任何时候都不要操作此对象
             * @param method 当前将要执行的目标对象的方法
             * @param args 这个方法调用时外界传入的参数值
             * @return
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //利用反射执行目标方法,目标方法执行后的返回值
//                System.out.println("这是动态代理执行的方法");
                Object result = null;
                try {
                    System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(args));
                    result = method.invoke(calculator, args);
                    System.out.println(method.getName()+"方法执行完成,结果是:"+ result);
                } catch (Exception e) {
                    System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
                } finally {
                    System.out.println(method.getName()+"方法执行结束了......");
                }
                //将结果返回回去
                return result;
            }
        };
        Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
        return (Calculator) proxy;
    }
}

​ We can see that this method is more flexible, and does not need to add additional code in the business method, this is the common way. If you want to pursue perfect students, you can also use the above log tools to improve.

LogUtil.java

package com.mashibing.util;

import java.lang.reflect.Method;
import java.util.Arrays;

public class LogUtil {

    public static void start(Method method, Object ... objects){
//        System.out.println("XXX方法开始执行,使用的参数是:"+ Arrays.asList(objects));
        System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));
    }

    public static void stop(Method method,Object ... objects){
//        System.out.println("XXX方法执行结束,结果是:"+ Arrays.asList(objects));
        System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));

    }

    public static void logException(Method method,Exception e){
        System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
    }
    
    public static void end(Method method){
        System.out.println(method.getName()+"方法执行结束了......");
    }
}

CalculatorProxy.java

package com.mashibing.proxy;

import com.mashibing.inter.Calculator;
import com.mashibing.util.LogUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * 帮助Calculator生成代理对象的类
 */
public class CalculatorProxy {

    /**
     *
     *  为传入的参数对象创建一个动态代理对象
     * @param calculator 被代理对象
     * @return
     */
    public static Calculator getProxy(final Calculator calculator){


        //被代理对象的类加载器
        ClassLoader loader = calculator.getClass().getClassLoader();
        //被代理对象的接口
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        //方法执行器,执行被代理对象的目标方法
        InvocationHandler h = new InvocationHandler() {
            /**
             *  执行目标方法
             * @param proxy 代理对象,给jdk使用,任何时候都不要操作此对象
             * @param method 当前将要执行的目标对象的方法
             * @param args 这个方法调用时外界传入的参数值
             * @return
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //利用反射执行目标方法,目标方法执行后的返回值
//                System.out.println("这是动态代理执行的方法");
                Object result = null;
                try {
                    LogUtil.start(method,args);
                    result = method.invoke(calculator, args);
                    LogUtil.stop(method,args);
                } catch (Exception e) {
                    LogUtil.logException(method,e);
                } finally {
                    LogUtil.end(method);
                }
                //将结果返回回去
                return result;
            }
        };
        Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
        return (Calculator) proxy;
    }
}

​ Many students may feel that the above code is perfect after seeing the above code, but it should be noted that this dynamic proxy implementation calls the basic implementation of jdk. If the target object that needs proxy does not implement any interface, then it is impossible This is also a fatal flaw in creating a proxy object for him. In Spring, we don't need to write the above-mentioned complicated code, but only need to use AOP to easily realize the above-mentioned functions. Of course, the underlying implementation of Spring AOP also relies on dynamic proxy.

The core concepts and terminology of AOP

  • Aspect: Refers to the modularity of the focus, which may cross-cut multiple objects. Transaction management is an example of cross-cutting concerns in enterprise Java applications. In Spring AOP, aspects can be implemented using the schema-based approach of generic classes or by @Aspectannotations (@AspectJ annotations) in ordinary classes .
  • Join point: A specific point in the execution of a program, such as the point in time when a method is called or the point in time when an exception is handled. In Spring AOP, a connection point always represents the execution of a method.
  • Advice: An action performed at a specific connection point of the aspect. There are many types of notifications, including "around", "before" and "after" and so on. The types of notifications will be discussed in later chapters. Many AOP frameworks, including Spring, use interceptors as notification models and maintain a chain of interceptors centered on connection points.
  • Pointcut: The assertion that matches the connection point. The advice is associated with the pointcut expression and runs on the connection point that satisfies the pointcut (for example, when a method with a specific name is executed). How the pointcut expression matches the connection point is the core of AOP: Spring uses AspectJ pointcut semantics by default.
  • Introduction: Declare additional methods or fields of a certain type. Spring allows the introduction of a new interface (and a corresponding implementation) to any notified object. For example, you can use imports to make beans implement  IsModifiedinterfaces in order to simplify the caching mechanism (in the AspectJ community, introductions are also called internal type declarations (inter)).
  • Target object: The object notified by one or more aspects. Also called an advised object. Since Spring AOP is implemented through runtime proxy, this object is always a proxied object.
  • AOP proxy: An object created by the AOP framework to implement aspect contracts (including notification method execution and other functions). In Spring, the AOP proxy can be a JDK dynamic proxy or a CGLIB proxy.
  • Weaving: The process of connecting aspects to other application types or objects and creating a notified object. This process can be done at compile time (for example, using the AspectJ compiler), class load time, or runtime. Spring, like other pure Java AOP frameworks, is woven at runtime.

AOP notification types

  • Before advice: A notification that runs before the connection point but cannot prevent the execution flow from entering the connection point (unless it raises an exception).
  • After returning advice: The advice that is executed after the connection point is completed normally (for example, when the method does not throw any exceptions and returns normally).
  • After throwing advice: advice executed when the method exits by throwing an exception.
  • After (finally) advice: The advice to be executed when the connection point exits (regardless of whether it is a normal return or an abnormal exit).
  • Around Advice: Notifications around connection points, such as method calls. This is the most powerful type of notification. Surround notifications can complete custom behaviors before and after the method is called. It can choose whether to continue executing the connection point or directly return a custom return value or throw an exception to end the execution.

Application scenarios of AOP

  • Log management
  • Authority authentication
  • Security check
  • Transaction control

2. Simple configuration of Spring AOP

​ In the above code, we implement the log function by means of dynamic proxy, but it is more troublesome. Now we will use the function of spring aop to achieve this requirement. In fact, in layman's terms, it is to replace the LogUtil tool class with another one. Method to realize.

1. Add pom dependency

        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

2. Write configuration

  • Add the target class and aspect class to the IOC container, and add component annotations to the corresponding class

    • Add @Component annotation to LogUtil

    • Add @Service annotation to MyCalculator

    • Add automatic scan configuration

      <!--别忘了添加context命名空间-->
      <context:component-scan base-package="com.mashibing"></context:component-scan>
      
  • Set the aspect class in the program

    • Add @Aspect annotation in LogUtil.java
  • Set when and where the method in the aspect class is executed

    package com.mashibing.util;
    
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    @Component
    @Aspect
    public class LogUtil {
    
        /*
        设置下面方法在什么时候运行
            @Before:在目标方法之前运行:前置通知
            @After:在目标方法之后运行:后置通知
            @AfterReturning:在目标方法正常返回之后:返回通知
            @AfterThrowing:在目标方法抛出异常后开始运行:异常通知
            @Around:环绕:环绕通知
    
            当编写完注解之后还需要设置在哪些方法上执行,使用表达式
            execution(访问修饰符  返回值类型 方法全称)
         */
        @Before("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
        public static void start(){
    //        System.out.println("XXX方法开始执行,使用的参数是:"+ Arrays.asList(objects));
    //        System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));
            System.out.println("方法开始执行,参数是:");
        }
    
        @AfterReturning("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
        public static void stop(){
    //        System.out.println("XXX方法执行结束,结果是:"+ Arrays.asList(objects));
    //        System.out.println(method.getName()+"方法执行结束,结果是:"+ Arrays.asList(objects));
            System.out.println("方法执行完成,结果是:");
    
        }
    
        @AfterThrowing("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
        public static void logException(){
    //        System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
            System.out.println("方法出现异常:");
        }
    
        @After("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
        public static void end(){
    //        System.out.println(method.getName()+"方法执行结束了......");
            System.out.println("方法执行结束了......");
        }
    }
    
  • Turn on the function of annotation-based aop

    <?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/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd
    ">
        <context:component-scan base-package="com.mashibing"></context:component-scan>
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    

3. Test

MyTest.java

import com.mashibing.inter.Calculator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
        Calculator bean = context.getBean(Calculator.class);
        bean.add(1,1);
    }
}

​ The dynamic proxy method of spring AOP is the method that comes with jdk, and the component stored in the container is the proxy object com.sun.proxy.$Proxy object

4. Create proxy objects through cglib

MyCalculator.java

package com.mashibing.inter;

import org.springframework.stereotype.Service;

@Service
public class MyCalculator {
    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    public int mult(int i, int j) {
        int result = i * j;
        return result;
    }

    public int div(int i, int j) {
        int result = i / j;
        return result;
    }
}

MyTest.java

public class MyTest {
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
        MyCalculator bean = context.getBean(MyCalculator.class);
        bean.add(1,1);
        System.out.println(bean);
        System.out.println(bean.getClass());
    }
}

​ The proxy object can be created by cglib, and there is no need to implement any interface at this time. The proxy object is

class com.mashibing.inter.MyCalculator$$EnhancerBySpringCGLIB$$1f93b605类型

To sum up: In the spring container, if there is an interface, it will use that comes with jdk dynamic proxy, if there is no interface, then use cglib dynamic proxy. The implementation principle of dynamic proxy will be discussed in detail later.

note:

1. Entry point expression

​ When using expressions, in addition to the previous writing, you can also use wildcards:

​ *:

​ 1. Match one or more characters

​ execution( public int com.mashibing.inter.My*alculator.*(int,int))

​ 2. Match any parameter,

​ execution( public int com.mashibing.inter.MyCalculator.*(int,*))

​ 3. Only one level of path can be matched. If there are multiple directories under the project path, then * can only match one level of path

​ 4. You cannot use * in the permission location. If you want to express all permissions, just leave it alone

​ execution( * com.mashibing.inter.MyCalculator.*(int,*))

​ ..:

​ 1. Match multiple parameters, any type of parameter

​ execution( * com.mashibing.inter.MyCalculator.*(..))

​ 2.Match any multi-layer path

​ execution( * com.mashibing..MyCalculator.*(..))

​ When writing expressions, there can be more than N ways to write, but there is one of the laziest and most precise ways:

​ The laziest way: execution(* *(..)) or execution(* *.*(..))

​ The most accurate way: execution( public int com.mashibing.inter.MyCalculator.add(int,int))

​ In addition, the expression also supports &&, ||,! The way

​ &&: Two expressions at the same time

​ execution( public int com.mashibing.inter.MyCalculator.*(..)) && execution(* *.*(int,int) )

​ ||: Any expression can be satisfied

​ execution( public int com.mashibing.inter.MyCalculator.*(..)) && execution(* *.*(int,int) )

​! : As long as it is not in this position, it can be cut in

​ &&: Two expressions at the same time

​ execution( public int com.mashibing.inter.MyCalculator.*(..))

2. The execution order of the notification method

​ In the previous code, everyone has always had questions about the execution order of the notification. In fact, the result of the execution is not wrong. You need to pay attention to:

​ 1. Normal execution: @Before--->@After--->@AfterReturning

​ 2. Abnormal execution: @Before--->@After--->@AfterThrowing

3. Obtain detailed information about the method

​ In the above case, we did not obtain the detailed information of the Method, such as the method name, parameter list and other information. If you want to obtain it, it is actually very simple, just add the JoinPoint parameter.

LogUtil.java

package com.mashibing.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
public class LogUtil {

    @Before("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public static void start(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public static void stop(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行完成,结果是:");

    }

    @AfterThrowing("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public static void logException(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法出现异常:");
    }

    @After("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public static void end(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行结束了......");
    }
}

​ Just get the method information, but if you need to get the result, you need to add another method parameter, and tell spring which parameter to use to receive the result

LogUtil.java

    @AfterReturning(value = "execution( public int com.mashibing.inter.MyCalculator.*(int,int))",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行完成,结果是:"+result);

    }

​ You can also obtain abnormal information in the same way

LogUtil.java

    @AfterThrowing(value = "execution( public int com.mashibing.inter.MyCalculator.*(int,int))",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法出现异常:"+exception);
    }

4. Spring's requirements for passing methods

​ Spring's requirements for notification methods are not very high. You can change the return value of the method and the access modifier of the method at will, but the only thing that cannot be modified is the parameter of the method. Parameter binding errors will occur because the notification method is Spring uses reflection to call, and each method call has to determine the value of the method's parameters.

LogUtil.java

    @After("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    private int end(JoinPoint joinPoint,String aa){
//        System.out.println(method.getName()+"方法执行结束了......");
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行结束了......");
        return 0;
    }

5. Extraction of expressions

If in actual use, the expressions of multiple methods are consistent, then you can consider extracting the entry point expression:

​ a, casually live an unimplemented empty method that returns void

​ b. Annotate the method with @Potintcut

package com.mashibing.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
public class LogUtil {
    
    @Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public void myPoint(){}
    
    @Before("myPoint()")
    public static void start(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行完成,结果是:"+result);

    }

    @AfterThrowing(value = "myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法出现异常:"+exception.getMessage());
    }

    @After("myPoint()")
    private int end(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行结束了......");
        return 0;
    }
}

6. Use of surround notifications

LogUtil.java

package com.mashibing.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
public class LogUtil {
    @Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public void myPoint(){}
    
    /**
     * 环绕通知是spring中功能最强大的通知
     * @param proceedingJoinPoint
     * @return
     */
    @Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        String name = proceedingJoinPoint.getSignature().getName();
        Object proceed = null;
        try {
            System.out.println("环绕前置通知:"+name+"方法开始,参数是"+Arrays.asList(args));
            //利用反射调用目标方法,就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知:"+name+"方法返回,返回值是"+proceed);
        } catch (Throwable e) {
            System.out.println("环绕异常通知"+name+"方法出现异常,异常信息是:"+e);
        }finally {
            System.out.println("环绕后置通知"+name+"方法结束");
        }
        return proceed;
    }
}

​ Summary: The execution order of surround notifications is better than that of ordinary notifications. The specific execution order is as follows:

Surround front-->Normal front-->Target method execution-->Surround normally ends/abnormal-->Surround back-->Normal back-->Normal return or abnormal.

However, it should be noted that if an exception occurs, the surrounding notification will process or catch the exception, and the ordinary exception notification cannot be received, so the best way is to throw an exception in the surrounding exception notification.

7. Sequence of multi-faceted operation

​ If there are multiple aspects to be executed, what is the order?

LogUtil.java

package com.mashibing.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
public class LogUtil {
    @Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public void myPoint(){}
    @Before("myPoint()")
    public static void start(JoinPoint joinPoint){
//        System.out.println("XXX方法开始执行,使用的参数是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
//        System.out.println("XXX方法执行结束,结果是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"方法执行结束,结果是:"+ Arrays.asList(objects));
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法执行完成,结果是:"+result);

    }

    @AfterThrowing(value = "myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
//        System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法出现异常:"+exception.getMessage());
    }

    @After("myPoint()")
    private int end(JoinPoint joinPoint){
//        System.out.println(method.getName()+"方法执行结束了......");
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法执行结束了......");
        return 0;
    }

    /**
     * 环绕通知是spring中功能最强大的通知
     * @param proceedingJoinPoint
     * @return
     */
    //@Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        String name = proceedingJoinPoint.getSignature().getName();
        Object proceed = null;
        try {
            System.out.println("环绕前置通知:"+name+"方法开始,参数是"+Arrays.asList(args));
            //利用反射调用目标方法,就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知:"+name+"方法返回,返回值是"+proceed);
        } catch (Throwable e) {
            System.out.println("环绕异常通知"+name+"方法出现异常,异常信息是:"+e);
        }finally {
            System.out.println("环绕后置通知"+name+"方法结束");
        }
        return proceed;
    }
}

SecurityAspect.java

package com.mashibing.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
public class SecurityAspect {

    @Before("com.mashibing.util.LogUtil.myPoint()")
    public static void start(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "com.mashibing.util.LogUtil.myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法执行完成,结果是:"+result);

    }

    @AfterThrowing(value = "com.mashibing.util.LogUtil.myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法出现异常:"+exception.getMessage());
    }

    @After("com.mashibing.util.LogUtil.myPoint()")
    private int end(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法执行结束了......");
        return 0;
    }

    /**
     * 环绕通知是spring中功能最强大的通知
     * @param proceedingJoinPoint
     * @return
     */
    //@Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        String name = proceedingJoinPoint.getSignature().getName();
        Object proceed = null;
        try {
            System.out.println("环绕前置通知:"+name+"方法开始,参数是"+Arrays.asList(args));
            //利用反射调用目标方法,就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知:"+name+"方法返回,返回值是"+proceed);
        } catch (Throwable e) {
            System.out.println("环绕异常通知"+name+"方法出现异常,异常信息是:"+e);
        }finally {
            System.out.println("环绕后置通知"+name+"方法结束");
        }
        return proceed;
    }
}

​ In spring, the default execution is in the dictionary order of the aspect names, but if you want to change the specific execution order yourself, you can use the @Order annotation to solve the problem. The smaller the value, the higher the priority.

LogUtil.java

package com.mashibing.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
@Order(2)
public class LogUtil {
    @Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public void myPoint(){}
    @Before("myPoint()")
    public static void start(JoinPoint joinPoint){
//        System.out.println("XXX方法开始执行,使用的参数是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
//        System.out.println("XXX方法执行结束,结果是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"方法执行结束,结果是:"+ Arrays.asList(objects));
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法执行完成,结果是:"+result);

    }

    @AfterThrowing(value = "myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
//        System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法出现异常:"+exception.getMessage());
    }

    @After("myPoint()")
    private int end(JoinPoint joinPoint){
//        System.out.println(method.getName()+"方法执行结束了......");
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法执行结束了......");
        return 0;
    }

    /**
     * 环绕通知是spring中功能最强大的通知
     * @param proceedingJoinPoint
     * @return
     */
    //@Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        String name = proceedingJoinPoint.getSignature().getName();
        Object proceed = null;
        try {
            System.out.println("环绕前置通知:"+name+"方法开始,参数是"+Arrays.asList(args));
            //利用反射调用目标方法,就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知:"+name+"方法返回,返回值是"+proceed);
        } catch (Throwable e) {
            System.out.println("环绕异常通知"+name+"方法出现异常,异常信息是:"+e);
        }finally {
            System.out.println("环绕后置通知"+name+"方法结束");
        }
        return proceed;
    }
}

SecurityAspect.java

package com.mashibing.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
@Order(1)
public class SecurityAspect {

    @Before("com.mashibing.util.LogUtil.myPoint()")
    public static void start(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "com.mashibing.util.LogUtil.myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法执行完成,结果是:"+result);

    }

    @AfterThrowing(value = "com.mashibing.util.LogUtil.myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法出现异常:"+exception.getMessage());
    }

    @After("com.mashibing.util.LogUtil.myPoint()")
    private int end(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法执行结束了......");
        return 0;
    }

    /**
     * 环绕通知是spring中功能最强大的通知
     * @param proceedingJoinPoint
     * @return
     */
    //@Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        String name = proceedingJoinPoint.getSignature().getName();
        Object proceed = null;
        try {
            System.out.println("环绕前置通知:"+name+"方法开始,参数是"+Arrays.asList(args));
            //利用反射调用目标方法,就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知:"+name+"方法返回,返回值是"+proceed);
        } catch (Throwable e) {
            System.out.println("环绕异常通知"+name+"方法出现异常,异常信息是:"+e);
        }finally {
            System.out.println("环绕后置通知"+name+"方法结束");
        }
        return proceed;
    }
}

​ If you need to add surround notifications, what order will the specific execution order be?

​ Because the surround notification is introduced at the aspect level when it is added, so in which aspect the surround notification is added, then it will be executed in which aspect.

3. AOP configuration based on configuration

​ Before we explained the annotation-based AOP configuration method, let's start to talk about the xml-based configuration method. Although there are many ways to use annotations in current enterprise-level development, you can’t do it, so you need to configure it simply , The annotation configuration is quick and simple, and the configuration method is complete.

1. Delete all comments

2. Add configuration file

aop.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/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd
">

    <context:component-scan base-package="com.mashibing"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <bean id="logUtil" class="com.mashibing.util.LogUtil2"></bean>
    <bean id="securityAspect" class="com.mashibing.util.SecurityAspect"></bean>
    <bean id="myCalculator" class="com.mashibing.inter.MyCalculator"></bean>
    <aop:config>
        <aop:pointcut id="globalPoint" expression="execution(public int com.mashibing.inter.MyCalculator.*(int,int))"/>
        <aop:aspect ref="logUtil">
            <aop:pointcut id="mypoint" expression="execution(public int com.mashibing.inter.MyCalculator.*(int,int))"/>
            <aop:before method="start" pointcut-ref="mypoint"></aop:before>
            <aop:after method="end" pointcut-ref="mypoint"></aop:after>
            <aop:after-returning method="stop" pointcut-ref="mypoint" returning="result"></aop:after-returning>
            <aop:after-throwing method="logException" pointcut-ref="mypoint" throwing="exception"></aop:after-throwing>
            <aop:around method="myAround" pointcut-ref="mypoint"></aop:around>
        </aop:aspect>
        <aop:aspect ref="securityAspect">
            <aop:before method="start" pointcut-ref="globalPoint"></aop:before>
            <aop:after method="end" pointcut-ref="globalPoint"></aop:after>
            <aop:after-returning method="stop" pointcut-ref="globalPoint" returning="result"></aop:after-returning>
            <aop:after-throwing method="logException" pointcut-ref="globalPoint" throwing="exception"></aop:after-throwing>
            <aop:around method="myAround" pointcut-ref="mypoint"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

Guess you like

Origin blog.csdn.net/zw764987243/article/details/111186772