0x5 Spring系列:深入聊一聊 Spring AOP 实现机制【二】
2: 扫描容器中的切面,创建 PointcutAdvisor对象
在spring ioc流程加载的过程中,会触发 beanPostProcessor 扩展接口,
而AnnotationAwareAspectJAutoProxyCreator又是SmartInstantiationAwareBeanPostProcessor的子类,所以该扩展接口正是 aop 实现的入口。
该接口的触发在实例化 bean 之后,初始化 bean之前,具体来看:
@OverridepublicObject 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)) {returnnull;}//判断当前bean是否可以被代理,然后存入advisedBeansif(isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey,Boolean.FALSE);returnnull;}}// 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());//如果最终可以获得代理类,则返回代理类,直接执行实例化后置通知方法returnproxy;}returnnull;}
来看一下判定 bean 是否被代理的方法依据:
@Overrideprotectedboolean isInfrastructureClass(Class<?> beanClass) {return(super.isInfrastructureClass(beanClass) ||(this.aspectJAdvisorFactory !=null&&this.aspectJAdvisorFactory.isAspect(beanClass)));}
privatebooleanhasAspectAnnotation(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() +"]");}returnretVal;}
重点来看 shouldSkip方法:
@OverrideprotectedbooleanshouldSkip(Class<?> beanClass, String beanName) {// TODO: Consider optimization by caching the list of the aspect names//获取所有的候选顾问类AdvisorList<Advisor> candidateAdvisors = findCandidateAdvisors();for(Advisor advisor : candidateAdvisors) {if(advisorinstanceofAspectJPointcutAdvisor &&((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {returntrue;}}returnsuper.shouldSkip(beanClass, beanName);}
上述代码通过findCandidateAdvisors()方法来获取所有的候选 advisor:
@OverrideprotectedList<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注解类,解析成Advisorif(this.aspectJAdvisorsBuilder !=null) {advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());}returnadvisors;}
继续跟进buildAspectJAdvisors方法,会触发ReflectiveAspectJAdvisorFactory中的getAdvisors方法:
@OverridepublicList<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 =newLazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);List<Advisor> advisors =newLinkedList<>();//遍历该类所有方法,根据方法判断是否能获取到对应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 =newSyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);advisors.add(0, instantiationAdvisor);}// Find introduction fields.//获取@DeclareParents注解修饰的属性(并不常用)for(Field field : aspectClass.getDeclaredFields()) {Advisor advisor = getDeclareParentsAdvisor(field);if(advisor !=null) {advisors.add(advisor);}}returnadvisors;}
继续来看getAdvisor方法:
@Override@NullablepublicAdvisorgetAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,intdeclarationOrderInAspect, String aspectName) {validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());//根据候选方法名,来获取对应的pointCutAspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());if(expressionPointcut ==null) {returnnull;}//如果能获取到pointCut,则将切点表达式expressionPointcut、当前对象ReflectiveAspectJAdvisorFactory、方法名等包装成advisor对象returnnewInstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);}
可以看到方法将切面相关的类,封装成InstantiationModelAwarePointcutAdvisorImpl对象,也就是Advisor 对象。
来看下上面获取切面的方法,规则就是遍历方法,根据注解判断:
protectedstatic 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) {returnfoundAnnotation;}}returnnull;}
我们继续来看 Advisor 对象的构造方法。
InstantiationModelAwarePointcutAdvisorImpl的构造方法会触发构造通知对象:
publicAdvicegetAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,MetadataAwareAspectInstanceFactory aspectInstanceFactory,intdeclarationOrder, String aspectName) {//......//根据注解类型,匹配对应的通知类型switch(aspectJAnnotation.getAnnotationType()) {//前置通知caseAtBefore:springAdvice =newAspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;//最终通知caseAtAfter:springAdvice =newAspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;//后置通知caseAtAfterReturning:springAdvice =newAspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();if(StringUtils.hasText(afterReturningAnnotation.returning())) {springAdvice.setReturningName(afterReturningAnnotation.returning());}break;//异常通知caseAtAfterThrowing:springAdvice =newAspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();if(StringUtils.hasText(afterThrowingAnnotation.throwing())) {springAdvice.setThrowingName(afterThrowingAnnotation.throwing());}break;//环绕通知caseAtAround:springAdvice =newAspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;//切面caseAtPointcut:if(logger.isDebugEnabled()) {logger.debug("Processing pointcut '"+ candidateAdviceMethod.getName() +"'");}returnnull;default:thrownewUnsupportedOperationException("Unsupported advice type on method: "+ candidateAdviceMethod);}//......}
可以看到,根据@Aspect类中方法的注解类型,生成对应的advice,并通过通知的构造方法,将通知增强方法,切面表达式传入到通知当中。
InstantiationModelAwarePointcutAdvisorImpl对象到这里构造完毕。
3: 生成代理类
上面创建advisor的逻辑发生在扩展接口中的postProcessBeforeInstantiation,实例化之前执行,如果有自定义的TargetSource指定类,则则直接生成代理类,并直接执行初始化之后的方法postProcessAfterInitialization。这种情况使用不多,常规代理类还是在postProcessAfterInitialization中创建,也就是 IOC 最后一个扩展方法。
@OverridepublicObjectpostProcessAfterInitialization(@NullableObjectbean,StringbeanName) throws BeansException {if(bean !=null) {ObjectcacheKey = getCacheKey(bean.getClass(), beanName);//处理循环依赖的判断if(!this.earlyProxyReferences.contains(cacheKey)) {returnwrapIfNecessary(bean, beanName, cacheKey);}}returnbean;}
protectedObject wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if(StringUtils.hasLength(beanName) &&this.targetSourcedBeans.contains(beanName)) {returnbean;}if(Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {returnbean;}if(isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey,Boolean.FALSE);returnbean;}// 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());returnproxy;}this.advisedBeans.put(cacheKey,Boolean.FALSE);returnbean;}
上述方法通过调用getAdvicesAndAdvisorsForBean()方法来获取advisor,该方法最终会调用findEligibleAdvisors(),Eligible意为有资格的,合适的。具体来看下:
protectedList<Advisor> findEligibleAdvisors(Class<?>beanClass,StringbeanName) {List<Advisor> candidateAdvisors = findCandidateAdvisors();//这里会对获取的advisor进行筛选List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);//添加一个默认的advisor,执行时用到。extendAdvisors(eligibleAdvisors);if(!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}returneligibleAdvisors;}
最终的筛选规则在AopUtils中:
publicstaticList<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors,Class<?>clazz) {//......for(Advisor candidate : candidateAdvisors) {if(candidateinstanceofIntroductionAdvisor) {// already processedcontinue;}//调用canApply方法,遍历所有的方法进行匹配if(canApply(candidate, clazz, hasIntroductions)) {eligibleAdvisors.add(candidate);}}//......}
调用canApply方法,遍历被代理类的所有的方法,跟进切面表达式进行匹配,如果有一个方法匹配到,也就意味着该类会被代理。
匹配方法是借助org.aspectj.weaver.internal.tools实现,也就是AspectJ框架中的工具类,有兴趣的可以自行查看。
重点来看一下代理生成方式:
publicAopProxycreateAopProxy(AdvisedSupport config)throwsAopConfigException {if(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if(targetClass ==null) {thrownewAopConfigException("TargetSource cannot determine target class: "+"Either an interface or a target is required for proxy creation.");}//如果代理目标是接口或者Proxy类型,则走jdk类型if(targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {returnnewJdkDynamicAopProxy(config);}returnnewObjenesisCglibAopProxy(config);}else{returnnewJdkDynamicAopProxy(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中
具体代理类如何调用执行呢?后面会详细介绍。
- 点赞
- 收藏
- 关注作者
评论(0)