Although the road is endless and faraway, I still want to pursue the truth in the world.
AOP
Aspect Oriented Programming means Aspect Oriented Programming, which may be because java's object-oriented programming is used to reduce duplicate code and coupling when dealing with log, authorization and other operations Therefore, a concept of aspect programming is introduced Popular aspect programming can be understood as an enhanced operation of multiple methods
Basic knowledge
This article doesn't explain the basics much. The following learning addresses are relatively comprehensive, but it is strongly recommended that you read the official website documents when learning Spirng related knowledge. Generally, blogs are the knowledge that some program apes understand and sort out by themselves. Many have a big problem because they don't understand clearly, so you must learn through the official website documents, Then assist the blog to understand some difficulties
data | address |
---|---|
Spring official website | https://docs.spring.io/spring-framework/docs/current/reference/html/core.html |
AOP Foundation | https://blog.csdn.net/qq_41701956/article/details/84427891 |
Introduce dependency
<!-- AOP support --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <scope>test</scope> </dependency> <!--aspectJ Weave in --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
I Turn on @ AspectJ support
Official documents:
To enable @AspectJ support with Java @Configuration, add the @EnableAspectJAutoProxy annotation, as the following example shows:
@Configuration @EnableAspectJAutoProxy public class AppConfig { }
This means that when configuring using java annotation, add @ EnableAspectJAutoProxy to the configuration class to enable the default support of spring for AspectJ
Define section
Two tangent points are defined in the tangent plane, and the connection points within the tangent point overlap. In order to verify the priority of the notification
/** * @description: Define a slice * be careful: * 1. Highest priority to lowest priority: @ Around, @ Before, @ After, @ AfterReturning, @ aftercrossing. * Note, however, that the @ after advice method will be effectively called after any @ afterreturn or @ aftercrossing advice methods in the same aspect * 2. When two overlapping tangent points are defined in a tangent plane, if notifications are defined separately at this time, the order cannot be determined. The official website recommends that we merge the two tangent points * Form a tangent point, or define another tangent plane, and specify the priority through @ Order (the smaller the data, the higher the priority) * @author: wangml * @date: 2021/8/11 09:00 */ @Aspect @Order(100) @Component public class NotVeryUsefulAspect { private static final Logger logger = LoggerFactory.getLogger(NotVeryUsefulAspect.class); @Pointcut("execution(* com.huntkey.rx..service.*.*(..))") public void pointMed1() {}; @Pointcut("execution(* com.huntkey.rx..service.UserService.delUser(..))") public void pointMed2() {}; @Before("pointMed1()") public void beforeMedExecute() { logger.info("----Section method 1 is called before execution.----"); } @AfterReturning("pointMed1()") public void afterReturnMedExecute() { logger.info("----Section method 1 is executed after successful execution.----"); } @After("pointMed1()") public void afterMedExecute() { logger.info("----Section method 1 is called after execution.--------"); } @Around("pointMed2()") public Object aroundMed2Execute(ProceedingJoinPoint pjp) throws Throwable { logger.info("******Facet method 2 is called********"); // Modify input parameters Object[] args = pjp.getArgs(); logger.info("Initial parameters: {}", args); // Get method signature MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); // Gets the parameter list of the method Class[] parameterTypes = methodSignature.getParameterTypes(); if (String.class.isAssignableFrom(parameterTypes[0])) { // The first parameter type of the method parameter is String args[0] = "88888"; } logger.info("Modified parameters: {}", args); logger.info("########surround, Method execution preprocessing#########"); Object result = pjp.proceed(args); logger.info("########surround, Method execution post-processing#########"); logger.info("Returned data: {}", result); return result; } }
Define a UserService
/** * @description: UserService * @author: wangml * @date: 2021/8/11 09:25 */ @Service public class UserService { public void addUser() { System.out.println("insert a user into user table"); } public String delUser(String id) { System.out.println("delete a user from user table where id = " + id); return "delete user success!"; } }
New test class
/** * @description:AOP Using test classes * @author: wangml * @date: 2021/8/11 09:32 */ @RunWith(SpringRunner.class) @SpringBootTest public class AopTest { @Autowired private UserService userService; // @Autowired // private EmployeeService employeeService; @Test public void addUser() { userService.addUser(); } @Test public void delUser() { userService.delUser("1111"); } // @Test // public void addEmployee() { // employeeService.addEmployee(); // } // // @Test // public void delEmployee() { // employeeService.delEmployee("111"); // } }
Analysis: when addUser is executed, because the method is within the connection point expression range of pointMed1, the notification of pointMed1 will be executed. The contents of beforemedexecute, afterreturnmedexecute and aftermedexecute will be executed. The execution results are as follows, which meet the expectations
2021-08-11 17:38:03.200 INFO 13388 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is called before execution.---- insert a user into user table 2021-08-11 17:38:03.216 INFO 13388 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is executed after successful execution.---- 2021-08-11 17:38:03.217 INFO 13388 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is called after execution.--------
When delUser is executed, the join point expressions of the two pointcuts are satisfied and will be executed. However, because @ around > @ before > @ afterreturning > @ after, pointpointmed2 will be executed first. The results are as follows, which meet the expectations
2021-08-11 17:39:28.614 INFO 14100 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ******Facet method 2 is called******** 2021-08-11 17:39:28.614 INFO 14100 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : Initial parameters: 1111 2021-08-11 17:39:28.617 INFO 14100 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : Modified parameters: 88888 2021-08-11 17:39:28.617 INFO 14100 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ########surround, Method execution preprocessing######### 2021-08-11 17:39:28.617 INFO 14100 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is called before execution.---- delete a user from user table where id = 88888 2021-08-11 17:39:28.632 INFO 14100 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is executed after successful execution.---- 2021-08-11 17:39:28.632 INFO 14100 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is called after execution.-------- 2021-08-11 17:39:28.632 INFO 14100 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ########surround, Method execution post-processing######### 2021-08-11 17:39:28.632 INFO 14100 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : Returned data: delete user success!
Special attention:
- Surround can control whether the actual method is executed. It is implemented through processed. If it is not written, the method will not be executed, and the calling process is a chain call, which is similar to the processing of filter FilterChain
- Wrapping can be used to pass parameters in the processed method before proceed ing. The processed method returns data. If the method is of void type, null is returned by default
- Generally, surround is preferred (official website)
Define another slice
It is mainly used to verify the priority of different cut points and to define the use of cut points with user-defined annotations
/** * @description: Slice intercepts all methods that use annotations * @author: wangml * @date: 2021/8/11 14:16 */ @Aspect @Order(10) @Component public class VeryUsefulAspect { public static final Logger logger = LoggerFactory.getLogger(VeryUsefulAspect.class); @Pointcut("@annotation(com.huntkey.rx.sceo.anno.Idempotent)") public void pointCutMed(){}; @Around("pointCutMed()") public Object aroundAnn(ProceedingJoinPoint joinPoint) throws Throwable { // Gets the data passed in the annotation Object[] args = joinPoint.getArgs(); logger.info("Method parameters are not processed, The parameter value is: {}", args); MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature(); Method method = methodSignature.getMethod(); Idempotent annotation = method.getAnnotation(Idempotent.class); String value = annotation.value(); String name = annotation.name(); if (!"".equals(value) || !"".equals(name)) { // When the return value of the method in the annotation is not the default value, there is a re assignment logger.info("*****Annotation data operation********"); logger.info("Idempotent Annotation data, value: {} name: {}", value, name); } // Specific implementation method Object proceed = joinPoint.proceed(); // Processed is the data returned after the method is executed. If the method is void, it will be null by default logger.info("***Method returns data after execution: {}", proceed); return proceed; } }
Custom annotation @ Idempotent
/** * @description: Method annotation * @author: wangml * @date: 2021/8/11 14:12 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Idempotent { @AliasFor("name") String value() default ""; @AliasFor("value") String name() default ""; }
Define an EmployeeService
/** * @description: Employee service (test AOP annotation usage) * @author: wangml * @date: 2021/8/11 14:48 */ @Service public class EmployeeService { private static final Logger logger = LoggerFactory.getLogger(EmployeeService.class); public void addEmployee() { logger.info("insert a employee into employee table"); } @Idempotent(name = "wangml") public String delEmployee(String id) { logger.info("delete a employee from employee table where id = {}", id); return "delete employee success!"; } }
Analysis: addEmployee doesn't add @ Idempotent, so only pointMed1 in NotVeryUsefulAspect
The connection point expression in is satisfied, the execution method is executed, and the output is as follows, which meets the expectation
2021-08-11 18:04:37.884 INFO 5228 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is called before execution.---- 2021-08-11 18:04:37.899 INFO 5228 --- [ main] c.h.rx.sceo.service.EmployeeService : insert a employee into employee table 2021-08-11 18:04:37.900 INFO 5228 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is executed after successful execution.---- 2021-08-11 18:04:37.900 INFO 5228 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is called after execution.--------
delEmployee adds @ Idempotent, so the join point expression of pointCutMed in the newly defined facet VeryUsefulAspect satisfies the join point expression in pointMed1 in NotVeryUsefulAspect, but the facet priority of VeryUsefulAspect is higher than that of NotVeryUsefulAspect, so the notification in VeryUsefulAspect is executed before NotVeryUsefulAspect The execution method is entered as follows, which meets the expectation
2021-08-11 18:09:41.215 INFO 7796 --- [ main] c.huntkey.rx.sceo.aop.VeryUsefulAspect : Method parameters are not processed, The parameter value is: 111 2021-08-11 18:09:41.219 INFO 7796 --- [ main] c.huntkey.rx.sceo.aop.VeryUsefulAspect : *****Annotation data operation******** 2021-08-11 18:09:41.219 INFO 7796 --- [ main] c.huntkey.rx.sceo.aop.VeryUsefulAspect : Idempotent Annotation data, value: name: wangml 2021-08-11 18:09:41.219 INFO 7796 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is called before execution.---- 2021-08-11 18:09:41.236 INFO 7796 --- [ main] c.h.rx.sceo.service.EmployeeService : delete a employee from employee table where id = 111 2021-08-11 18:09:41.236 INFO 7796 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is executed after successful execution.---- 2021-08-11 18:09:41.236 INFO 7796 --- [ main] c.h.rx.sceo.aop.NotVeryUsefulAspect : ----Section method 1 is called after execution.-------- 2021-08-11 18:09:41.236 INFO 7796 --- [ main] c.huntkey.rx.sceo.aop.VeryUsefulAspect : ***Method returns data after execution: delete employee success!
summary
- aop is powerful, but it's best not to define too many, or you will faint when checking the problem, because there may be layers of nesting, and you can process the data every time
- It is applicable to reduce repetitive code. It can be used when many business classes need some processing logic that is not available before