@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种情况. 这样我们就得出了以下这个结论图;
不觉得奇怪吗? 为什么方法会在这里内部插进去? 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);
}
}
}
以下是 执行对象 执行过程:
- 在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor
- 代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行 匹配筛选
- 把和方法所匹配的Advisor适配成MethodInterceptor
- 把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前 Method对象、方法参数封装为MethodInvocation对象
- 调用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的的更深 更细致的同学 可以一起来讨论 一起学习 共同努力 为祖国出一份自己的力量;