Spring AOP implementation process and source code analysis

Preface

The literal interpretation of AOP is aspect-oriented programming. Aspect-oriented programming is a programming model. We know that JAVA is object-oriented, which is OOP. The object-oriented programming of OOP is suitable for defining vertical relationships, but it is not suitable for defining horizontal relationship. So to deal with these disadvantages of the existence of this kind of OOP, the AOP aspect-oriented programming model is used as a supplement to object-oriented, which is used to extract and encapsulate the common behaviors and logic that are not related to the business but have an impact on multiple objects. As a reusable module, this module is named "Aspect", which reduces the repetitive code in the system, reduces the coupling between modules, and improves the maintainability of the system. It can be used for authority authentication, log, and transaction processing.

AOP proxy introduction

Here is the implementation of AOP. Note that it is not Spring AOP. First of all, the key to AOP implementation lies in the proxy mode. AOP proxy is divided into dynamic proxy and static proxy. As we know, Spring AOP belongs to dynamic proxy, and the representative of static proxy is AspectJ;

Static proxy

AspectJ is an enhancement of the static proxy. The so-called static proxy means that the AOP framework will generate AOP proxy classes during the compilation phase, so this is also a compile-time enhancement. The static proxy will weave AspectJ (aspects) into the java bytecode during the compilation phase. , When it is running, it is the enhanced AOP object;

Dynamic proxy

SpringAOP is a dynamic proxy. The so-called dynamic proxy means that the AOP framework does not modify the bytecode file, but generates an AOP object for the method at zero time in memory every time it runs. This AOP object contains all the methods of the target object. And enhanced processing in specific aspects, and callback methods of the original object;

the difference

The difference between static proxy and dynamic proxy is that the timing of generating AOP proxy objects is different. Compared with AspectJ's static proxy method, it has better performance, but AspectJ requires a specific compiler for processing, while SpringAOP does not require a specific compiler to process.

SpringAOP dynamic proxy

We already know that SpringAOP is implemented by dynamic proxy. In the dynamic proxy, there are two implementation methods: JDK dynamic proxy and CGLIB dynamic proxy! If the proxy class does not implement the InvocationHandler interface in the JDK dynamic proxy, then SpringAOP will choose to use CGLIB to dynamically proxy the target class! By default @EnableAspectJAutoProxy does not specify proxyTargetClass = true (the default is not written as false), and our business class implements the InvocationHandler interface interface, then AOP will use the JDK dynamic proxy, if it does not implement the InvocationHandler interface, use the CGLIB proxy, if @EnableAspectJAutoProxy has Specify proxyTargetClass = true, then it is mandatory to use CGLIB, regardless of whether the business class implements the InvocationHandler interface or not, as well as reference enhancement (method B is called in method A) if exposeProxy=true is specified in the @EnableAspectJAutoProxy annotation (default is false), Then the proxy object will be exposed to the thread variable, so when you call, take the proxy object out of the thread variable and call it through the proxy object, then the A method is enhanced, and the B method is also enhanced! Similar to transaction A method calls transaction method B

  • JDK dynamic proxy only provides interface proxy and does not support class proxy. The core is the Proxy class. InvocationHandle calls the code in the target class through the invoke() method reflection, dynamically weaving the cross-cutting logic and business together; then Proxy uses InvocationHandle dynamically creates an instance that conforms to a certain interface to generate a proxy object of the target class

  • CGLIB (Code Generation Library) is a class library for code generation, which can dynamically generate a subclass object of a specified class at runtime, and override specific methods and add enhanced code to achieve AOP. CGLIB is a dynamic proxy through an integrated way, so if a class is marked as final, then CGLIB cannot be used as a dynamic proxy.
    InvocationHandler

AOP terminology

Joinpoint

Is a method that needs to be enhanced

PointCut

A set of connection points is called a tangent point

Aspect

It is a class consisting of cut points, connection points, and notifications.

Advice

  • Pre-notification (before): do some operations before executing the business code, such as obtaining the connection object
  • After notification (after): do something after executing the business code, it will be executed regardless of whether an exception occurs, such as closing the connection object
  • Exception notification (afterThrowing): The operation that needs to be done when an exception occurs after the execution of the business code, such as rolling back the transaction
  • Return notification (afterReturning), the operation that will be performed without exception after the execution of the business code
  • Around notification (around), there is no corresponding operation for the transaction we are currently talking about, so I won’t talk about it for the time being

Target
business objects that need to be strengthened

Weaving

Weaving is the process of adding enhancements to specific connection points to the target class. Weaving is a vivid term. Specifically, it is the process of generating proxy objects and integrating aspect content into business processes.

Proxy

After a class is woven and enhanced by AOP, a proxy class is created.

SpringAOP example code

target

@Service
public class A {
    
    
    public void f(){
    
    
        System.out.println("我被执行了");
    }
}

section

@Component
@Aspect
public class MyAspect {
    
    
	
	//切点
    @Pointcut("execution(* com.xxx.xxx..*(..))")
    private void anyOldTransfcr(){
    
    }
	//com.xxx.xxx..*(..))这个中描述的每个方法为织入点

	//通知
    @Before("anyOldTransfcr()")
    public void advice(JoinPoint joinPoint){
    
    
        //String methodName=joinPoint.getSignature().getName();
        //System.out.println("执行的方法--->"+methodName);
        //System.out.println(Arrays.asList(joinPoint.getArgs()));
        System.out.println("--------开始执行-------");
    }

	//通知
    @After("anyOldTransfcr()")
    public void advice2(){
    
    
        System.out.println("--------结束执行-------");
    }

}

Main start class

@EnableAspectJAutoProxy(proxyTargetClass = true)

补充:这里在切面中有个@Aspect这个注解,和上面提到的AspectJ静态代理其实是有点关系的,因为Spring在刚开始实现AOP的时候写的语法是不太友好的,然后Spring后面在迭代的过程中实现AOP就参照了AspectJ的语法,注意是语法!

Run results after startup
Insert picture description here

Analysis of SpringAOP source code implementation process

Here you need to go through the entire process of SpringIOC. To put it bluntly, SpringAOP is a function implemented by a certain part of IOC! For the SpringIOC process, please see Spring source code analysis (process combing). In this article, there is the following picture.
Insert picture description here
The process of AOP creation of proxy objects can be seen in the above picture. It can be seen that it is implemented in the process of Bean creation. The AOP process is divided into the following;

  1. Facet lookup
  2. Cache aspect notification
  3. Determine whether the current bean belongs to the weaving point through the pointcut expression
  4. Is the weaving point to create a proxy object
  5. Execution target method

1. Sectional search

Every time a Bean is created, it will enter the createBean method in the AbstractAutowireCapableBeanFactory class. There is a very key code about AOP in this method;
Insert picture description here

		try {
    
    
			// 给BeanPostProcessors一个返回代理而不是目标bean实例的机会
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
    
    
				return bean;
			}
		}

After entering this line of code,
Insert picture description here
continue to enter
Insert picture description here
here is particularly important, when we enable AOP function annotation, @EnableAspectJAutoProxy(proxyTargetClass = true)

This annotation will help us to Import an AspectJAutoProxyRegistrar class.
Insert picture description here
Enter the AspectJAutoProxyRegistrar class. This class implements the ImportBeanDefinitionRegistrar interface and rewrites the registerBeanDefinitions. So here is definitely to help us register the Bean, and enter this method to
Insert picture description here
Insert picture description here
register an AnnotationAwareAspectJAutoProxyCreator. Yes, this bean is very important!

Back to our AOP process code

Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);

This line of code enters the postProcessBeforeInstantiation in the AbstractAutoProxyCreator class when ibp is the AnnotationAwareAspectJAutoProxyCreator injected above, because the top level of AnnotationAwareAspectJAutoProxyCreator inherits the AbstractAutoProxyCreator, so you can call the postProcessBeforeInstantiation method in the AbstractAutoProxyCreator class.
Insert picture description here
This method shouldSkip(beanClass, beanName) will call the shouldSkip method in the AspectJAwareAdvisorAutoProxyCreator class. The
Insert picture description here
Insert picture description here
call here will go to buildAspectJAdvisors in the BeanFactoryAspectJAdvisorsBuilder class. This method is to find the aspect class and then return the notification type set declared in the aspect class.

2. Cache aspect notification

List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

In this line of code, a notification type object collection is generated, and the aspect class is used as the key, and the notification object collection is stored as the value of the current aspect class as the key!
Insert picture description here
In this step, the process of obtaining the aspect class and generating the notification object according to the notification in the aspect class is time-consuming, so the parsed notification object is added to the cache. When the subsequent creation of the Bean comes here, it will first determine whether the aspectBeanNames is empty. If it is empty, it will directly fetch the data in the cache and return

According to the notification type in the aspect class, the
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); line of code that generates the notification object is used as the entry, and finally enters the getAdvice method in the ReflectiveAspectJAdvisorFactory class
Insert picture description here

Back to the main process, then the main process has found the aspect class and the notification object has been cached here, then start the original way and return to the
Insert picture description here
main process and start to enter the process of creating Bean;
Insert picture description here
we skip the process of attribute assignment. look, and AOP does not matter much, directly into the Bean initialization process
Insert picture description here
after initialization is complete will perform
Insert picture description here
Insert picture description here
here, there is a key, opened here after @EnableAspectJAutoProxy comment AOP above mentioned BeanPostProcessor will be more of a AnnotationAwareAspectJAutoProxyCreator
Insert picture description here
when the processor here When it is the AnnotationAwareAspectJAutoProxyCreator object, enter the postProcessAfterInitialization method in the AbstractAutoProxyCreator class.
Insert picture description here
Insert picture description here
This line of code will go to 1. Find the aspect class, 2. Cache the notification object in the aspect class, but only do this operation, but the first time the shouldSkip method is called is already After completing the two operations of 1. Find the aspect class and 2. Cache the notification object in the aspect class, then the logic will not be executed again here, but after entering the buildAspectJAdvisors() method, it will return directly to the first execution of buildAspectJAdvisors() The result set of the notification object cached by the method!

3. Determine whether the current bean belongs to the weaving point through the pointcut expression

Insert picture description here
If the returned value is null, it means that the bean that needs to be initialized does not need to create a proxy object. If the return is not null, then start to create a proxy object

4. Is the weaving point to create a proxy object

Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Use CGLIB to generate proxy objects TheInsert picture description here
introduction of Spring AOP at the beginning of the article explains why CGLIB is used to complete dynamic proxy!

5. Implementation of the target method

Insert picture description here
When the f method is called, it is the f method that enters the proxy object.
Insert picture description here
Since the CGLIB is used to complete the dynamic proxy, the execution is also completed by the supporting proxy object executor. The CGLIB is performed by the CglibAopProxy and the JDK is performed by the JdkDynamicAopProxy! In fact, it is finally executed through the proceed() method in the ReflectiveMethodInvocation class!
Insert picture description here

//当前拦截器的的索引
this.currentInterceptorIndex

//拦截器集合
this.interceptorsAndDynamicMethodMatchers

//当拦截器索引等于拦截器集合数组长度-1时,执行目标方法
this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1

//执行目标方法
return invokeJoinpoint();

//完成当前拦截器获取和当前拦截器索引++操作
Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

//使用当前索引获取到的拦截器执行invoke方法逻辑
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

这里在invoke方法中传入的是this就是为了递归循环调用proceed()方法自己

Insert picture description here
Each recursion actually adds a logical code block to the code block and then executes it. The complete recursive skeleton code is as above. Execute from top to bottom!

  1. AOP Aronud before…
  2. AOP Before Advice…
  3. The target method is executed...
  4. AOP Aronud after…
  5. AOP After Advice…
  6. AOP AfterReturning Advice: (If the normal execution is completed, execute the current method)
  7. AOP AfterThrowing Advice... (If an exception is thrown, the current method is executed)

This call flow is completed by the chain of responsibility + recursion

Guess you like

Origin blog.csdn.net/CSDN877425287/article/details/114328007