Spring原理篇(15)--Spring AOP源码的实现<AOP终>

@TOC# Spring系列
记录在程序走的每一步___auth:huf


昨天细心的同学已经发现一个问题; 就是在Spring5.X 版本 或者是SpringBoot 2.x版本 不管类是否实现了接口 都是使用的CGLIB 接口;. 在Spring5.X 版本 Spring默认就是使用CGLIB接口 在Spring 4开头的版本 使用的是JDK代理 但是到了5.X以后 我们可以在 AOP自动装载过程中发现一个 AopAutoConfiguration 以下是源码

在这里插入图片描述
原因如下:
在这里插入图片描述
翻译过来就是 :我们应该使用@EnableTransactionManagement(proxyTargetClass=true)来防止人们不使用接口时出现严重的代理问题。
例如

"场景: StudentService是一个接口 StudentServiceImpl是其实现类"
	"使用接口的方式"
    @Autowired
    StudentService studentService;
	"不使用接口的方式"
    @Autowired
    StudentServiceImpl studentService;

如果这时候用了JDK代理 如果声明的类型是 StudentServiceImpl 如果使用JDK代理的话. 那么就会出现
异常;

The bean 'studentServiceImpl' could not be injected as a 'com.huf.service.impl.studentServiceImpl' because it is a JDK dynamic proxy that implements 

在本章节中.我们将会展开整个AOP 使用代理类; 使用AspectJ 彻底讲解注解的应用. 执行顺序; 以及AOP使用的责任链模式执行过程;沉浸式学习 Spring AOP 将会理解的透彻.
首先 我们Maven 引入
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

其他任何包 任何动作都可以不用做了. 默认开启了AOP;
定义个AspectJ 类 我们顺便顺一下基础知识; 然后开始往深处介绍里面具体的执行流程;

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 {
    
    
	"代表着一个切点; 该方法不会进入;仅仅是一个切点;"
    @Pointcut("execution(public * com.huf.service.impl.*.*(..))")
    public void testPoincut(){
    
    }
    "执行目标对象方法之前执行;但是位于环绕通知之后"
    @Before("testPoincut()") 
    public void testBefore(){
    
    
        System.out.println("Aop testBefore------------>");
    }
    "环绕通知 执行目标方法的 最前面(第一位)  环绕后也是也是最后一个执行;"
    @Around("testPoincut()")
    public void testAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    
    
        System.out.println("环绕通知前------------>");
        proceedingJoinPoint.proceed();
        System.out.println("环绕通知后------------>");
    }
    "执行目标方法之后;在环绕后之前"
    @After("testPoincut()")
    public void testAfter(){
    
    
        System.out.println("Aop testAfter------------>");
    }
    "在目标方法之后执行 returning 然后执行after "
    @AfterReturning("testPoincut()")
    public void testAfterReturning(){
    
    
        System.out.println("Aop testAfterReturning------------>");
    }
    "在目标方法抛出异常之后执行.然后不会执行环绕后. 直接执行AfterThrowing 然后执行after"
    @AfterThrowing("testPoincut()")
    public void testAfterThrowing(){
    
    
        System.out.println("Aop testAfterThrowing------------>");
    }
}

这里我们就有总结: 以下是我运行的结果 :不报错的运行结果
在这里插入图片描述

报错的运行结果:
在这里插入图片描述


因此 我们得出一个结论;

Around

环绕通知 通知前 一定是最先执行的 在目标方法不报错的情况下 通知后一定是最后执行的;

Before

目标对象方法前执行 该方法处于Around 的之后; 目标对象方法之前;

target

proceedingJoinPoint.proceed(); 被代理方法永远在Around 与Before 方法之后执行;

AfterReturning(一)

从名字就可以看出来 只要方法不出异常. 那么 AfterReurning 在代理方法之后执行;

AfterThrowing(二)

从名字就可以看出来 只要方法抛出异常 那么就会执行该方法;同时 AfterReturning 与 AfterThrowing 只有一个会执行. 因为代理方法执行下来只会有两种情况 第一种情况就是完全执行完毕 第二种就是 中途抛了异常 抛了异常之后 AfterReturning 与 Around的换绕后 都不会执行;

After

不管目标方法是否出现异常 都会执行After;

Around (三)

环绕通知之后执行;

总结: 正常情况下:只要出现<一> 那么<三>一定执行 只要出现二 那么<一三> 一定不会执行;

尽管这样记忆 会有问题所在. 如果钻牛角尖的 那么就会有N种情况. 这样我们就得出了以下这个结论图;

Around环绕通知前
Before
代理对象target
AfterReturning
AfterThrowing
After
Around环绕通知后
一样是After
错误异常

不觉得奇怪吗? 为什么方法会在这里内部插进去? Before方法为什么会在环绕通知之前执行?
在这里插入图片描述在它初始化Bean之后 转换为代理对象之前 会调用 AbstractAutoProxyCreator 中的 createProxy()方法
在该方法中 创建了一个新的ProxyFactory 以下是源码

	"创建代理对象"
	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);
		}
		"创建代理对象工厂"
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);
			
		if (!proxyFactory.isProxyTargetClass()) {
    
    
			if (shouldProxyTargetClass(beanClass, beanName)) {
    
    
				proxyFactory.setProxyTargetClass(true);
			}
			else {
    
    
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
		"通过class名称寻找到 所有相匹配的Advisor"
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		"把Advisor放进去 proxFactory 里面"
		proxyFactory.addAdvisors(advisors);
		"把被代理对象放到TargetSource 里面(原对象)"
		proxyFactory.setTargetSource(targetSource);
		
		customizeProxyFactory(proxyFactory);

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

		"如果bean类未在重写类加载器中本地加载,则使用原始类加载器"
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
    
    
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
		"通过DefaultAopProxyFactory 的 createAopProxy 自动选择使用JDK代理 还是CGLIB代理;"
		return proxyFactory.getProxy(classLoader);
	}

这里就清晰了. 我们首先创建代理之前做了一个非常重要的动作 就是找到所有 与 被代理方法相关的 Advice并且把它们放到了新创建的ProxyFactory里边;
在这里插入图片描述
最后初始化出来了代理对象;


我们在执行
/**
 * 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");
        "在执行这行代码的时候 我们就会进入 DynamicAdvisedInterceptor intercept 方法中"
        studentService.insert(new Student());
    }
}

该处源码 拿到代理对象 执行这个方法 在IDEA 点击
在这里插入图片描述
就会进入到源码中 以下是执行源码过程:

/**
	 * 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;
				}
				"拿到的是 原对象"
				target = targetSource.getTarget();
				Class<?> targetClass = (target != null ? target.getClass() : null);
				"把目前代理方法 传入 通过poincut 得到了一个 Advice 的一个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 {
    
    
					"然后在 CglibMethodInvocation  执行所有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);
				}
			}
		}

以下是 执行对象 执行过程:

  1. 在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor
  2. 代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行 匹配筛选
  3. 把和方法所匹配的Advisor适配成MethodInterceptor
  4. 把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前 Method对象、方法参数封装为MethodInvocation对象
  5. 调用MethodInvocation的proceed()方法,开始执行各个MethodInterceptor以及被代理对象 的对应方法 6. 按顺序调用每个MethodInterceptor的invoke()方法,并且会把MethodInvocation对象传入 invoke()方法 7. 直到执行完最后一个MethodInterceptor了,就会调用invokeJoinpoint()方法,从而执行被代 理对象的当前方法

这就解开疑惑了. ![在这里插入图片描述](https://img-blog.csdnimg.cn/2c7469ce7aa44ddfa382e4f878c2a20c.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5Zac5qyi57yW56CB55qE6ICB6IOh,size_20,color_FFFFFF,t_70,g_se,x_16) 通过 proceedingJoinPoint.proceed(); 执行完 与方法匹配的所有的其他Advice后.执行最后一句话.

在这里插入图片描述
最后打印结果为:
在这里插入图片描述


总结

1.查明Spring 5.x SpringBoot 2.X 为什么默认使用CGLIB代理原因;
2.由浅到深 讲明了AOP的实现原理.
3.从 [Around],[Before],[target],[AfterReturning],[AfterThrowing],[After] 全部讲解完;执行顺序;

AOP 在这里 作者 就把自己知道的 就已经全部通过文字的方式描述出来了. 如果说有关于AOP的的更深 更细致的同学 可以一起来讨论 一起学习 共同努力 为祖国出一份自己的力量;

seeyou

猜你喜欢

转载自blog.csdn.net/wenaicoo/article/details/121131346