Hanging bomb, facing the Ali interviewer, he answered the introduction and use of Spring AOP in this way

Introduction and use of Spring AOP

AOP: Aspect Oriented Programming Aspect Oriented Programming

OOP: Object Oriented Programming object-oriented programming

Aspect-oriented programming: Based on the new programming ideas based on OOP, the main object of OOP is class, while the main object of AOP is aspect, which plays a very important role in processing logs, security management, transaction management, etc. 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 that dynamically cuts a certain piece of code into a specified location of a specified method to run during the running of the program.

1. The concept of AOP

Why introduce AOP?

Calculator.java

package com.courage.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.courage.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, and it is the basic javase code implementation. What should I do if I need to add a log function at this time? I can only add log output in each method, and it will become very troublesome if it needs to be modified.

MyCalculator.java

package com.courage.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 log processing and turning it into a tool class for implementation:

LogUtil.java

package com.courage.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.courage.inter;

import com.courage.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, and we want to dynamically obtain the name and parameters of the method during the running of the program , results and other relevant information can be implemented by using a dynamic agent .

CalculatorProxy.java

package com.courage.proxy;

import com.courage.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 require adding additional code to the business method, which is the commonly used method. If you want to pursue perfect students, you can also use the above-mentioned log tools to improve.

LogUtil.java

package com.courage.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.courage.proxy;

import com.courage.inter.Calculator;
import com.courage.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 it 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 to be proxied does not implement any interface, then it is impossible Create a proxy object for him, which is also a fatal flaw. In Spring, we don't need to write such complex codes, we only need to use AOP to easily realize the above functions. Of course, the underlying implementation of Spring AOP also relies on dynamic proxies.

AOP's core concepts and terminology

  • Aspect: Refers to the modularization of concerns, 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 a generic class-based schema-based approach or using @Aspect annotations (@AspectJ annotations) in ordinary classes.
  • Join point: A specific point in the execution of a program, such as when a method is called or when an exception is handled. In Spring AOP, a join point always represents a method execution.
  • Advice: An action performed on a specific join point of an aspect. There are several types of notifications, including "around", "before" and "after" to name a few. The types of notifications are discussed in later chapters. Many AOP frameworks, including Spring, use interceptors as notification models and maintain a chain of interceptors centered around join points.
  • Pointcut: An assertion that matches a join point. Advice is associated with a pointcut expression and operates on join points that satisfy this pointcut (for example, when a method of a particular name is executed). How pointcut expressions match join points is at the heart of AOP: Spring uses AspectJ pointcut semantics by default.
  • Introduction: Declare additional methods or fields of a type. Spring allows introducing new interfaces (and a corresponding implementation) to any advised object. For example, you can use imports to make beans implement the IsModified interface in order to simplify caching mechanisms (in the AspectJ community, imports are also known as internal type declarations (inter)).
  • Target object: The object to be advised by one or more aspects. Also known as the advised object. Since Spring AOP is implemented through a runtime proxy, this object is always a proxied object.
  • AOP proxy (AOP proxy): The object created by the AOP framework is used to implement the aspect contract (including functions such as notification method execution). In Spring, AOP proxies can be JDK dynamic proxies or CGLIB proxies.
  • Weaving: The process of connecting aspects to other application types or objects and creating an object to be advised. This process can be done at compile time (eg using the AspectJ compiler), classload time or runtime. Spring, like other pure Java AOP frameworks, is weaved at runtime.

AOP notification types

  • Before advice: Advice that runs before a join point but cannot prevent the flow of execution from entering the join point (unless it raises an exception).
  • After returning advice: Advice that is executed after the join point completes normally (for example, when the method returns normally without throwing any exceptions).
  • After throwing advice: Advice executed when a method exits with an exception thrown.
  • After (finally) advice: Advice to be executed when the join point exits (either normally or abnormally).
  • Around Advice (Around Advice): Advice around a join point, such as a method call. This is the most powerful type of notification. Surround advice can accomplish custom behavior before and after a method call. It can choose whether to continue the execution of the join point or directly return a custom return value or throw an exception to end the execution.

Application scenarios of AOP

  • log management
  • Authentication
  • Security check
  • transaction control

2. Simple configuration of Spring AOP

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

1. Add pom dependencies

        <!-- 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 classes
    • Add @Component annotation to LogUtil
    • Add @Service annotation to MyCalculator
    • Add auto scan configuration
    • <!--Don't forget to add the context namespace--> <context:component-scan base-package="com.courage"></context:component-scan>
  • Setting up the aspect class in the program
    • Add @Aspect annotation in LogUtil.java
  • Set when and where to execute the method in the aspect class
  • package com.courage.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 { /* Set when the following method runs @Before: run before the target method: pre-notification @After: run after the target method: post-notification @AfterReturning: after the target method returns normally: return notification @AfterThrowing: in Start running after the target method throws an exception: Exception notification @Around: Surround: Surround notification After writing the annotation, which methods need to be set to execute, use the expression execution (full name of the access modifier return value type method) */ @Before ("execution( public int com.courage.inter.MyCalculator.*(int,int))") public static void start(){ // System.out.println("The XXX method starts to execute, and the parameters used are:" + Arrays.asList(objects)); // System.out.println(method.getName()+"The method starts to execute, the parameter is: "+ Arrays.asList(objects)); System.out.println("The method starts Execution, the parameter is: "); } @AfterReturning("execution( public int com.courage.inter.MyCalculator.*(int,int))") public static void stop(){ // System.out.println("XXX method execution ends, the result is: "+ Arrays.asList(objects)); // System.out.println(method.getName()+" The method execution is completed, the result is: "+ Arrays.asList(objects)); System.out.println("The method execution is completed, the result is: "); } @AfterThrowing("execution( public int com.courage.inter.MyCalculator .*(int,int))") public static void logException(){ // System.out.println(method.getName()+"method exception: "+ e.getMessage()); System.out.println ("The method has an exception: "); } @After("execution( public int com.courage.inter.MyCalculator.*(int,int))") public static void end(){ // System.out.println( method.getName()+"The method execution is over..."); System.out.println("The method execution is over..."); } }+ Arrays.asList(objects)); System.out.println("Method execution completed, the result is: "); } @AfterThrowing("execution( public int com.courage.inter.MyCalculator.*(int,int)) ") public static void logException(){ // System.out.println(method.getName()+"A method has an exception: "+ e.getMessage()); System.out.println("A method has an exception:") ; } @After("execution( public int com.courage.inter.MyCalculator.*(int,int))") public static void end(){ // System.out.println(method.getName()+" method Execution ended..."); System.out.println("Method execution ended..."); } }+ Arrays.asList(objects)); System.out.println("Method execution completed, the result is: "); } @AfterThrowing("execution( public int com.courage.inter.MyCalculator.*(int,int)) ") public static void logException(){ // System.out.println(method.getName()+"A method has an exception: "+ e.getMessage()); System.out.println("A method has an exception:") ; } @After("execution( public int com.courage.inter.MyCalculator.*(int,int))") public static void end(){ // System.out.println(method.getName()+" method Execution ended..."); System.out.println("Method execution ended..."); } }Method exception: "); } @After("execution( public int com.courage.inter.MyCalculator.*(int,int))") public static void end(){ // System.out.println(method. getName()+"The method execution is over..."); System.out.println("The method execution is over..."); } }Method exception: "); } @After("execution( public int com.courage.inter.MyCalculator.*(int,int))") public static void end(){ // System.out.println(method. getName()+"The method execution is over..."); System.out.println("The method execution is over..."); } }
  • Enable 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.courage"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>

3. Test

MyTest.java

import com.courage.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. 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.courage.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());
    }
}

​ Proxy objects can be created through cglib. At this time, there is no need to implement any interface. The proxy object is

class com.courage.inter.MyCalculator

EnhancerBySpringCGLIBEnhancerBySpringCGLIB

1f93b605 type

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

Notice:

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.courage.inter.My*alculator.*(int,int))

2. Match any parameter,

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

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

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

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

​ ..:

1. Match multiple parameters, any type of parameter

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

2. Match any multi-layer path

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

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

​ The most lazy way: execution(* *(..)) or execution(* *.*(..))

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

​ In addition, &&, ||, ! The way

​ &&: two expressions at the same time

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

​ ||: any expression can be satisfied

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

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

​ &&: two expressions at the same time

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

2. The execution order of the notification method

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

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

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

3. Get the detailed information of 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. It is actually very simple to obtain it, just add the JoinPoint parameter.

LogUtil.java

package com.courage.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.courage.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.courage.inter.MyCalculator.*(int,int))")
    public static void stop(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行完成,结果是:");

    }

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

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

​ Just got the information of the method, 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.courage.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 get exception information in the same way

LogUtil.java

    @AfterThrowing(value = "execution( public int com.courage.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 does not have very high requirements for the notification method. You can change the return value of the method and the access modifier of the method arbitrarily, but the only thing that cannot be modified is the parameter of the method. There will be an error of parameter binding because the notification method is Spring uses reflection to call, and each method call must determine the value of the parameter of this method.

LogUtil.java

    @After("execution( public int com.courage.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 the expressions of multiple methods are consistent in actual use, then you can consider extracting the pointcut expression:

​ a. Randomly create an empty method that returns void without implementation

​ b. Mark the @Potintcut annotation on the method

package com.courage.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.courage.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. The use of surrounding notifications

LogUtil.java

package com.courage.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.courage.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 surrounding notifications is better than ordinary notifications. The specific execution order is as follows:

Surround preposition --> normal preposition --> target method execution --> surround normal end/exception --> surround post --> normal post --> normal return or exception.

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

7. The sequence of running multiple aspects

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

LogUtil.java

package com.courage.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.courage.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.courage.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.courage.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.courage.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.courage.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.courage.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 is to execute according to the dictionary order of the aspect name, but if you want to change the specific execution order yourself, you can use the @Order annotation to solve it. The smaller the value, the higher the priority.

LogUtil.java

package com.courage.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.courage.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.courage.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.courage.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.courage.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.courage.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.courage.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 will be the specific order of execution?

​ Because the surround notification is introduced at the aspect layer when it is added, so which aspect to add the surround notification on, it will be executed on that aspect.

3. Configuration-based AOP 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 the current enterprise-level development, you can’t do it, so you need to configure it simply , Annotation configuration is fast and simple, and the configuration method is perfect.

1. Delete all annotations

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.courage"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <bean id="logUtil" class="com.courage.util.LogUtil2"></bean>
    <bean id="securityAspect" class="com.courage.util.SecurityAspect"></bean>
    <bean id="myCalculator" class="com.courage.inter.MyCalculator"></bean>
    <aop:config>
        <aop:pointcut id="globalPoint" expression="execution(public int com.courage.inter.MyCalculator.*(int,int))"/>
        <aop:aspect ref="logUtil">
            <aop:pointcut id="mypoint" expression="execution(public int com.courage.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>

Learn more JAVA knowledge and skills, follow and private message bloggers (learning) to learn and receive JAVA courseware for free,

Source code, installation package, and the latest interview questions from major manufacturers, etc.

Guess you like

Origin blog.csdn.net/m0_67788957/article/details/123774202