Principles of Spring (15) -- implementation of Spring AOP source code

Keywords: Java Spring Back-end

@TOC #Spring collection
Record every step of the program___ auth:huf

Yesterday, careful students have found a problem; In Spring 5.X or SpringBoot 2.x, the CGLIB interface is used regardless of whether the class implements the interface In Spring 5.X, Spring uses the CGLIB interface by default. In Spring 4, the JDK proxy is used, but after 5.X, we can find an AOP autoconfiguration in the process of AOP automatic loading. The following is the source code


The reasons are as follows:

We should use @ enable transaction management (proxyTargetClass=true) to prevent serious proxy problems when people don't use the interface.
for example

"scene: StudentService Is an interface StudentServiceImpl Is its implementation class"
	"How to use the interface"
    @Autowired
    StudentService studentService;
	"Not using interfaces"
    @Autowired
    StudentServiceImpl studentService;

If the JDK proxy is used at this time, if the declared type is StudentServiceImpl, if the JDK proxy is used, it will appear
Abnormal;

The bean 'studentServiceImpl' could not be injected as a 'com.huf.service.impl.studentServiceImpl' because it is a JDK dynamic proxy that implements 
In this chapter, we will expand the whole AOP proxy class; Use AspectJ to thoroughly explain the application and execution sequence of annotations; And the implementation process of the responsibility chain model used by AOP; Immersive learning Spring AOP will be understood thoroughly First, we introduce Maven
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

Any other package and any action can be omitted. AOP is enabled by default;
Define an AspectJ class, and we'll go through the basics by the way; Then start to introduce the specific implementation process in depth;

package com.huf.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
 * auth:huf
 */
@Aspect
@Component
public class HufAop {
	"Represents a tangent point; This method does not enter;Just a tangent point;"
    @Pointcut("execution(public * com.huf.service.impl.*.*(..))")
    public void testPoincut(){}
    "Execute before executing the target object method;But after the surround notification"
    @Before("testPoincut()") 
    public void testBefore(){
        System.out.println("Aop testBefore------------>");
    }
    "The front of the target method around the notification execution(first place)  Surround is also the last execution;"
    @Around("testPoincut()")
    public void testAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Before surround notification------------>");
        proceedingJoinPoint.proceed();
        System.out.println("After surround notification------------>");
    }
    "After executing the target method;Before wrapping"
    @After("testPoincut()")
    public void testAfter(){
        System.out.println("Aop testAfter------------>");
    }
    "Execute after target method returning Then execute after "
    @AfterReturning("testPoincut()")
    public void testAfterReturning(){
        System.out.println("Aop testAfterReturning------------>");
    }
    "Execute after the target method throws an exception.After that, no wrapping will be performed. Direct execution AfterThrowing Then execute after"
    @AfterThrowing("testPoincut()")
    public void testAfterThrowing(){
        System.out.println("Aop testAfterThrowing------------>");
    }
}

Here we have a summary: the following is the result of my operation: the operation result without error

Operation result of error report:

So we come to a conclusion;

Around

The surround notification must be executed first before the notification. If the target method does not report an error, it must be executed last after the notification;

Before

Before executing the target object method, the method is after Around; Before the target object method;

target

proceedingJoinPoint.proceed(); The proxy method is always executed after the Around and Before methods;

Afterreturning (I)

It can be seen from the name that afterreturning is executed after the proxy method as long as there is no exception in the method;

Afterthrowing (II)

It can be seen from the name that as long as the method throws an exception, the method will be executed; At the same time, only one of AfterReturning and AfterThrowing will be executed, because there are only two situations when the agent method is executed. The first is that the agent method is completely executed. The second is that the exception is thrown in the middle. After the exception is thrown, the after returning and Around will not be executed after the bypass is changed;

After

After will be executed regardless of whether the target method has an exception;

Around (III)

Execution after notification;

Conclusion: under normal circumstances, as long as < 1 > occurs, then < 3 > must be executed, as long as 2 > occurs, then < 1 3 > must not be executed;

Although there will be problems with memory. If you drill a bull's horn, there will be N cases. In this way, we come to the following conclusion figure;

Isn't it strange? Why does the method plug in here? Why does the Before method execute Before the wrap notification?
The createProxy() method in AbstractAutoProxyCreator is called after it initializes the Bean and before it is converted to a proxy object
In this method, a new ProxyFactory is created. The following is the source code

	"Create proxy object"
	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {
		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}
		"Create proxy object factory"
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);
			
		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
		"adopt class Find all matching names Advisor"
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		"hold Advisor Put it in proxFactory inside"
		proxyFactory.addAdvisors(advisors);
		"Put the delegate object into TargetSource inside(Original object)"
		proxyFactory.setTargetSource(targetSource);
		
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		"If bean If the class is not loaded locally in the override class loader, the original class loader is used"
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
		"adopt DefaultAopProxyFactory of createAopProxy Automatic selection and use JDK Agent or CGLIB agent;"
		return proxyFactory.getProxy(classLoader);
	}

This is clear. Before creating the proxy, we first do a very important action, that is, find all the Advice related to the proxy method and put them into the newly created ProxyFactory;

Finally, the proxy object is initialized;

We're doing it
/**
 * auth:huf
 */
@EnableAspectJAutoProxy(proxyTargetClass=true)
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
public class TestMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(TestMain.class);
        context.refresh();
        StudentService studentService = (StudentService) context.getBean("studentService");
        "When we execute this line of code, we will enter DynamicAdvisedInterceptor intercept Method"
        studentService.insert(new Student());
    }
}

Click the proxy object to execute this method in IDEA

You will enter the source code. The following is the process of executing the source code:

/**
	 * General purpose AOP callback. Used when the target is dynamic or when the
	 * proxy is not frozen.
	 */
	private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

		private final AdvisedSupport advised;

		public DynamicAdvisedInterceptor(AdvisedSupport advised) {
			this.advised = advised;
		}

		@Override
		@Nullable
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			boolean setProxyContext = false;
			Object target = null;
			TargetSource targetSource = this.advised.getTargetSource();
			try {
				if (this.advised.exposeProxy) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
				"You got the original object"
				target = targetSource.getTarget();
				Class<?> targetClass = (target != null ? target.getClass() : null);
				"Pass in the current proxy method poincut Got one Advice One of List"
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
					retVal = methodProxy.invoke(target, argsToUse);
				}
				else {
					"Then in CglibMethodInvocation  Execute all Advice"
					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null && !targetSource.isStatic()) {
					targetSource.releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

The following is the execution process of the execution object:

  1. Before using ProxyFactory to create proxy objects, you need to add Advisor to ProxyFactory
  2. When a proxy object executes a method, it will take out the Advisor in ProxyFactory to match and filter the currently executing method
  3. Adapt the Advisor matched with the method to the MethodInterceptor
  4. Encapsulate the MethodInterceptor chain matching the current Method, as well as the proxy object, proxy object, proxy class, current Method object and Method parameters as MethodInvocation object
  5. Call the processed () method of MethodInvocation and start to execute the corresponding methods of each MethodInterceptor and the proxy object. 6. Call the invoke() method of each MethodInterceptor in sequence and pass the MethodInvocation object into the invoke() method. 7. Call the invokeJoinpoint() method until the last MethodInterceptor is executed, This executes the current method of the proxied object
This will solve the doubt! [insert picture description here]( https://img-blog.csdnimg.cn/2c7469ce7aa44ddfa382e4f878c2a20c.png?x -OSS process = image / watermark, type_zhjvawrzyw5zmfsbgjhy2s, shadow_50, text_q1netiba5zac5qyi57yw56cb55qe6icb6ioh, size_20, color_ffffff, t_70, g_se, x_16) through processingjoinpoint. Processed(); After executing all other Advice matching the method, execute the last sentence


The final printing result is:

summary

1. Find out why spring 5. X and springboot 2. X use CGLIB proxy by default;
2. Explain the implementation principle of AOP from shallow to deep
3. Complete the explanation from [around], [before], [target], [afterreturning], [afterthrowing] and [after]; Execution sequence;

AOP here, the author has described everything he knows in words. If there are deeper and more detailed students about AOP, they can discuss and study together and work together to contribute their own strength to the motherland;

seeyou

Posted by mlavwilson on Wed, 03 Nov 2021 19:51:54 -0700