Tangent programming in Spring Boot, explanation of example scenario (log printing)

Keywords: Spring Java Programming Maven

Article directory

1. What is AOP

Official explanation:

AOP is a technology that can add functions to the program dynamically and uniformly without modifying the source code through precompiling and runtime dynamic agent. In the code stage, we can realize separation of concerns and loose coupling, and then implant the relevant action features into the operation, so as to change the running order of the program, and achieve the purpose of intercepting or adding functions.

Well, I admit, it's awkward to read, and I don't think you can understand what it means to read it over and over again.

Let me present a scenario to see how you can implement it, and use this example to understand aspect programming.

There are three methods A, B and C, but before calling each method, it is required to print A log: A certain method is called! After each method is called, the log is also printed: A method is called! Count the execution time of each method.

Ordinary people will add a sentence of log printing at the beginning and end of each method. If there are many methods, there will be a lot of repetitive code, which is very troublesome. At this time, some people will think about why not encapsulate the function of printing log, and then make it automatically go to the designated place (such as before or after executing the method) What about invocation? In this way, it's too verbose to let developers do business development professionally.

If possible, this other code will not be mixed in the business function code, so AOP does this kind of work, such as log output, transaction control, exception handling, etc.

Here's how to integrate AOP with Springboot

2.Spring Boot integrated AOP

2.1 add maven dependency

To use AOP, first introduce the dependency on AOP, as follows:

<!--Aop Dependence-->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>

2.2 section programming (implementation of log printing example)

Here are some important notes:

1.@Pointcut: defines a facet, that is, an entry to something that is concerned as described above.
2.@Before: something done before doing something.
3.@After: after doing something.
4.@AfterReturning: after doing something, enhance its return value.
5.@AfterThrowing: handle when doing something and throwing an exception.
6.@Around: advice is executed before and after the original method is executed (@ Around can implement the other two kinds of advice)

2.2.1 create the facet class LogAspectHandler.java

It's very easy to integrate AOP in Spring Boot. If we want to print some logs in the project, we will create a new class, LogAspectHandler, to define the faceting and processing methods. Just add an @ Aspect annotation to the class. @The Aspect annotation is used to describe a tangent class, which needs to be marked when defining a tangent class. @Component annotation is to give this class to Spring for management.

@Aspect
@Component
public class LogAspectHandler {

}

2.2.2 @Pointcut: pointcut

@Pointcut annotation: used to define a cut-in point, that is, to define interception rules and determine which methods will be cut in. Pointcuts determine what concerns the join point, allowing us to control when notifications are executed.

@Aspect
@Component
public class LogAspectHandler {
    /**
     * Define a facet to intercept all methods under com.ieslab.powergrid.demosvr.controller package and subpackage
     */
    @Pointcut("execution(* com.ieslab.powergrid.demosvr.controller..*.*(..))")
    public void pointCut() {}
}

@The Pointcut annotation specifies a facet and defines what needs to be intercepted. Here we focus on two common expressions: one is to use execution(), the other is to use annotation().

First, take the expression execution(* com.ieslab.powergrid.demosvr.controller.. * (..)) as an example. The syntax is as follows:

  1. execution() is the body of the expression
  2. Position of the first * sign: indicates the return value type, * indicates all types
  3. Package name: indicates the package name to be intercepted. The following two periods indicate the current package and all subpackages of the current package. com.itcodai.course09.controller package and methods of all classes under the subpackages
  4. Position of the second * sign: indicates the class name, * indicates all classes
  5. *(..): This asterisk indicates the method name, * indicates all methods, followed by parentheses indicating the parameters of the method, and two periods indicating any parameters

annotation() is used to define facets for a certain annotation. For example, we can define facets for methods with @ GetMapping annotation as follows:

@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void annotationCut() {}

Then, using this aspect, we will cut into the annotation method of @ GetMapping. Because in the actual project, there may be different logical processing for different annotations, such as @ GetMapping, @ PostMapping, @ DeleteMapping, etc. So this way of cutting in according to annotations is also very common in actual projects.

In practical applications, we sometimes customize annotations and define facets according to them, so that we can intercept the methods of adding custom annotations.

2.2.3 @Before: pre advice

@Before note: advice. The specified method is executed before cutting into the target method. It can do some log processing, and can also do some information statistics, such as obtaining the user's request url and the user's ip address. This method can be used when making a personal site. It is a common method. For example:

package com.ieslab.powergrid.demosvr.utils;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/** <p>Title: LogAspectHandler </p>
 * <p>Description: Log faceting class</p>
 *
 * @author bingge
 * @date 2020-2-20 7:15:30 PM
 * @version V1.0
 */
@Aspect
@Component
@Slf4j
public class LogAspectHandler {
    /**
     * Define a facet to intercept all methods under com.ieslab.powergrid.demosvr.controller package and subpackage
     */
    @Pointcut("execution(* com.ieslab.powergrid.demosvr.controller..*.*(..))")
    public void pointCut() {}

    /**
     * Execute this method before the facet method defined above
     * @param joinPoint jointPoint
     */
    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("====doBefore Method in====");

        // Get signature
        Signature signature = joinPoint.getSignature();
        // Get the package name of the cut in
        String declaringTypeName = signature.getDeclaringTypeName();
        // Get the name of the method to be executed
        String funcName = signature.getName();
        log.info("The method to be executed is: {},belong to{}package", funcName, declaringTypeName);

        // It can also be used to record some information, such as the url and ip of the request
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // Get request url
        String url = request.getRequestURL().toString();
        // Get request ip
        String ip = request.getRemoteAddr();
        log.info("User requested url For:{},ip The address is:{}", url, ip);
    }
}

The JointPoint object is very useful. You can use it to obtain a signature, and then use the signature to obtain the package name and method name of the request, including parameters (obtained through joinPoint.getArgs()).

2.2.4 @After: Post advice

advice, @ After annotation and @ Before annotation correspond to each other. The specified method is executed After cutting into the target method, and some log processing After a method is completed can also be done.

Add the code as follows:

/**
     * Execute this method after the facet method defined above
     * @param joinPoint jointPoint
     */
    @After("pointCut()")
    public void doAfter(JoinPoint joinPoint) {
        log.info("====doAfter Method in====");
        Signature signature = joinPoint.getSignature();
        String method = signature.getName();
        log.info("Method{}Finished executing", method);
    }

So far, let's write a Controller to test the execution results, and create a new AopController as follows:

package com.ieslab.powergrid.demosvr.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/aop")
public class AopController {

    /**
     * Test interface
     * @param name Name
     * @return
     */
    @GetMapping("/test/{name}")
    public String testAop(@PathVariable String name) {
        return "Hello " + name;
    }

}

Start the project, enter http://localhost:8080/aop/test/binge in the browser, and see the output information of the console:

If shown in, the logs added in the previous two steps have been printed out.

2.2.5 @AfterReturning

After doing something, enhance the return value: @ AfterReturning annotation is similar to @ after, the difference is that @ AfterReturning annotation can be used to capture the return value after the execution of the cut in method and enhance the business logic of the return value.

For example, continue to add the following code in the LogAspectHandler.java class:

    /**
     * You can capture or enhance return objects
     * @param joinPoint joinPoint
     * @param result result
     */
    @AfterReturning(pointcut = "pointCut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        Signature signature = joinPoint.getSignature();
        String classMethod = signature.getName();
        log.info("Method{}After execution, the return parameters are:{}", classMethod, result);
        // In actual projects, specific return value enhancements can be made according to the business
        log.info("Business enhancements to return parameters:{}", result + "Enhanced Edition");
    }

Run the program, continue to enter http://localhost:8080/aop/test/binge in the browser, and observe the console log:

Note: in the @ AfterReturning annotation, the value of the property returning must be consistent with the parameter, otherwise it will not be detected. The second input parameter in this method is the return value of the cut method. The return value can be enhanced in the doAfterReturning method, and can be processed and encapsulated according to the business needs.

2.2.6 @AfterThrowing

As the name implies, the @ AfterThrowing annotation means that when an exception is thrown when the cut method is executed, it will enter the @ AfterThrowing annotation's method for execution. In this method, some exception handling logic can be made. Note that the value of the throwing property must be consistent with the parameter, otherwise an error will be reported. The second input parameter in this method is the exception thrown.

Continue to add the following code in the LogAspectHandler.java class:

/**
 * When the section method defined above performs throw exception, execute the method
 * @param joinPoint breakthrough point
 * @param ex error message
 */
@AfterThrowing(pointcut = "pointCut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
    Signature signature = joinPoint.getSignature();
    String method = signature.getName();
    // Logic for handling exceptions
    log.info("Execution method{}Error with exception:{}", method, ex);
}

Add the following code to the TestController class to intentionally create an exception:

@GetMapping("/testException")
public String testException() {
    int num = 1/0;
    return "Hello world";
}

Restart the program, continue to enter http://localhost:8080/aop/test/binge in the browser, and observe the console log:

2.2.7 @Around: around

Around advice, execute before and after the original method (@ around can implement the other two kinds of advice). No more introduction here, please try it yourself.

3. What are the application scenarios

Authority control, log output, transaction control, exception handling.
The log scenario has been introduced, and the use of other scenarios will be introduced in other articles later.

4. summary

This lesson gives a detailed explanation of AOP in Spring Boot. It mainly introduces the introduction of AOP in Spring Boot, the use of common annotations, the use of parameters, and the introduction of common APIs.

AOP is very useful in practical projects. It can be used to preprocess or enhance the aspect method before and after implementation according to the specific business. It can also be used as exception capture processing. It can be used reasonably according to the specific business scenarios.

Published 25 original articles, won praise 4, visited 600
Private letter follow

Posted by abriggs on Sat, 22 Feb 2020 01:45:48 -0800