AOP的一切要从 @EnableAspectJAutoProxy说起。
它通过 @Import注解AspectJAutoProxyRegistrar类,这个类是ImportBeanDefinitionRegistrar实现类。
通过这种手动注入方式,最终将AnnotationAwareAspectJAutoProxyCreator注入到IOC容器中。
查看AnnotationAwareAspectJAutoProxyCreator的类图,可以发现它是BeanPostProcessor的实现,BeanPostProcessor允许对Bean进行增强操作。
增强操作分为两种:前置(postProcessBeforeInitialization)、后置(postProcessAfterInitialization)增强,最终我们在AnnotationAwareAspectJAutoProxyCreator的其中一个超类中找到了这两个增强方法的实现,那就是AbstractAutoProxyCreator。
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
最终,我们发现AbstractAutoProxyCreator在前置增强中并未做任何处理,而我们的故事也就是从后置增强中开始……
咳咳~
好啦,故事我们讲完了,现在开始源码分析。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 省略部分代码……
// 1、获取所有Advisors(可以理解成拦截器或者切面),官方定义https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/aop/Advisor.html
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 2、创建代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
我将整个过程分两步:
1、切入点的解析与Advice初排序
2、代理中的递归调用链逻辑分析
它们分别隶属getAdvicesAndAdvisorsForBean()分支以及createProxy()分支,那么我们先来看第一部分的源码分析。
IDEA Ctrl+Alt+B 可以发现,getAdvicesAndAdvisorsForBean有两种实现,当方法是具有多个实现的抽象方法时,可以选择调试的方式确定最终的调用方向。
最终,我们可以确定代码走向AbstractAdvisorAutoProxyCreator的实现。
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
// 寻找合格的顾问
——> protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 查询所有候选Advisors
// Ctrl+Alt+B 进入AnnotationAwareAspectJAutoProxyCreator的findCandidateAdvisors()实现
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
——> protected List<Advisor> findCandidateAdvisors() {
// 执行父类中的findCandidateAdvisors()方法,也就是我们上一段代码所在类。
List<Advisor> advisors = super.findCandidateAdvisors();
// 查询添加Bean工厂中所有的AspectJ切面Advisor。
if (this.aspectJAdvisorsBuilder != null) {
// 查询添加Bean工厂中所有的AspectJ切面Advisor,被@AspectJ标注的类将被IOC做特殊标记
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
到这里我们可以初略的将Advisor分为两类,过滤器以及切面,接下来我们重点关注一下切面(@AspectJ标注的类)如何被解析的?以及如何进行的排序?
——> public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<>();
aspectNames = new LinkedList<>();
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// 1、过滤被@AspectJ标注的Bean
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 2、获取当前切面下的所有Advisor切入点
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// 省略部分代码……
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
// 省略部分尾部代码……
return advisors;
}
Spring 如何获取切入点信息?初排序Order如何设置?
——>public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
// 验证当前实例,验证@AspectJ标注等
validate(aspectClass);
// 延迟加载相关.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new LinkedList<>();
for (Method method : getAdvisorMethods(aspectClass)) {
// 我们重点关注这一行,可以看到的是
// declarationOrderInAspect参数入参为 advisors.size(),这意味着
// 这与Advisor之后的Order排序有着密切的关系,从赋值来看,顺序为Advisor声明的顺序
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// 检查是否又被@DeclareParents标注的属性.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
@DeclareParents用来标注对象属性,可以为对象添加一个新方法。继续追踪调用链,查看如何创建的切入点Advisor:
——> public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
// 1、超类@AspectJ标注等验证
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
// 2、获取切入点配置信息。说白了,就是在查找方法上是否有AOP切入点相关的注解标注
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
// 在这里将过滤掉所有没有切入点配置的方法
if (expressionPointcut == null) {
return null;
}
// 3、构造切入点Advisor,关注到declarationOrderInAspect的赋值
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
// 看看Spring是如何获取到方法上面的切入点的
——> private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
// 查找方法是否有被@AspectJ类注解标注
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// 构造成切入点对象
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
——> protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
// 看到熟悉的AOP切入点注解:@Before、@Around、@After、@AfterReturning、@AfterThrowing、@Pointcut
Class<?>[] classesToLookFor = new Class<?>[] {
Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
// 若方法被其中某个标注,则为切入点
for (Class<?> c : classesToLookFor) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
// 非切入点方法,则会被过滤掉
return null;
}
最终,根据AOP的切入点注解过滤出来的切面方法,将被构建成,InstantiationModelAwarePointcutAdvisorImpl。
查看它的类图可以发现其实现了Order接口,我们知道Order接口是Spring排序用的,这在我们讲解IOC时提到过,接下来我们就看一下,Spring是如何对这些切入点进行排序的?这种排序可以保证方法最终的执行顺序吗?
回到我们寻找所有Advice代码处findEligibleAdvisors,接着进行初排序分析。
protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
AnnotationAwareOrderComparator.sort(advisors);
return advisors;
}
// AspectJAwareAdvisorAutoProxyCreator类中的实现
——> protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors =
new ArrayList<>(advisors.size());
for (Advisor element : advisors) {
partiallyComparableAdvisors.add(
new PartiallyComparableAdvisorHolder(element, DEFAULT_PRECEDENCE_COMPARATOR));
}
// 根据Order值进行排序,可通过@Order进行设置,order的值越小其优先级越高
List<PartiallyComparableAdvisorHolder> sorted =
PartialOrder.sort(partiallyComparableAdvisors);
if (sorted != null) {
List<Advisor> result = new ArrayList<>(advisors.size());
for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {
result.add(pcAdvisor.getAdvisor());
}
return result;
}
else {
return super.sortAdvisors(advisors);
}
}
可以看到,最终Order排序与切入点方法的执行顺序并不相同,那么Spring 又是如何保证切入点方法与代理方法的执行顺序的呢,也就是谁前谁后?
通过以上代码分析,我们了解到我们可以通过@Order注解标注切面的执行顺序,order的值越小其优先级越高。
Advisor如何层层传递调用的?这一切都依仗于责任链设计模式。
createProxy()经过层层调用将根据Bean实例是否是接口实现类判断采用JDK或CgLib动态代理,被代理的对象实例的方法在执行时,将会执行invoke()或intercept()方法。(如有疑问,请先了解AOP原理)
现在,我们就从invoke()/intercept()方法开始分析,调用链究竟如何实现的。
不论是采用JDK或是CgLib,你会发现在它们各自的代理方法中都包括如下这段代码,而这就是AOP Advisor 调用链的入口。
// 获取当前代理的方法的所有Advice。
// 如果你细细研究,会发现在getInterceptorsAndDynamicInterceptionAdvice中,有一段代码,
// 将所有的AOP切面Advice转换成了MethodInterceptor接口。
// MethodInterceptor则是Spring所有切入点方法的祖先。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 通过反射技术创建调用链执行方法...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 现在执行所有的拦截器和切面Advice.
retVal = invocation.proceed();
}
——> public Object proceed() throws Throwable {
// 执行完所有Advice后执行代理方法,currentInterceptorIndex 是标识调用链位置的偏移量。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 获取下一个要执行的Advice
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 执行动态方法匹配器
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
// InterceptorAndDynamicMethodMatcher 为动态拦截器,根据运行时参数来决定拦截器是否生效,有动态拦截器当然也有静态拦截器,静态拦截器一般通过包名,类名,方法名 ,参数来确定静态拦截器是否对代理方法生效
return dm.interceptor.invoke(this);
}
else {
// 动态方法匹配失败,跳过此拦截器并调用链中的下一个Advice拦截器,递归执行。
return proceed();
}
}
else {
// 执行静态拦截器,我们的AOP 切入点都是此类拦截器(包括@DeclareParents)。
// 这里将this传入过去,就是为了实现递归责任链
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
Ctrl+Alt+B 可以看到invoke(this)有非常多的实现,我们重点关注@Before、@Around、@After、@AfterReturning、@AfterThrowing、@Pointcut的实现。
首先,我们需要了解它们各自的执行时机:
@Before:前置通知,在方法执行之前运行;
@After:后置通知,在方法返回结果之后运行;
@AfterReturning:返回结果通知,方法返回结果之后运行,也拦截返回的结果;
@Around:环绕通知,运行方法执行;
@AfterThrowing:异常通知,在方法引发异常之后运行。
// MethodBeforeAdviceInterceptor 前置通知
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
// AspectJAfterAdvice 后置通知
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
// AfterReturningAdviceInterceptor 返回结果通知
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
// AspectJAroundAdvice 环绕通知
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
// 具体实现方式参阅invokeAdviceMethod部分代码。
return invokeAdviceMethod(pjp, jpm, null, null);
}
// AspectJAfterThrowingAdvice 异常通知
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
最终,Spring通过层层传递的this进行递归调用proceed(),通过currentInterceptorIndex偏移量记录调用链执行位置,通过反射调用目标方法,进而实现层层传递,层层调用的责任链模型。
1、第一步,Spring IOC Bean后置处理器执行阶段,解析被@AspectJ注解标注的实例;
2、第二步,解析@AspectJ标注的实例,这个过程是解析器内部的切入点、以及切面方法,最终将解析成MethodInterceptor接口1(当然这个过程还包括Spring其他拦截器的解析)。主要解析@Before、@Around、@After、@AfterReturning、@AfterThrowing、@Pointcut注解;
3、第三步,根据order值,对MethodInterceptor排序,order的值越小其优先级越高。MethodInterceptor的order值等于它们的声明(加载)顺序,当然我们也可以通过@Order注解标注他们的顺序,Order的大小决定它们最终的执行顺序;
4、第四步,当被代理类方法执行前,调用拦截链逻辑,主要依据下图所示实现。
4.1、使用一个列表(interceptorsAndDynamicMethodMatchers)存放排序后的MethodInterceptor;
4.2、使用一个偏移量(currentInterceptorIndex)标识当前调用链执行下标;
4.3、递归过程中传递this关键字保证以上两个变量的全局一致;
4.4、切入点逻辑依照不同类型的MethodInterceptor而选择。例如,前置通知,在方法之前执行等;
4.5、层层传递,责任调用。前一个切面代码执行完交由下一个切面执行,形成链状模型。
public class TestTransactional {
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
User user = new User("chunsoft");
userMapper.insertSelective(user);
if (true) {
throw new RuntimeException("抛异常");
}
}
public void B() {
this.A();
}
}
参考实例给出的代码,这里的事务为何会失效呢?我们先查看一下事务的执行代码,它也是MethodInterceptor的一种实现,上面我们介绍的AOP切面执行逻辑是一样的,它的名字叫做TransactionInterceptor。
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// 执行事务逻辑...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 如果transaction属性为null,则该方法为非事务处理。.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 创建事务管理器
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 开启事务
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// 调用链中的下一个拦截器.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 事务回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 重置TransactionInfo ThreadLocal
cleanupTransactionInfo(txInfo);
}
// 提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// 省略CallbackPreferringPlatformTransactionManager部分代码……
}
}
可以看到,最终事务通过反射进行调用,但若@Transactional标注的方法在同类内被调用,则事务不会生效,因为Jdk/CgLib代理是基于对象的,需要在解析切入点是解析@Transactional注解的,同类调用,则代理不生效,事务不生效。
这里之所以说解析成MethodInterceptor而不是Advice,是因为MethodInterceptor继承了Advice,便于我们理解记忆。 ↩︎