0x5 Spring系列:深入聊一聊 Spring AOP 实现机制【二】

网友投稿 781 2022-05-30

2: 扫描容器中的切面,创建 PointcutAdvisor对象

spring ioc流程加载的过程中,会触发 beanPostProcessor 扩展接口,

而AnnotationAwareAspectJAutoProxyCreator又是SmartInstantiationAwareBeanPostProcessor的子类,所以该扩展接口正是 aop 实现的入口。

该接口的触发在实例化 bean 之后,初始化 bean之前,具体来看:

@Override

public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {

Object cacheKey = getCacheKey(beanClass, beanName);

if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {

//advisedBeans用于存储不可代理的bean,如果包含直接返回

if (this.advisedBeans.containsKey(cacheKey)) {

return null;

}

//判断当前bean是否可以被代理,然后存入advisedBeans

if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {

this.advisedBeans.put(cacheKey, Boolean.FALSE);

return null;

}

}

// Create proxy here if we have a custom TargetSource.

// Suppresses unnecessary default instantiation of the target bean:

// The TargetSource will handle target instances in a custom fashion.

//到这里说明该bean可以被代理,所以去获取自定义目标类,如果没有定义,则跳过。

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;

}

来看一下判定 bean 是否被代理的方法依据:

@Override

protected boolean isInfrastructureClass(Class beanClass) {

return (super.isInfrastructureClass(beanClass) ||

(this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));

}

private boolean hasAspectAnnotation(Class clazz) {

//判定当前类是否有 Aspect 注解,如果有,则不能被代理

return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);

}

protected boolean isInfrastructureClass(Class beanClass) {

//判定当前bean是否是 Advice、Pointcut、Advisor、AopInfrastructureBean等子类或实现类,如果是,则不能被代理

boolean retVal = Advice.class.isAssignableFrom(beanClass) ||

Pointcut.class.isAssignableFrom(beanClass) ||

Advisor.class.isAssignableFrom(beanClass) ||

AopInfrastructureBean.class.isAssignableFrom(beanClass);

if (retVal && logger.isTraceEnabled()) {

logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");

}

return retVal;

}

重点来看 shouldSkip方法:

@Override

protected boolean shouldSkip(Class beanClass, String beanName) {

// TODO: Consider optimization by caching the list of the aspect names

//获取所有的候选顾问类 Advisor

List candidateAdvisors = findCandidateAdvisors();

for (Advisor advisor : candidateAdvisors) {

if (advisor instanceof AspectJPointcutAdvisor &&

((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {

return true;

}

}

return super.shouldSkip(beanClass, beanName);

}

上述代码通过findCandidateAdvisors()方法来获取所有的候选 advisor:

@Override

protected List findCandidateAdvisors() {

// Add all the Spring advisors found according to superclass rules.

//获得 Advisor 实现类

List advisors = super.findCandidateAdvisors();

// Build Advisors for all AspectJ aspects in the bean factory.

//将@Aspect注解类, 解析成Advisor

if (this.aspectJAdvisorsBuilder != null) {

advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());

}

return advisors;

}

继续跟进buildAspectJAdvisors方法,会触发

ReflectiveAspectJAdvisorFactory中的getAdvisors方法:

@Override

public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {

//从 aspectMetadata 中获取 Aspect()标注的类 class对象

Class aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();

//获取Aspect()标注的类名

String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();

validate(aspectClass);

// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator

// so that it will only instantiate once.

MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =

new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

List advisors = new LinkedList<>();

//遍历该类所有方法,根据方法判断是否能获取到对应 pointCut,如果有,则生成 advisor 对象

for (Method method : getAdvisorMethods(aspectClass)) {

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);

}

// Find introduction fields.

//获取 @DeclareParents 注解修饰的属性(并不常用)

for (Field field : aspectClass.getDeclaredFields()) {

Advisor advisor = getDeclareParentsAdvisor(field);

0x5 Spring系列:深入聊一聊 Spring AOP 实现机制【二】

if (advisor != null) {

advisors.add(advisor);

}

}

return advisors;

}

继续来看getAdvisor方法:

@Override

@Nullable

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,

int declarationOrderInAspect, String aspectName) {

validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

//根据候选方法名,来获取对应的 pointCut

AspectJExpressionPointcut expressionPointcut = getPointcut(

candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());

if (expressionPointcut == null) {

return null;

}

//如果能获取到 pointCut,则将切点表达式 expressionPointcut、当前

对象ReflectiveAspectJAdvisorFactory、 方法名等包装成 advisor 对象

return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,

this, aspectInstanceFactory, declarationOrderInAspect, aspectName);

}

可以看到方法将切面相关的类,封装成InstantiationModelAwarePointcutAdvisorImpl对象,也就是Advisor 对象。

来看下上面获取切面的方法,规则就是遍历方法,根据注解判断:

protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method) {

//定义class对象数组,如果方法中有以下注解中任何一种,则返回该注解

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) c);

if (foundAnnotation != null) {

return foundAnnotation;

}

}

return null;

}

我们继续来看 Advisor 对象的构造方法。

InstantiationModelAwarePointcutAdvisorImpl的构造方法会触发构造通知对象:

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,

MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

//......

//根据注解类型,匹配对应的通知类型

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);

}

//......

}

可以看到,根据@Aspect类中方法的注解类型,生成对应的advice,并通过通知的构造方法,将通知增强方法,切面表达式传入到通知当中。

InstantiationModelAwarePointcutAdvisorImpl对象到这里构造完毕。

3: 生成代理类

上面创建advisor的逻辑发生在扩展接口中的postProcessBeforeInstantiation,实例化之前执行,如果有自定义的TargetSource指定类,则则直接生成代理类,并直接执行初始化之后的方法postProcessAfterInitialization。这种情况使用不多,常规代理类还是在postProcessAfterInitialization中创建,也就是 IOC 最后一个扩展方法。

@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;

}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {

if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {

return bean;

}

if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {

return bean;

}

if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {

this.advisedBeans.put(cacheKey, Boolean.FALSE);

return bean;

}

// Create proxy if we have advice.

//获取到合适的advisor,如果为空。如果不为空,则生成代理类。

Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

if (specificInterceptors != DO_NOT_PROXY) {

this.advisedBeans.put(cacheKey, Boolean.TRUE);

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;

}

上述方法通过调用getAdvicesAndAdvisorsForBean()方法来获取advisor,该方法最终会调用findEligibleAdvisors(),Eligible意为有资格的,合适的。具体来看下:

protected List findEligibleAdvisors(Class beanClass, String beanName) {

List candidateAdvisors = findCandidateAdvisors();

//这里会对获取的advisor进行筛选

List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

//添加一个默认的advisor,执行时用到。

extendAdvisors(eligibleAdvisors);

if (!eligibleAdvisors.isEmpty()) {

eligibleAdvisors = sortAdvisors(eligibleAdvisors);

}

return eligibleAdvisors;

}

最终的筛选规则在AopUtils中:

public static List findAdvisorsThatCanApply(List candidateAdvisors, Class clazz) {

//......

for (Advisor candidate : candidateAdvisors) {

if (candidate instanceof IntroductionAdvisor) {

// already processed

continue;

}

//调用 canApply 方法,遍历所有的方法进行匹配

if (canApply(candidate, clazz, hasIntroductions)) {

eligibleAdvisors.add(candidate);

}

}

//......

}

调用canApply方法,遍历被代理类的所有的方法,跟进切面表达式进行匹配,如果有一个方法匹配到,也就意味着该类会被代理。

匹配方法是借助org.aspectj.weaver.internal.tools实现,也就是AspectJ框架中的工具类,有兴趣的可以自行查看。

重点来看一下代理生成方式:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {

if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {

Class targetClass = config.getTargetClass();

if (targetClass == null) {

throw new AopConfigException("TargetSource cannot determine target class: " +

"Either an interface or a target is required for proxy creation.");

}

//如果代理目标是接口或者Proxy类型,则走jdk类型

if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {

return new JdkDynamicAopProxy(config);

}

return new ObjenesisCglibAopProxy(config);

}

else {

return new JdkDynamicAopProxy(config);

}

}

上述方法通过三个变量来进行筛选代理方法:

·      optimize:官方文档翻译为设置代理是否应执行积极的优化,默认为false。

·      proxyTargetClass:这个在上面已经提到了,AopAutoConfiguration中指定,默认为true,也就是选择使用 cglib 代理。可以看到该变量和optimize意义一样,之所以这么做,个人理解是为了可以在不同的场景中使用。

·      hasNoUserSuppliedProxyInterfaces:是否指定了实现接口。什么意思呢?

hasNoUserSuppliedProxyInterfaces方法如下:

private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {

Class[] ifcs = config.getProxiedInterfaces();

return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));

}

主要就是判断AdvisedSupport中interfaces变量中是否设置了接口,

意思是如果一个类实现了接口,把接口设置到该方法的变量中,但是不是一定会设置到该变量中,具体设置接口的代码如下:

protected Object createProxy(

Class beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

//......

if (!proxyFactory.isProxyTargetClass()) {

if (shouldProxyTargetClass(beanClass, beanName)) {

proxyFactory.setProxyTargetClass(true);

}

else {

evaluateProxyInterfaces(beanClass, proxyFactory);

}

}

//......

}

可以看到如果proxyTargetClass为 true,上述方法将不再执行,也就意味着interfaces变量不再赋值。同时,只要为类代理,默认会走 CGLIB 方式。

三:Spring Boot 1.x 版本和 2.x 版本中 AOP配置变动

配置类AopAutoConfiguration:

1.5x版本:

@Configuration

@EnableAspectJAutoProxy(proxyTargetClass = true)

@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)

public static class CglibAutoProxyConfiguration {

}

2.x版本:

@Configuration

@EnableAspectJAutoProxy(proxyTargetClass = true)

@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)

public static class CglibAutoProxyConfiguration {

}

在SpringBoot2.x中最主要的变化就是proxy-target-class默认为true,意味着类代理的时候全部走cglib代理方式,只有为接口代理时才走jdk代理(注意:这里为接口代理,不是指代理目标类是否实现了接口)。所以,在使用springboot2.x的版本中,除了代理目标类是接口外,其余的代理方式全部采用cglib类型。

总结

Springboot通过自动装配AopAutoConfiguration配置类,默认自动开启 AOP 功能。通过注册AnnotationAwareAspectJAutoProxyCreator类,来扫描创建所有的Advisor,再通过 Advisor在 Spring IOC的扩展接口中,通过各种设置的匹配规则,来判断是否设置代理,最终生成代理类,注入容器 Spring中

具体代理类如何调用执行呢?后面会详细介绍。

AOP Spring

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:为什么要阅读代码?怎么阅读k8s源代码?
下一篇:Java全栈程序员之04:Ubuntu下安装MySQL、注册服务及Navcat
相关文章