Table of contents
As the core module of the Spring framework, Spring AOP provides us with an elegant way to deal with cross-cutting concerns. This blog will analyze Spring AOP in depth from three aspects: composition, implementation and implementation principle, and illustrate it with specific examples.
The composition of Spring AOP
Components and Common Annotations
- Aspect: An aspect is a concrete implementation of a crosscutting concern, which defines where and when to execute crosscutting logic. We can define an aspect by pointcut and advice.
- Join Point: A join point is a specific point where a method can be intercepted during program execution (this point can be when a method is called, when an exception is thrown, or even when a field is modified)
. In Spring AOP, a join point typically represents the execution of a method. - Pointcut: A pointcut specifies on which methods the aspect logic should be applied. Pointcuts can be defined using expressions or annotations to precisely locate the target method.
- Advice: Advice defines the crosscutting logic executed on the pointcut, which can be triggered before or after the method is executed or when an exception is thrown.
In the Spring aspect class, you can use the following annotations on the method, which will set the method as a notification method, and notify this method to call after the conditions are met:
- Use pre-advice
@Before
: the notification method will be executed before the target method is called. - Use post-notification
@After
: the notification method will be called after the target method returns or throws an exception. - Use notification after return
@AfterReturning
: The notification method will be called after the target method returns. - Notification after exception is thrown :
@AfterThrowing
the notification method will be called after the target method throws an exception. - Surrounding notification usage
@Around
: the notification wraps the notified method, and executes custom behaviors before and after the notified method is notified.
Example understanding
Through an example in life, we can more intuitively understand the components of Spring AOP and how it works.
In this example, we can relate the components of Spring AOP to the cafe scenario:
-
Aspect: Aspects are like different additional services provided by cafes, such as packaging, delivery, etc. Each additional service is an aspect, which defines a specific cross-cutting logic.
-
Pointcut: Pointcut is like deciding under which circumstances additional services should be provided. For example, every time someone orders coffee, that's a pointcut, representing a place where additional services can be applied.
-
Advice: Advice is the implementation of specific additional services. In a coffee shop, this could be the process of the coffee being packaged, or the delivery of the coffee to a designated location by a delivery person.
-
Join Point: The join point is like the coffee preparation process, such as grinding coffee beans, brewing coffee, etc. These processes are specific points in coffee making, the points of connection.
Here's how Spring AOP works when you order a cup of coffee at a cafe:
-
Aspects define additional services, such as packing and shipping.
-
Pointcuts determine when additional services should be provided, such as every time a coffee is ordered.
-
Notifications implement the specific logic of additional services, such as packing coffee or delivering it to a designated location.
-
A join point is the coffee making process, each step is a join point, and on some join points (that is, where the pointcut matches), notifications for additional services are triggered.
In this way, when you order a cup of coffee, Spring AOP will trigger corresponding additional service notifications according to the matching of cut points during the process of making coffee, such as packing the coffee or delivering it to a designated location.
Implementation of Spring AOP
The implementation of Spring AOP mainly depends on JDK dynamic proxy and CGLIB dynamic proxy. By default, Spring AOP uses JDK dynamic proxies, but it can also be switched to CGLIB dynamic proxies through configuration.
Add Spring AOP framework support
Add the following configuration in pom.xml:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-bo
ot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Define facets and pointscuts
Pointcut refers to a certain type of problem to be dealt with. The definition of Spring AOP pointcut is as follows. In pointcut, we need to define the rules of interception. The specific implementation is as follows:
@Aspect //表明这个类为一个切面
@Component
public class UserAspect {
//定义切点,使⽤ AspectJ 表达式语法
@Pointcut("execution(* com.example.springaop.Controller.UserController.* (..))")
public void pointcut(){
}
}
The method marked by the @Pointcut annotation is an empty method , which does not require a specific method body. Its role is to provide an identifier for the following notification method to specify which pointcut to point to.
Define Notifications
The notification defines the specific business to be executed by the intercepted method, such as unified exception handling and waiting.
Still write the code in the aspect class UserAspect just now:
// 前置通知
@Before("pointcut()")
public void doBefore(){
System.out.println("执⾏ Before ⽅法");
}
We write a test method in UserController:
@RestController
@ResponseBody
@RequestMapping("/con")
public class UserController {
@RequestMapping("/tsst")
public Object test() {
System.out.println("测试方法执行");
return "测试";
}
}
We conduct access test methods in the browser:
through the console information, we find that the implementation of the pre-notification method is before the test method. As for other notification methods, we also complete them in the aspect class.
// 后置通知
@After("pointcut()")
public void doAfter() {
System.out.println("执⾏ After ⽅法");
}
@AfterReturning("pointcut()")
public void doAfterReturning() {
System.out.println("执⾏ return 之前通知⽅法");
}
@AfterThrowing
public void doAfterThrowing() {
System.out.println("执行异常抛出之前的通知方法");
}
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) {
Object obj = new Object();
System.out.println("开始执行环绕通知方法");
try {
//执行目标方法
joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("环绕通知方法结束");
return obj;
}
The surround notification method can be understood as a layer of "wrapper" that wraps the target method, which can perform some additional operations and controls before and after the execution of the target method. By wrapping notifications, we can add some cross-cutting functions, such as logging, performance monitoring, transaction management, etc., without changing the original logic of the target method.
Pointcut expression description
AspectJ supports three wildcards
*
: Match any character, only one element (package, class, or method, method parameter)..
: Match any character, can match multiple elements, and must be used in combination with * when representing a class+
: Indicates that all classes of the specified class are matched according to the type, and must be followed by the class name, such as com.cad.Car+, which means that all subclasses inheriting the class include itself
Pointcut expressions are composed of pointcut functions, among which execution() is the most commonly used pointcut function, used to match methods, the syntax is: where execution(<修饰符><返回类型><包.类.⽅法(参数)><异常>)
modifiers and exceptions can be omitted .
Example:
execution(* com.cad.demo.User+.*(..))
Matching subclasses of this class include all methods of that class.
execution(* addUser(String, int))
Match the addUser method, and the first parameter type is String, and the second parameter type is int.
Spring AOP implementation principle
The implementation principle of Spring AOP mainly depends on the dynamic proxy mechanism. When using Spring AOP, it will generate a proxy object for the target object at runtime, and weave the aspect logic into the method call of the target object.
The proxy methods used by Spring AOP can be divided into two types: JDK dynamic proxy and CGLIB dynamic proxy.
JDK dynamic proxy
Spring AOP uses JDK dynamic proxies to generate proxy objects when the target object implements at least one interface. JDK dynamic proxies are implemented through java.lang.reflect.Proxy
classes and interfaces.java.lang.reflect.InvocationHandler
- In Spring AOP, first, a proxy object that implements the InvocationHandler interface is created based on the target object and the aspect object.
- When the method of the target object is called, the invoke() method of the proxy object is called. Inside the invoke() method, the target method can be executed through the reflection mechanism, and at the same time, the aspect logic can be inserted before and after the execution of the target method.
CGLIB dynamic proxy
When the target object does not implement any interface, Spring AOP will use CGLIB dynamic proxy to generate the proxy object. CGLIB dynamic proxy is realized by inheriting the target class and rewriting the target method.
- In Spring AOP, a proxy class that inherits the target class is first generated for the target object, and the target method is rewritten in the proxy class.
- When a method of the target object is called, the corresponding overridden method of the proxy object is called. In the rewriting method, you can insert aspect logic before and after the execution of the target method.
To sum up, the implementation principle of Spring AOP is to generate a proxy object for the target object at runtime through the dynamic proxy mechanism, and weave the aspect logic into the method of the target object. The specific use of JDK dynamic proxy or CGLIB dynamic proxy depends on whether the target object implements the interface.