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

举报
云享专家 发表于 2019/09/19 16:02:01 2019/09/19
【摘要】 可以看到,根据@Aspect类中方法的注解类型,生成对应的advice,并通过通知的构造方法,将通知增强方法,切面表达式传入到通知当中。 InstantiationModelAwarePointcutAdvisorImpl对象到这里构造完毕。

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是否是 AdvicePointcutAdvisorAopInfrastructureBean等子类或实现类,如果是,则不能被代理
        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<Advisor> 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<Advisor> findCandidateAdvisors() {
        
// Add all the Spring advisors found according to superclass rules.
        
//获得 Advisor 实现类
        List<Advisor> 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<Advisor> 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<Advisor> 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);
            
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<Annotation>) 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<Advisor> findEligibleAdvisors(Class<?> beanClassString beanName) {
        
List<Advisor> candidateAdvisors = findCandidateAdvisors();
        
//这里会对获取的advisor进行筛选
        
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        
//添加一个默认的advisor,执行时用到。
        extendAdvisors(eligibleAdvisors);
        
if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        
return eligibleAdvisors;
    }


最终的筛选规则在AopUtils中:

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> 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])));
      }


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

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

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

 

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


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。