AOP流程及原理
目录
一、AOP结构介绍
- 
@Pointcut 
- 
通知 
- 
原理 
- 
连接点 
- 
拦截器 
二、Bean介入点
- 
EnableAspectJAutoProxy 
- 
AspectJAutoProxyRegistrar 
- 
AnnotationAwareAspectJAutoProxyCreator 
- 
AbstractAutoProxyCreator - 
实例前执行 
- 
初始化后执行 
- 
循环依赖会调用 
 
- 
- 
总结 
三、处理切面
- 
获取所有切面其下通知方法 - 
获取切面 
- 
获取切面下的通知方法 
- 
通知方法的封装 
 
- 
- 
通知方法与Bean匹配 
- 
总结 
四、创建代理对象
五、代理执行方法
六、总结
一、AOP结构介绍
我们先看个简单的AOP例子:

结果:

我们来细数一下有哪些要素 ?
- 
@Aspect:切面类,告诉Spring我这个类是个切面,里面有特殊处理方法 
- 
@Pointcut:切点,告诉Spring我要针对那些业务方法进行增强 
- 
@Before、@Around、@AfterReturning、@After、@AfterThrowing:通知,告诉Spring针对后要做什么处理 
要素就这些吧,@Aspect就不说了就是个标识,主要是切点和处理方法吧
@Pointcut
这个注解值的格式是:表达标签 (表达式格式),用白话说就是用了一种表达式来代表我要针对什么来进行特殊处理
- 
execution:用于匹配方法执行的连接点 
- 
within:用于匹配指定类型内的方法执行 
- 
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配 
- 
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配 
- 
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法 
- 
@within:用于匹配所以持有指定注解类型内的方法 
- 
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解 
- 
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行 
- 
@annotation:用于匹配当前执行方法持有指定注解的方法 
- 
bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法 
通知
我们上述看到了有五种通知注解,分别表示如下,表示有五种特殊处理方式:
- 
@Before: 前置通知,在目标方法执行前执行 
- 
@Around: 环绕通知,可以在目标方法前、后进行处理,还可以修改目标方法返回值 
- 
@AfterReturning: 后置通知,在目标方法后执行(发生异常便不会执行) 
- 
@After: 最终通知,不管异常还是正常一定都会执行 
- 
@AfterThrowing:异常通知,在目标方法发生异常后执行 
原理
一提起AOP可能第一反应就是动态代理,但是真的就只有动态代理这么简单吗?我们看一个动态代理的例子(以JDK动态代理为例):

这乍一看好像就是这个道理啊,好像全满足了呀,真满足吗?环绕通知要怎么做?通知有多个,有多个处理方法怎么做?总不可能一直往这里面塞吧,还有环绕通知需要在invoke方法外面再套一层吧,有多个的话无限套娃?
那要怎么做?注意看这是不是都是串行执行的,串行执行的拦截处理方法是什么?拦截器链!!
流程如下图所示:

注意看所有通知都是多个:
- 
无环绕,无异常的情况下:所有前置通知 --> 目标方法 --> 所有后置通知 --> 所有最终通知 --> 返回 
- 
无环绕,有异常的情况下:所有前置通知 --> 目标方法 --> 所有异常通知 --> 所有最终通知 --> 返回(这里注意前置、目标、后置任何一个异常都会到异常通知) 
- 
有环绕的情况下:先执行环绕前置 --> 再执行链条 --> 然后环绕后置(如下图) 

多个环绕会怎样?注意环绕通知本身就是链条的里面的,只不过在最前面执行,多个环绕就会像这样:

好了重点来了,我们知道原理了动态代理+拦截器链,我们需要知道Spring怎么帮我们组装的?
- 
动态代理很简单就两种方式:JDK 和 Cglib 
- 
拦截器链:是不是需要把上述切面里面的方法全提取出来封装好,然后最后组装成链条 
- 
连接点:拦截器通过什么连接到一起?需要相同的连接点吧,所谓连接点是指那些被拦截到的方法 
连接点
在Spring里面连接点是Joinpoint这个接口:

如上图可见就两个实现类:
- 
ReflectiveMethodInvocation:提供给JDK动态代理方式使用 
- 
CglibMethodInvocation:提供给Cglib动态代理方式使用 
二、拦截器
既然知道是拦截器链了,那每个通知方法应该都有对应的拦截器,我们去看看(只看invoke方法哈):
1、前置通知拦截器 MethodBeforeAdviceInterceptor:

2、后置通知拦截器AfterReturningAdviceInterceptor:

3、异常通知拦截器ThrowsAdviceInterceptor :

4、最终通知AspectJAfterAdvice :

5、环绕通知AspectJAroundAdvice :

以上就是关于通知链条里面所有最后会执行的方法,可以看到共同点就是invoke方法的传参MethodInvocation ,这不就是我们之前说的连接点嘛,
当然还有很多内置的其他拦截器,但这都跟我们AOP拦截器没关系
以上基础概念相信大家都懂了,接下来我们看看Spring是怎么代理一个Bean的,是怎么为这个Bean组装这些拦截器的
三、Bean介入点
这AOP代理到底是在Bean生成流程中哪个地方介入进来为我们生成代理对象的 ?
从AOP配置加载点一看便知,开启AOP的配置注解是 @EnableAspectJAutoProxy(现在已经默认开启了,不需要加注解也行,配置类是AopAutoConfiguration)
1、EnableAspectJAutoProxy
@EnableAspectJAutoProxy注解内部导入了一个类AspectJAutoProxyRegistrar

2、AspectJAutoProxyRegistrar
这个类实现了ImportBeanDefinitionRegistrar接口,这个接口之前说过了,可以注册bean的定义信息BeanDefination,所以我们要看看注册的这个是什么?干了什么?

沿着那个方法一路往下,发现注册了AnnotationAwareAspectJAutoProxyCreator

3、AnnotationAwareAspectJAutoProxyCreator
这个类可谓是最重要的类了,从下方的类图上看,它实现了很多接口,还有我们非常熟悉的后置处理器,在这里面主要实现了4个方法:
- 
setBeanFactory:实例化后,初始化前调用 
- 
getEarlyBeanReference:和三级缓存有关,存在循环依赖里面会调用 
- 
postProcessBeforeInstantiation:实例化前执行 
- 
postProcessAfterInitialization:初始化后执行 
别看有4个方法,其实下面三个方法内部都会调用一样的方法,只是需要注意在Bean生成流程中的介入点

4、AbstractAutoProxyCreator
在目标对象创建之前,执行 AbstractAutowireCapableBeanFactory#Object bean = resolveBeforeInstantiation(beanName, mbdToUse) --> applyBeanPostProcessorsBeforeInstantiation(targetType, beanName) ---> Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName),主要是判断代理目标对象是否已经存在,存在了且需要代理就走getAdvicesAndAdvisorsForBean方法,然后调用createProxy()方法创建代理对象

5、目标对象创建完且初始化后执行
目标对象创建完且初始化后执行,AbstractAutowireCapableBeanFactory#initializeBean --> wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
--> result = beanProcessor.postProcessAfterInitialization(result, beanName),会调用wrapIfNecessary()方法

wrapIfNecessary()方法也会调用 getAdvicesAndAdvisorsForBean方法来获取对应的通知处理,如果没获取到通知处理方法说明不需要代理,获取到了就要创建代理对象了createProxy()
注意: 这里的通知处理就是切面里面的通知方法,getAdvicesAndAdvisorsForBean就是获取所有的切面类里面的切点与Bean来匹配,匹配上了说明这个Bean要被代理,同时会封装匹配的切点对应的所有通知返回

6、循环依赖会调用
getEarlyBeanReference,三级缓存,存在循环依赖则会调用,这里put进去代表已经生成代理了,所以后续初始化后调用的时候会get判断一次,也会调用wrapIfNecessary()方法

7、总结
所以会在Bean实例化前、循环依赖、初始化后介入处理,当然只会处理一次,最终都会调用getAdvicesAndAdvisorsForBean方法来对Bean进行切点匹配,匹配上了就调用createProxy方法生成代理对象然后返回
四、处理切面
AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean()作用:
会先获取所有的切面其下的通知方法(@Before、@Around、@AfterReturning、@After等),然后根据切点表达式去和这个Bean对象匹配,
将匹配成功的通知方法返回,这就说明该Bean需要被代理,匹配成功的通知方法排序后就是需要执行的方法调用链

1、获取所有切面其下通知方法
使用了模板方法模式,获取切面,AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors 有个父类的方法是获取一些实现了Advisor接口的Bean,我们重点关注被@Aspect注解标识的Bean的处理

BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors
会遍历所有的Bean找到其中被注解@Aspect标识的,然后去处理其下的切点和通知方法

2、获取@Aspect切面下的通知方法
ReflectiveAspectJAdvisorFactory.getAdvisors
遍历切面下的所有方法,去找方法上是否有相应的注解,如果有则需要封装处理

ReflectiveAspectJAdvisorFactory.getAdvisor
遍历我需要的注解,在方法上找注解是否存在,存在的就需要封装处理

3、通知方法的封装
InstantiationModelAwarePointcutAdvisorImpl
这个在构造里面就会对通知方法进行处理封装
  public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
  			MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
  		Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
  		validate(candidateAspectClass);
  		AspectJAnnotation<?> aspectJAnnotation =
  				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
  		if (aspectJAnnotation == null) {
  			return null;
  		}
  		// If we get here, we know we have an AspectJ method.
  		// Check that it's an AspectJ-annotated class
  		if (!isAspect(candidateAspectClass)) {
  			throw new AopConfigException("Advice must be declared inside an aspect type: " +
  					"Offending method '" + candidateAdviceMethod + "' in class [" +
  					candidateAspectClass.getName() + "]");
  		}
  		if (logger.isDebugEnabled()) {
  			logger.debug("Found AspectJ method: " + candidateAdviceMethod);
  		}
  		AbstractAspectJAdvice springAdvice;
  		switch (aspectJAnnotation.getAnnotationType()) {
  			case AtBefore:
  				springAdvice = new AspectJMethodBeforeAdvice(
  						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
  				break;
  			case AtAfter:
  				springAdvice = new AspectJAfterAdvice(
  						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
  				break;
  			case AtAfterReturning:
  				springAdvice = new AspectJAfterReturningAdvice(
  						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
  				AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
  				if (StringUtils.hasText(afterReturningAnnotation.returning())) {
  					springAdvice.setReturningName(afterReturningAnnotation.returning());
  				}
  				break;
  			case AtAfterThrowing:
  				springAdvice = new AspectJAfterThrowingAdvice(
  						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
  				AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
  				if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
  					springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
  				}
  				break;
  			case AtAround:
  				springAdvice = new AspectJAroundAdvice(
  						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
  				break;
  			case AtPointcut:
  				if (logger.isDebugEnabled()) {
  					logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
  				}
  				return null;
  			default:
  				throw new UnsupportedOperationException(
  						"Unsupported advice type on method: " + candidateAdviceMethod);
  		}
  		// Now to configure the advice...
  		springAdvice.setAspectName(aspectName);
  		springAdvice.setDeclarationOrder(declarationOrder);
  		String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
  		if (argNames != null) {
  			springAdvice.setArgumentNamesFromStringArray(argNames);
  		}
  		springAdvice.calculateArgumentBindings();
  		return springAdvice;
  }
4、通知方法与 Bean匹配
AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply

5、总结
所以这一步会找到所有的切面,遍历其下的所有切点和通知方法,然后根据切点中的表达式去与Bean对象匹配,获取所有匹配成功的通知方法,
将这些通知方法排序后就是最后的方法执行链,同时也说明该Bean需要被代理,所以需要创建代理对象
五、创建代理对象
AbstractAutoProxyCreator.createProxy
这里实际就是在创建代理对象前填充一下必要信息,然后创建代理对象,默认是采用JDK动态代理,如果被代理的目标对象不是接口,则会采用Cglib动态代理
- 
CglibAopProxy:Cglib动态代理逻辑类 
- 
JdkDynamicAopProxy:Jdk动态代理逻辑类(我们以这个为例) 

JdkDynamicAopProxy.getProxy
这一步很简单就是直接创建代理对象,处理类是this,说明该类本身就是处理类

六、代理执行方法
我们以JDK动态代理为例,最终代理对象在执行方法的时候就会调用该方法:
JdkDynamicAopProxy.invoke

六、总结
1、AOP代理对象的生成机会是在Bean实例化前、初始化后这两个位置判断生成的(以初始化后为主,其他阶段属于特殊阶段)
2、通过获取所有的切面下的通知方法以切点表达式来与Bean匹配,来判断该Bean是否需要被代理,同时准备好了与该Bean相关的所有增强方法
3、AOP默认采用JDK动态代理的方式,如果被代理目标对象不是接口,则会采用Cglib的代理方法
4、AOP的底层原理虽然是动态代理,但是我觉得最重要的还是执行的方法调用链非常巧妙
5、在逻辑实现上:每种通知在调用链上执行的方式及其执行顺序决定了其扮演的角色
6、每个通知最后执行类在前面已经给出,可直接查看学习
最后附上个执行结构图

 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号