Spring源码分析之AOP

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码分析之AOP相关的知识,希望对你有一定的参考价值。

AOP是什么

面向切面的程序设计(Aspect-oriented programming,AOP,又译作面向方面的程序设计剖面导向程序设计),是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知(Advice)机制,能够对被声明为“切点(Pointcut)”的代码块进行统一管理与装饰。

怎么在Spring里使用AOP

在​​Spring​​里,​​AOP​​通过​​EnableAspectJAutoProxy​​注解开启。默认情况下,​​Spring​​会通过​​AopAutoConfiguration​​自动引入这个注解

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration

@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
static class JdkDynamicAutoProxyConfiguration



@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration




可以看到,如果我们不主动设置​​spring.aop.auto=false​​。那么​​Spring​​默认会启用AOP。接下来,我们可以通过在类上标注​​Aspect​​即可使用AOP

package org.example.aspect;

@Aspect
@Component
public class SampleAspect

@Pointcut("execution(* org.example.xxx.*.*(..))")
private void executionPointcut()



@After(value = "executionPointcut()")
public void doAfter()


源码分析

1\\. AOP初始化

1.1 初始化AspectJAutoProxyRegistrar

​EnableAspectJAutoProxy​​通过​​Import​​注解引入了​​AspectJAutoProxyRegistrar​

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy

​AspectJAutoProxyRegistrar​​实现了​​ImportBeanDefinitionRegistrar​​,​​Spring​​在初始化​​AopAutoConfiguration​​时把所有通过​​Import​​注解引入的​​ImportBeanDefinitionRegistrar​​实现类拿出来进行初始化,并调用其​​registerBeanDefinitions​​函数

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar 

@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

1.2 初始化AnnotationAwareAspectJAutoProxyCreator

​AspectJAutoProxyRegistrar​​ 则在​​registerBeanDefinitions​​注册了一个​​AnnotationAwareAspectJAutoProxyCreator​​的​​BeanDefinition​

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source)

return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);

​AnnotationAwareAspectJAutoProxyCreator​​实现了​​BeanPostProcessor​​,​​Spring​​会在初始化普通​​Bean​​之前初始化所有​​BeanPostProcessor​​。

public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext

public void refresh() throws BeansException, IllegalStateException
// 初始化BeanProcessor来拦截Bean的创建
registerBeanPostProcessors(beanFactory);

// 初始化所有剩下的非懒加载的Bean,比如我们写的Service
finishBeanFactoryInitialization(beanFactory);

1.3 初始化切面方法跟切点

另外,​​AnnotationAwareAspectJAutoProxyCreator​​实现了​​InstantiationAwareBeanPostProcessor​​,​​Spring​​ 会在​​Bean​​创建时调用其​​postProcessBeforeInstantiation​​方法对​​Bean​​进行处理。

在第一次调用该方法时,​​AnnotationAwareAspectJAutoProxyCreator​​会初始化切面

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
Object cacheKey = getCacheKey(beanClass, beanName);

if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName))
//判断当前BeanName对应的Bean是否应该被代理
//并将判断结果保存下来,避免后续的后处理方法重复计算
//在第一次判断时,会在shouldSkip里扫描所有Bean进行切面初始化
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName))
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;



//如果为AbstractAutoProxyCreator注入了自定义的TargetSourceCreator
//则通过TargetSourceCreator创建的Bean都被被AOP代理
//TargetSourceCreator默认为空
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null)
if (StringUtils.hasLength(beanName))
this.targetSourcedBeans.add(beanName);

Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;


return null;

切面初始化

public class BeanFactoryAspectJAdvisorsBuilder 

public List<Advisor> buildAspectJAdvisors()
List<String> aspectNames = this.aspectBeanNames;

//如果还未进行初始化
if (aspectNames == null)
synchronized (this)
aspectNames = this.aspectBeanNames;
if (aspectNames == null)
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();

//拿到容器里所有的beanName
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames)

Class<?> beanType = this.beanFactory.getType(beanName, false);

//判断类上是否标注Aspect,以及判断该class是否已经被代码式的Aspectj处理过
if (this.advisorFactory.isAspect(beanType))
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);

//从类中拿到所有带有Before、Around等注解的方法,
//将这些方法包装成MethodInterceptor放入Advisor,MethodInterceptor#invoke为增强方法的调用入口
//将Advisor排好顺序组成List返回
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

this.advisorsCache.put(beanName, classAdvisors);
advisors.addAll(classAdvisors);


this.aspectBeanNames = aspectNames;
return advisors;




Advisor排序

public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable 
private static final Comparator<Method> adviceMethodComparator;

static

Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
//按照注解顺序设置方法对应的advisor的顺序
//在AspectJAfterAdvice里,会先将请求继续向拦截器链后传播,
//对增强方法的调用是在后面的finnaly块里。所以这里的After顺序即使在AfterReturning前面也没关系
//另外,因为在finnly块里触发,所以即使后续的调用抛出了未捕获的异常,After指定的增强方法也会被执行
new InstanceComparator<>(
Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),

(Converter<Method, Annotation>) method ->
//如果方法上没有标注上面的几个注解,则返回null,null会排在最后
AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
return (ann != null ? ann.getAnnotation() : null);
);
Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
adviceMethodComparator = adviceKindComparator.thenComparing(methodNameComparator);

判断method是否属于切面方法

public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable 
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName)

//获取切点信息,如果candidateAdviceMethod不是切面方法,则返回null
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null)
return null;


return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);


private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass)
//在方法上查找Aspectj的相关注解(Around、After等)
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null)
return null;


...

2\\. 生成代理对象

​AbstractAutoProxyCreator​​实现了​​BeanPostProcessor​​,在创建​​Bean​​时,​​Spring​​会调用​​AbstractAutoProxyCreator​​#​​postProcessAfterInitialization​​对​​Bean​​进行处理

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName)
if (bean != null)
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean)
//对bean进行包装,返回代理bean
return wrapIfNecessary(bean, beanName, cacheKey);


return bean;


protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey)
//如果bean有TargetSourceCreator创建,说明已经被代理过了,直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName))
return bean;

//拿出缓存的检测的结果进行判断
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey)))
return bean;

//初步判断bean是否可以被代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName))
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;


//根据切点Point的表达式获得符合当前bean的所有advisor
//如果当前bean不在切点的指向中,则返回DO_NOT_PROXY
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY)
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建代理对象,将所有advisor包装成DynamicAdvisedInterceptor,
//其intercept方法为所有增强方法的统一入口,这个类来自Spring
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;

选择代理对象的创建方式

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable   
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)))
Class<?> targetClass = config.getTargetClass();

//如果targetClass是接口类型或者是通过Proxy.getProxyClass生成的或者类名里包含$$Lambda
//则使用JDK动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass))
return new JdkDynamicAopProxy(config);

//使用cglib,ASM修改字节码的方式生产代理类
return new ObjenesisCglibAopProxy(config);

else
return new JdkDynamicAopProxy(config);


3\\. 访问代理对象

​DispatcherServlet​​ 找到对应的实例跟方法后通过反射进行调用(前面​​Spring Mvc​​博文里已分析过),此时会遍历代理对象上的所有​​MethodInterceptor​​,

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable 

public Object proceed() throws Throwable
//如果拦截器遍历完了,则调用目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1)
return invokeJoinpoint();


//遍历所有增强器MethodInterceptor,
//遍历方式是在MethodInterceptor里调用MethodInvocation#proceed
//每次进入该方法,都会使currentInterceptorIndex增加1,从而达成遍历
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

//调用MethodInterceptor#invoke
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

增强方法实际执行的顺序图

Spring源码分析之AOP_spring

接下来看看每个增强注解对应的​​MethodInterceptor​​处理类是怎么进行请求处理与传递的

1\\. AspectJAroundAdvice

public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable 

@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable
...
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
//调用Around对应的增强方法,并将mi传如增强方法
//mi持有了所有的拦截/增强器信息,通过Joinpoint#proceed实现请求的传递
//所以,around增强方法里需要注意接收Joinpont的实例并调用其proceed方法
return invokeAdviceMethod(pjp, jpm, null, null);


2\\. MethodBeforeAdviceInterceptor

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable 

private final MethodBeforeAdvice advice;

public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice)
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;


@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable
//调用before增强方法
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
//向后传递
return mi.proceed();


3\\. AspectJAfterAdvice

public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable

public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif)

super(aspectJBeforeAdviceMethod, pointcut, aif);


@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable
try
//先向后传递
return mi.proceed();

finally
//执行After方法
invokeAdviceMethod(getJoinPointMatch(), null, null);



4\\. AfterReturningAdviceInterceptor

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable 

private final AfterReturningAdvice advice;

public AfterReturningAdviceInterceptor(AfterReturningAdvice advice)
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;


@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable

//向后传递
Object retVal = mi.proceed();
//调用AfterReturning方法
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;


5\\. AspectJAfterThrowingAdvice

public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable

public AspectJAfterThrowingAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif)

super(aspectJBeforeAdviceMethod, pointcut, aif);


@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable
try
//向后传递
return mi.proceed();

catch (Throwable ex)
//判断增强器上定义的异常类型是否匹配
if (shouldInvokeOnThrowing(ex))
//调用AfterThrowing方法
invokeAdviceMethod(getJoinPointMatch(), null, ex);

throw ex;



/**
* In AspectJ semantics, after throwing advice that specifies a throwing clause
* is only invoked if the thrown exception is a subtype of the given throwing type.
*/
private boolean shouldInvokeOnThrowing(Throwable ex)
return getDiscoveredThrowingType().isAssignableFrom(ex.getClass());


6\\. ExposeInvocationInterceptor

public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable 

private static final ThreadLocal<MethodInvocation> invocation =
new NamedThreadLocal<>("Current AOP method invocation");

@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable
MethodInvocation oldInvocation = invocation.get();
//将MethodInvocation绑定到当前线程的本地变量里,
//从而实现在其他地方访问MethodInvocation
//但不建议这样做,因为AOP对于被代理对象来说应该是无感知的,不应该产生这种依赖
invocation.set(mi);
try
return mi.proceed();

finally
invocation.set(oldInvocation);



以上是关于Spring源码分析之AOP的主要内容,如果未能解决你的问题,请参考以下文章

你必须要懂的 Spring-Aop 之源码跟踪分析 Aop

Spring源码学习之Aop源码分析

Spring读源码系列之AOP--01---aop基本概念扫盲---上

spring源码分析之spring注解@Aspect是如何工作的?

Spring AOP介绍及源码分析

spring源码之AOP下篇