Home  >  Article  >  Java  >  How to quickly implement Aop function in Springboot project

How to quickly implement Aop function in Springboot project

WBOY
WBOYforward
2023-05-16 23:04:151421browse

Dependency introduction

After Springboot introduces the AOP dependency package, generally speaking, there is no need to do other configurations. In lower versions or other configurations affect the related functions of AOP, resulting in the inability of the aop function. To take effect, you can try adding @EnableAspectJAutoProxy to the startup class to enable it;

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

Code implementation

1. Custom annotation @TestAop

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAop {
}

2.ExampleAop .java

@Component
@Aspect
@Slf4j
public class ExampleAop {
 
    //切入点:增强标有@TestAop注解的方法
    @Pointcut(value = "@annotation(TestAop)")
    //切入点签名
    public void pointcut() {
        System.out.println("pointCut签名。。。");
    }
 
    //前置通知
    @Before("pointcut()")
    public void deBefore(JoinPoint joinPoint) throws Throwable {
        log.info("前置通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
    }
 
    //返回通知
    @AfterReturning(returning = "ret", pointcut = "pointcut()")
    public void doAfterReturning(Object ret) throws Throwable {
        log.info("返回通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
    }
 
    //异常通知
    @AfterThrowing(throwing = "ex", pointcut = "pointcut()")
    public void throwss(JoinPoint jp, Exception ex) {
        log.info("异常通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
        //可以从ex中获取到具体的异常信息
    }
 
    //后置通知
    @After("pointcut()")
    public void after(JoinPoint jp) {
        log.info("后置通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
    }
 
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------环绕通知 start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName = null;
        StringBuilder sb = new StringBuilder();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length() > 0) {
                argsName = sb.toString().substring(1);
            }
        }
        log.info("命中类:{},方法{},参数{};", className, methodName, argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------环绕通知 end");
        return proceed;
    }
 
}

Core annotations and classes

1. Aspect indicates that the current class is an aspect class. A simple understanding is the abstract encapsulation of entry points and notifications. It expresses the relationship between entry points and notification methods. Correspondence; the method marked with the

@Before annotation will be executed before the method is cut into execution, which is called a pre-notification

3, @After, post-notification, used for methods, is The method marked by the @After annotation will be executed after the cut method is executed;

4. @AfterReturning, return notification, is used for methods. The method marked by the @AfterReturning annotation will be executed after the cut method returns the result. ;

6. @AfterThrowing: Exception notification, used on methods. The method marked by the @AfterThrowing annotation will be executed after the exception is thrown by the cut-in method. It is generally used to obtain exception information for a purpose;

7, @Aroud: surround notification, used for methods, the method marked by @Around annotation will be executed before and after the execution of the cut-in method;

8, @Pointcut, cut-in point, marked in the method on, used to define the entry point. The so-called entry point refers to the definition of which connection points are to be enhanced. In Spring, it specifically refers to the definition of which methods are to be enhanced; the expression annotated with @Pointcut represents the entry point. There are many, the most commonly used two are execution expressions and annotations;

9. Jointpoint, the so-called connection point refers to the position point cut by the aop aspect, specifically in Spring it refers to Method to be cut in;

10, PointCut,

11, Advice, notification. The so-called notification refers to the definition of specific operations to be performed after intercepting the defined entry point; In Spring, it refers to methods marked by @Before, @After, @AfterReturning, @AfterThrowing, and @Around annotations;

Common ways to mark entry points

1, execution expression

Expression method: access modifier return value package name.package name...class name.method (parameter list)

Example 1: Indicates matching all com.fanfu packages and sub-packages Methods starting with add in all classes, the return value and parameters are not limited;

@Pointcut("execution(* com.fanfu..*.*.add*(..))")

Example 2: Indicates matching all classes starting with add in all com.fanfu packages and sub-packages, and the parameter type is String method, the return value is not limited;

@Pointcut("execution(* com.fanfu..*.*.add*(String))")

Example 3: Indicates matching any class, any method, and any parameter under the com.fanfu package;

@Pointcut("execution(* com.fanfu..*.*.*(String))")

execution() is an expression The main body;
The first * indicates that the return value type is arbitrary, that is, the return value type is not limited;
The * after the package indicates the current package, and the two consecutive .. after the package indicate the current package and sub-packages;
(..) means any parameter;
The last *.*(..) means matching any class, any method, any parameter;

2, annotation

Annotation syntax: @annotation (custom annotation)

Example: Indicates a method that matches all tags @TestAop annotations;

@Pointcut("@annotation(com.fanfu.config.TestAop)")

Spring Aop tips

Each one @Pointcut can use execution or annotations to define entry points. Logical operators can also be used between multiple cut points, such as &&, ||, ! operation;

1, point1()&&point2() indicates a hit The intersection of all entry points of point1 and point2; for example: in all classes of the com.fanfu package and all subordinate sub-packages, the method name starts with add and the parameter type is String, and all methods of the com.fanfu.service package and The intersection of all methods in all classes of all subordinate sub-packages, regardless of method name and parameter type, that is, in all classes of the com.fanfu.service package and all subordinate sub-packages, the method is either the add1 or add2 method, before and after the call The logic within the surrounding notification around() method will be executed;

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu..*.*.add*(String))")
    public void point1() {
    }
    @Pointcut("execution(* com.fanfu.service..*.*(..))")
    public void point2() {
    }
    @Pointcut("point1()&&point2()")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}

2, point1()&&point2() represents the union of all pointcuts that hit point1 and point2; such as the example: com.fanfu.service package and Among all the classes in all sub-packages, the method name is add1 and the parameter type is String. In the com.fanfu.controller package and all classes in all sub-packages, the method name is add2 and the parameter type is String. The method is the union, that is, in all classes of the com.fanfu.service or com.fanfu.controller package and all subordinate sub-packages, the method or the add1 or add2 method will execute the surrounding notification before and after calling the around() method. Logic;

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu.service..*.add*(String))")
    public void point1() {
    }
    @Pointcut("execution(* com.fanfu.controller..*.*.add*(String))")
    public void point2() {
    }
    @Pointcut("point1()||point2()")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}

3. !point() indicates the inversion of all entry points that hit point(). For example: in all classes of the com.fanfu.service package and all subordinate sub-packages, it is not added with The method at the beginning will execute the logic in the around() method before and after the call.

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu.service..*.add*(String))")
    public void point() {
    }
    @Around("!point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}

Spring Aop Notes

1. If the method matches the defined pointcut, if it is in the current call chain , if the method is matched for the first time in the current class, it will hit, that is, the relevant notification will be executed. If the current call chain has not ended, and the matching method of the current class with other entry points is called in the current method, it will not hit again. That is, other methods that match the entry point will no longer trigger relevant notifications when executed;

The following example:

当请求http://localhost:8080/example时,ExampleController中的example方法被触发,ExampleController#example()又调用了ExampleService#test(),在ExampleService#test()内部,又顺序调用了ExampleService#test1()和ExampleService#test2();在ExampleAop中,按照execution中的配置,是可以匹配到test()、test1()、test2(),实际是命中的方法只有test();

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu.service.impl.ExampleServiceImpl.test*(..))")
    public void point2() {
        log.info("切入点匹配");
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName=null;
        StringBuilder sb=new StringBuilder();
        if (args!=null&&args.length>0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length()>0) {
                argsName=sb.toString().substring(1);
            }
        }
        log.info("命中类:{},方法{},参数{};",className,methodName,argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}
@Service
@Slf4j
public class ExampleServiceImpl implements IExampleService {
 
    @Override
    public String test(String msg) {
        log.info("test方法被执行");
        this.test1(msg);
        this.test2(msg);
        return msg;
    }
 
    public String test1(String msg) {
        log.info("test1方法被执行");
        return "msg1";
    }
 
    public String test2(String msg) {
        log.info("test2方法被执行");
        return "msg2";
    }
}
public interface IExampleService {
    public String test(String msg);
    public String test1(String msg);
    public String test2(String msg);
}
@RestController
@Slf4j
public class ExampleController {
    @Autowired
    private IExampleService exampleService;
    @GetMapping("/example")
    public String example() {
        log.info("example start");
        exampleService.test(null);
        log.info("example end");
        return String.valueOf(System.currentTimeMillis());
    }
}

How to quickly implement Aop function in Springboot project

2、对于上面的问题,如果把execution表达换成注解,会不会结果不一样?再把ExampleAop中的@Pointcut改成注解形式,再在ExampleService#test1()、ExampleService#test2()、ExampleService#test()添加注解@TestAop,验证结果依然是一样的,只有test()会命中,其他不会!所以要注意呀。

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("@annotation(TestAop)")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName = null;
        StringBuilder sb = new StringBuilder();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length() > 0) {
                argsName = sb.toString().substring(1);
            }
        }
        log.info("命中类:{},方法{},参数{};", className, methodName, argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}
@Service
@Slf4j
public class ExampleServiceImpl implements IExampleService {
 
    @Override
    @TestAop
    public String test(String msg) {
        log.info("test方法被执行");
        this.test1(msg);
        this.test2(msg);
        return msg;
    }
    @Override
    @TestAop
    public String test1(String msg) {
        log.info("test1方法被执行");
        return "msg1";
    }
    @Override
    @TestAop
    public String test2(String msg) {
        log.info("test2方法被执行");
        return "msg2";
    }
   
}

3、那什么情况下,ExampleService#test1()、ExampleService#test2()、ExampleService#test()会同时命中呢?让从ExampleController#example()到ExampleService#test1()、ExampleService#test2()、ExampleService#test()分别在不同的调用链上,那么就可以同时命中了;

@RestController
@Slf4j
public class ExampleController {
    @Autowired
    private IExampleService exampleService;
    @GetMapping("/example")
    public String example() {
        log.info("example start");
        exampleService.test(null);
        exampleService.test1(null);
        exampleService.test2(null);
        log.info("example end");
        return String.valueOf(System.currentTimeMillis());
    }
}

How to quickly implement Aop function in Springboot project

The above is the detailed content of How to quickly implement Aop function in Springboot project. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete