AOP learning of Spring

Now let's learn aop of spring

AOP concept

Keyword Meaning
aspect Cut, focus (what I can do).
PointCut Cut point, define cut rules (who can I do things for)
Joinpoint Connection point, specific method of agent needed (specific thing of agent needed)
Target Object Represented object (target object)
Advice Notification of callbacks (what behavior to weave in at what time)

Let's think about what scenarios aop is to solve. Because aop is to solve a kind of problem, for this kind of problem, we can understand that aop is for some kinds (for example, for those who are wandering in search of cheap rental houses, or for those who are still interested in their studies and want to get graduate students)

Agent mode of AOP

Name annotation Use opportunity
Before advice @Before Call the weaving method before the PointCut method is executed
Post notification @After The weaving method is called after the PointCut method is executed.
Around Advice @Around Execute Before @ Before, execute Before @ After
Return notification @AfterReturning The target method is called before returning (the return parameter is not void).
Exception notification @AfterThrowing Called when the target method throws an exception

AOP execution order

  1. @Around
  2. @Before
  3. @Around
  4. @After
  5. @AfterReturning
  6. @AfterThrowing

pointCut expression matching rule

Rule name Rule description case
execution Matching method signature needs to meet the method signature described in execution @Pointcut("execution(public * com.spring.aop.xiaozhe.controller..(...))")
target The type of matching target object (non proxy instance) meets the type described in target. The expression type is fully qualified. Wildcards are not supported @Before(value = "target(com.spring.aop.xiaozhe.UserService)")
@target Match target object has annotation given in @ target description, expression type is fully qualified name, wildcard is not supported @Before(value = "@target(com.spring.aop.xiaozhe.TestTargetAnnotation)")
within All non private methods that match a package or type to a class of the package or type described in within @Before(value = "within(com.spring.aop.xiaozh.*)")
@within The matching object type has the annotation given in the @ within description, which has the same function as @ target @Difference between within and @ target @ within: if the current class has annotation, it will have effect on the method overloaded by the class to the parent class and its own method, and other methods will have no effect. If the subclass has no annotation, and the subclass calls the method of the class (that is, the subclass does not overload the method), it has effect. @Target: if the current class has annotation, it is valid for the methods inherited, owned and overloaded by the class. No effect if subclass has no annotation
this Match the type of the target object to meet the type described in this (can be parent class, interface) @Before(value = "this(com.spring.aop.xiaozhe.Service)")
args The parameter full parameter type of the matching method is the type described in args, and it is also used to receive the parameter of the target method @Before(value = "args(java.lang.String,java.lang.Integer)")
@args The actual type of the match method parameter contains the annotation given in the @ args description @Before(value = "@args(com.spring.aop.xiaozhe.ArgstAnnotation)")
@annotation The matching method has the annotation given in the @ annotation description The name of the bean or the name or Id in the bean description
bean The name of the bean or the name or Id in the bean description @Before(value = "bean(userService)")

Optional parameters for each agent method

JoinPoint

Pointcut information. You can get some parameters of method runtime through JoinPoint, such as: target object, method parameter value of target object, proxy object, etc. the details of the method are as follows
//Get the method information of the agent
	//For example: String org.springframework.aop.UserService.getUser(String,String)
	String toString();

   //Get the method information of the agent
	//For example: execution(public java.lang.String //org.springframework.aop.UserService.getUser(java.lang.String,java.lang.String))
    String toShortString();

   //Get the method information of the agent
   //For example: execution(UserService.getUser(..))
    String toLongString();

  	//Get AOP proxy object
    Object getThis();

   	//Get proxied object
    Object getTarget();

  	//Get the parameter value of the proxied method
    Object[] getArgs();

 	//Signature encapsulates the method information of the proxy object
	//getDeclaringType gets the class name of the proxy object: class org.springframework.aop.UserService
	//getName get method name: for example: getUser
    //Get the method name, parameter name, Class of the Class and other information of the agent
    Signature getSignature();

 
    SourceLocation getSourceLocation();

   
    String getKind();

ProceedingJoinPoint

JoinPoint sub interface, in addition to having the same function as JoinPoint, also provides a method that can call the target class with the proceed method. ProceedingJoinPoint can only be used in surround notification. 

If you want to use this parameter in other notification methods, you will be prompted with the following error:

java.lang.IllegalArgumentException: ProceedingJoinPoint is only supported for around advice

Test code

Test control code

package com.spring.aop.xiaozhe.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;

/**
 * @ClassName AopController
 * @Description AOP Test class
 * @Author ZhengZhe
 * @Date 2020/1/14 14:41
 * @Version 1.0
 **/
@Api(tags = "AopController", description = "AOP Test class")
@RestController
@RequestMapping("/aopTest")
public class AopController {
    @ApiOperation("test AOP agent")
    @RequestMapping(value = "aopTestMethod", method = RequestMethod.POST)
    @ResponseBody
    public Object aopTestMethod(){
        long startTime = System.currentTimeMillis();

        // 1 output top half
        int starLine = 19;
        for (int i = 1; i <= starLine; i++) {
            // 1.1 output blank
            for (int j = 0; j <= starLine - i; j++) {
                System.out.print("A");
            }
            //Output empty string
            for (int m = 1; m <= 5; m++) {
                System.out.print(" ");
            }
            // 1.2 output *
            for (int k = 1; k <= 2 * i - 1; k++) {
                System.out.print("*");
            }
            //Output empty string
            for (int n = 1;n <= 5 ; n++) {
                System.out.print(" ");
            }
            //Output right A
            for (int j = 0; j <= starLine - i; j++) {
                System.out.print("B");
            }
            System.out.println();
        }
        // 2 output lower half
        for (int i = 1; i <= starLine - 1; i++) {
            // 2.1 output A
            for (int j = 0; j <= i; j++) {
                System.out.print("A");
            }
            //Output empty string
            for (int m= 1; m <= 5 ; m++) {
                System.out.print(" ");
            }
            // 2.2 output *
            for (int k = 1; k <= (-2 * i + 2 * starLine - 1); k++) {
                System.out.print("*");
            }
            //Output empty string
            for (int k= 1; k <= 5; k++) {
                System.out.print(" ");
            }
            for (int j = 0; j <= i; j++) {
                System.out.print("B");
            }
            System.out.println();
        }
        long endTime = System.currentTimeMillis();
        return "Executing this method consumes : "+(endTime-startTime)+" ms";
    }
}

AOP cut class

package com.spring.aop.xiaozhe.component;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.spring.aop.xiaozhe.dto.WebLog;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName WebLogAspect
 * @Description Log facet class
 * @Author ZhengZhe
 * @Date 2020/1/14 14:41
 * @Version 1.0
 **/
@Aspect//This note indicates that this class is a tangent class
@Component//Inject into spring container
@Order(1)//Execution sequence
public class WebLogAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class);
    //Defining tangent point is tangent rule
    @Pointcut("execution(public * com.spring.aop.xiaozhe.controller.*.*(..))")
    public void webLog() {
    }
    //Before advice
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        System.out.println("Pre notification is performed");
    }
    //Return notification
    @AfterReturning(value = "webLog()", returning = "ret")
    public void doAfterReturning(Object ret) throws Throwable {
        System.out.println("Return notification is performed");
    }
    //Post notification
    @After(value = "webLog()")
    public void doAfter(JoinPoint joinPoint){
        System.out.println("Post notification is performed");
    }
    //Exception notification
    @AfterThrowing(value = "webLog()")
    public void doAfterThrowing(JoinPoint joinPoint){
        System.out.println("Exception notification is performed");
    }

    //Around Advice
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Orbit notification begins");
        long startTime = System.currentTimeMillis();
        //Get current request object
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //Record request information
        WebLog webLog = new WebLog();
        Object result = joinPoint.proceed();
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method.isAnnotationPresent(ApiOperation.class)) {
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
            webLog.setDescription(apiOperation.value());
        }
        long endTime = System.currentTimeMillis();
        String urlStr = request.getRequestURL().toString();
        webLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()));
        webLog.setIp(request.getRemoteUser());
        webLog.setMethod(request.getMethod());
        webLog.setParameter(getParameter(method, joinPoint.getArgs()));
        webLog.setResult(result);
        webLog.setSpendTime((int) (endTime - startTime));
        webLog.setStartTime(startTime);
        webLog.setUri(request.getRequestURI());
        webLog.setUrl(request.getRequestURL().toString());
        LOGGER.info("{}", JSONUtil.parse(webLog));
        System.out.println("The execution of the orbit notification ends");
        return result;
    }

    /**
     * Get request parameters based on method and passed in parameters
     */
    private Object getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //Use the parameter decorated by the RequestBody annotation as the request parameter
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
            }
            //Use the parameter decorated by the RequestParam annotation as the request parameter
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map<String, Object> map = new HashMap<>();
                String key = parameters[i].getName();
                if (!StringUtils.isEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        if (argList.size() == 0) {
            return null;
        } else if (argList.size() == 1) {
            return argList.get(0);
        } else {
            return argList;
        }
    }
}

Log packing class

package com.spring.aop.xiaozhe.dto;

/**
 * @ClassName WebLog
 * @Description Log packing class
 * @Author ZhengZhe
 * @Date 2020/1/14 14:41
 * @Version 1.0
 **/
public class WebLog {
    /**
     * Pedagogical operation
     */
    private String description;

    /**
     * Operation user
     */
    private String username;

    /**
     * Operation time
     */
    private Long startTime;

    /**
     * Consumption time
     */
    private Integer spendTime;

    /**
     * Root path
     */
    private String basePath;

    /**
     * URI
     */
    private String uri;

    /**
     * URL
     */
    private String url;

    /**
     * Request type
     */
    private String method;

    /**
     * IP address
     */
    private String ip;

    /**
     * Request parameters
     */
    private Object parameter;

    /**
     * Results returned by request
     */
    private Object result;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Long getStartTime() {
        return startTime;
    }

    public void setStartTime(Long startTime) {
        this.startTime = startTime;
    }

    public Integer getSpendTime() {
        return spendTime;
    }

    public void setSpendTime(Integer spendTime) {
        this.spendTime = spendTime;
    }

    public String getBasePath() {
        return basePath;
    }

    public void setBasePath(String basePath) {
        this.basePath = basePath;
    }

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public Object getParameter() {
        return parameter;
    }

    public void setParameter(Object parameter) {
        this.parameter = parameter;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }
}

Test code execution results

When no exception occurs

Orbit notification begins
//Pre notification is performed
AAAAAAAAAAAAAAAAAAA     *     BBBBBBBBBBBBBBBBBBB
AAAAAAAAAAAAAAAAAA     ***     BBBBBBBBBBBBBBBBBB
AAAAAAAAAAAAAAAAA     *****     BBBBBBBBBBBBBBBBB
AAAAAAAAAAAAAAAA     *******     BBBBBBBBBBBBBBBB
AAAAAAAAAAAAAAA     *********     BBBBBBBBBBBBBBB
AAAAAAAAAAAAAA     ***********     BBBBBBBBBBBBBB
AAAAAAAAAAAAA     *************     BBBBBBBBBBBBB
AAAAAAAAAAAA     ***************     BBBBBBBBBBBB
AAAAAAAAAAA     *****************     BBBBBBBBBBB
AAAAAAAAAA     *******************     BBBBBBBBBB
AAAAAAAAA     *********************     BBBBBBBBB
AAAAAAAA     ***********************     BBBBBBBB
AAAAAAA     *************************     BBBBBBB
AAAAAA     ***************************     BBBBBB
AAAAA     *****************************     BBBBB
AAAA     *******************************     BBBB
AAA     *********************************     BBB
AA     ***********************************     BB
A     *************************************     B
AA     ***********************************     BB
AAA     *********************************     BBB
AAAA     *******************************     BBBB
AAAAA     *****************************     BBBBB
AAAAAA     ***************************     BBBBBB
AAAAAAA     *************************     BBBBBBB
AAAAAAAA     ***********************     BBBBBBBB
AAAAAAAAA     *********************     BBBBBBBBB
AAAAAAAAAA     *******************     BBBBBBBBBB
AAAAAAAAAAA     *****************     BBBBBBBBBBB
AAAAAAAAAAAA     ***************     BBBBBBBBBBBB
AAAAAAAAAAAAA     *************     BBBBBBBBBBBBB
AAAAAAAAAAAAAA     ***********     BBBBBBBBBBBBBB
AAAAAAAAAAAAAAA     *********     BBBBBBBBBBBBBBB
AAAAAAAAAAAAAAAA     *******     BBBBBBBBBBBBBBBB
AAAAAAAAAAAAAAAAA     *****     BBBBBBBBBBBBBBBBB
AAAAAAAAAAAAAAAAAA     ***     BBBBBBBBBBBBBBBBBB
AAAAAAAAAAAAAAAAAAA     *     BBBBBBBBBBBBBBBBBBB
2020-01-14 15:23:30.715  INFO 6084 --- [nio-8080-exec-2] c.s.aop.xiaozhe.component.WebLogAspect   : {"result":"Executing this method consumes : 14 ms","basePath":"http://127.0.0.1:8080","method":"POST","description ":" test AOP agent "," starttime ": 1578986664," URI ":" / aoptest / aoptestmethod "," URL ":" http://127.0.0.1:8080/aoptest/aoptestmethod "," pendtime ": 21}
//The execution of the orbit notification ends
//Post notification is performed
//Return notification is performed

When there is an exception in the execution code

We need to write a runtime exception in the code, such as int xiaozhe = 1/0
Orbit notification begins
 Pre notification is performed
 Post notification is performed
 Exception notification is performed
2020-01-14 15:32:13.222 ERROR 11608 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero

summary

Through the above description, we can probably understand the application scenarios of AOP, such as the log system that needs to be used by the project. To obtain the log information in each method (mainly to capture the exception log), we need to set a set of AOP scanning rules that conform to the project. Or to apply it to the cache to improve the user's access experience, AOP can be used. Of course, the above test code is simple For example, the specific application needs to be considered in the actual project

Published 25 original articles, won praise 11, visited 715
Private letter follow

Tags: Java Spring JSON

Posted on Tue, 14 Jan 2020 03:31:25 -0500 by sarbas