1, Foreword
Long time no see. How are you?
Recently started learning Ruoyi Framework: a background management system based on SpringBoot. As a rapid development framework, online evaluation is good. Students who have time can look at the code and learn it.
At the beginning, I planned to introduce the user-defined annotation with the Log annotation of the Controller layer in the framework as a template, because some encapsulation classes and methods of the basic framework layer are involved in the aspect class. If I go deep into it, it will spread a lot, which is divorced from the theme of the user-defined annotation, Therefore, this article only uses the simplest program to demonstrate the implementation of SpringBoot custom annotation. Demonstration only
2, Text start
1. Project structure: the simplest web project built by SpringBoot scaffold
pom dependency:
<dependencies> <!-- SpringBoot Web container --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot Interceptor --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies>
application.yml:
server: port: 12345
2. Custom annotation
package com.example.demoannotation.annotation; import java.lang.annotation.*; /** * @Author: zongshaofeng * @Description: Custom operation log annotation * @Date:Create: in 2021/10/22 8:25 * @Modified By: */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface OperationLog { /** * Module name */ String title() default ""; /** * For simplicity, the following contents are omitted */ }
3. User defined annotation facet class
package com.example.demoannotation.aspectj; import com.example.demoannotation.annotation.OperationLog; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; /** * @Author: zongshaofeng * @Description: Facet class of operation logging * @Date:Create: in 2021/10/22 8:30 * @Modified By: */ @Aspect @Component public class OperationLogAspect { /** * @Author: zongshaofeng * @Description: Returns enhancement. The target method is executed when it is completed normally * @Date: 2021/10/22 20:15 * @param: joinPoint * @param: operationLog * @param: returnObj * @return: void * @Version: 1.0 */ @AfterReturning(pointcut = "@annotation(operationLog)",returning = "returnObj") public void doAfterReturning(JoinPoint joinPoint, OperationLog operationLog, Object returnObj) { System.out.println("===============AfterReturning Pointcut start execution......==============="); Map<String,Object> map=new HashMap<>(); map.put("title",operationLog.title()); map.put("returnObj",returnObj); map.put("method",joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName()); System.out.println(map); System.out.println("===============AfterReturning Pointcut execution is complete.==============="); } /** * @Author: zongshaofeng * @Description: Exception throwing is enhanced, and the target method is executed when an exception occurs * @Date: 2021/10/22 20:16 * @param: joinPoint * @param: operationLog * @param: e * @return: void * @Version: 1.0 */ @AfterThrowing(pointcut = "@annotation(operationLog)",throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, OperationLog operationLog, Exception e){ System.out.println("===============AfterThrowing Pointcut start execution......==============="); Map<String,Object> map=new HashMap<>(); map.put("title",operationLog.title()); map.put("exceptionMsg",e.getMessage()); map.put("method",joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName()); System.out.println(map); System.out.println("===============AfterThrowing Pointcut execution is complete.==============="); } /** * @Author: zongshaofeng * @Description: Pre enhancement: before the target method is executed * @Date: 2021/10/22 20:16 * @param: joinPoint * @param: operationLog * @return: void * @Version: 1.0 */ @Before(value = "@annotation(operationLog)") public void doBefore(JoinPoint joinPoint, OperationLog operationLog){ System.out.println("===============Before Pointcut start execution......==============="); Map<String,Object> map=new HashMap<>(); map.put("title",operationLog.title()); map.put("method",joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName()); System.out.println(map); System.out.println("===============Before Pointcut execution is complete.==============="); } /** * @Author: zongshaofeng * @Description: Post enhancement, whether throwing an exception or normal exit, will be executed * @Date: 2021/10/22 20:17 * @param: joinPoint * @param: operationLog * @return: void * @Version: 1.0 */ @After(value = "@annotation(operationLog)") public void doAfter(JoinPoint joinPoint, OperationLog operationLog){ System.out.println("===============After Pointcut start execution......==============="); Map<String,Object> map=new HashMap<>(); map.put("title",operationLog.title()); map.put("method",joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName()); System.out.println(map); System.out.println("===============After Pointcut execution is complete.==============="); } /** * @Author: zongshaofeng * @Description:Surround notification: perform some operations before and after the target method is executed * @Date: 2021/10/22 20:17 * @param: proceedingJoinPoint * @param: operationLog * @return: java.lang.Object * @Version: 1.0 */ @Around(value = "@annotation(operationLog)") public Object doAround(ProceedingJoinPoint proceedingJoinPoint, OperationLog operationLog) throws Throwable { System.out.println("===============Around Pointcut start execution......==============="); Map<String,Object> map=new HashMap<>(); map.put("title",operationLog.title()); map.put("method",proceedingJoinPoint.getTarget().getClass().getName()+"."+proceedingJoinPoint.getSignature().getName()); System.out.println(map); System.out.println("---------Around Pre notification execution completed in-------"); Object proceed = proceedingJoinPoint.proceed(); System.out.println("---------Around Return result of target method in:"+proceed); System.out.println("---------Around Target method execution completed in-------"); System.out.println("This is a post notification..."); System.out.println("---------Around Post notification execution completed-------"); System.out.println("===============Around Pointcut execution is complete.==============="); return proceed; } }
4. Controller layer
package com.example.demoannotation.controller; import com.example.demoannotation.annotation.OperationLog; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.Mapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author: zongshaofeng * @Description: * @Date:Create: in 2021/10/22 8:16 * @Modified By: */ @RestController @RequestMapping("/demo") public class SayHelloController { @GetMapping("/sayHello") @OperationLog(title = "Custom log annotation test-Say hello module") public String sayHello(String name) { return "Hello " + name; } }
5. Brief introduction to basic concepts
AOP (Aspect Oriented Programming) is a technology to realize the unified maintenance of program functions through precompiled mode and runtime dynamic agent. AOP is the continuation of OOP, a hot spot in software development, an important content in Spring framework, and a derivative paradigm of functional programming. AOP can isolate each part of business logic, reduce the coupling between each part of business logic, improve the reusability of program, and improve the efficiency of development.
In Spring AOP, the business logic only focuses on the business itself, dividing the logging, performance statistics, security control, transaction processing, exception handling and other codes from the business logic code, so that changing these behaviors will not affect the business logic code.
Relevant notes involved in AOP mainly include the following:
annotation | effect |
---|---|
@Aspect | Define facet class |
@Pointcut | Pointcut is the trigger condition for weaving Advice. The definition of each pointcut includes two parts: an expression and a method signature. Method signature must be public and void type. The method in pointcut can be regarded as a mnemonic referenced by Advice. Because the expression is not intuitive, we can name this expression by means of method signature. Therefore, the method in pointcut only needs the method signature, and does not need to write the actual code in the method body. |
@Around | Surround notification: perform some operations before and after the target method is executed |
@Before | Pre enhancement: before the target method is executed |
@AfterReturning | Returns enhancement. The target method is executed when it is completed normally |
@AfterThrowing | Exception throwing is enhanced, and the target method is executed when an exception occurs |
@After | Post enhancement, whether throwing an exception or normal exit, will be executed |
3, Look at the operation results and say the conclusion
It seems that we can't remember the text. Let's run it to see what the execution order of these enhanced notifications is, so that we can be handy and use it freely in the actual development in the future.
- First, start the project. We wrote a sayHello method in the Controller, and added our custom @ OperationLog annotation for notification enhancement.
- Open browser access Say hello ( http://localhost:12345/demo/sayHello?name= (Xiaozong)
- Page return:
1. Normal execution
Let's take a look at console printing:
Therefore, the execution order of the enhanced notification of the whole AOP is as follows:
@Code before target method call in Around Notification - > @ before code in pre Notification - > @ Around code after target method call - > @ Around code after target method call - > @ after post Notification - > @ afterreturning
2. When an exception occurs
Let's create some artificial trouble: add some byZero exceptions
Let's look at the implementation results:
I don't want to emphasize the inevitable implementation of @ AfterThrowing, but to point out that After our verification: @ After enhanced notification will be executed whether it is executed normally or abnormally.
All right, it's over.
In fact, it is very simple. There are many things that have not been mentioned. The author still wants to play a role in attracting jade.