Spring AOP @EnableAspectJAutoProxy 实现原理分析

时间:2022-11-22小编:迷失传奇版本

@EnableAspectJAutoProxy注解是SpringAOP框架提供给用户开启AspectJ注解支持的一个开关。将其添加到Spring配置类上,然后就可以在SpringBean上使用AspectJ注解,将bean配置为一个Aspect。

SpringAOP对AspectJ的依赖是可选的,如果使用这个注解还需要显式的引入aspectjweaver.jar。如果你的项目中用到了spring-boot-starter-aop,Spring会自动开启对AspectJ的支持,无需额外操作。

实现分析

在《SpringAOP与AspectJ》一篇中,我们提到,SpringAOP是通过在运行时创建代理来实现的,那么这里就跟着@EnableAspectJAutoProxy注解的实现来进行分析。Spring@Enable*注解的实现大多相同,可以参考《Spring框架中的@Enable*注解是怎样实现的?》,这里将侧重AOP相关的实现部分。

注解说明

@EnableAspectJAutoProxy源码如下。

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AspectJAutoProxyRegistrar.class)public@interfaceEnableAspectJAutoProxy{//是否使用CGLIB创建代理booleanproxyTargetClassdefaultfalse;//代理是否暴露在ThreadLocal中,以便通过AopContext获取booleanexposeProxydefaultfalse;}

注解定义了两个属性。

proxyTargetClass属性用于指示是否优先使用CGLIB创建代理,Spring内部会进行检测,即便配置为false,如果不能使用JDK动态代理创建代理对象,那么Spring仍将选择CGLIB,通常情况保持默认配置即可。

exposeProxy属性用于配置是否在ThreadLocal中暴露代理对象,如果配置为true,则可以在目标类的方法中使用AopContext#currentProxy获取代理对象。通常情况,目标类对代理类应该是无感知的,因此这里一般也保持默认配置。

@EnableAspectJAutoProxy注解上添加了元注解@Import,这意味着将使用AspectJAutoProxyRegistrar新引入一些bean到Spring的上下文中。

Bean注册

跟踪AspectJAutoProxyRegistrar源码。

classAspectJAutoProxyRegistrarimplementsImportBeanDefinitionRegistrar{@OverridepublicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry){AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);AnnotationAttributesenableAspectJAutoProxy=AnnotationConfigUtils.attributesFor(importingClassMetadata,EnableAspectJAutoProxy.class);if(enableAspectJAutoProxy!=null){if(enableAspectJAutoProxy.getBoolean("proxyTargetClass")){AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}if(enableAspectJAutoProxy.getBoolean("exposeProxy")){AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}}

AspectJAutoProxyRegistrar实现了接口ImportBeanDefinitionRegistrar,主要利用AopConfigUtils这个工具类做了3步。

向BeanDefinitionRegistry注册了某个bean。

强制配置是否使用CGLIB创建代理。

强制配置是否在目标方法执行前将代理对象暴露到ThreadLocal。

后面两个步骤较为简单,先分析AopConfigUtils。registerAspectJAnnotationAutoProxyCreatorIfNecessary注册bean的逻辑,跟踪代码如下。

publicabstractclassAopConfigUtils{@NullablepublicstaticBeanDefinitionregisterAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistryregistry){returnregisterAspectJAnnotationAutoProxyCreatorIfNecessary(registry,null);}@NullablepublicstaticBeanDefinitionregisterAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistryregistry,@NullableObjectsource){returnregisterOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class,registry,source);}}

到了这里,我们可以小结如下,@EnableAspectJAutoProxy注解主要向Spring应用上下文中注册了一个类型为AnnotationAwareAspectJAutoProxyCreator的bean。

自动代理创建分析

下面分析AnnotationAwareAspectJAutoProxyCreator如何与IOC容器整合创建代理的。这个类自身相对简单,实现逻辑主要在父类中。查看类图如下。

看到这里的小伙伴,想必心里一脸懵逼,这个类层次结构怎么如此深?不用过于担心,下面我会对主要的流程进行分析。

如果想创建bean的代理,Spring一定会在bean的生命周期中处理,如果你对SpringBean的生命周期不熟悉,可以先参考《Java面试必备的SpringBean生命周期总结》。Spring提供了InstantiationAwareBeanPostProcessor,Spring会使用这个接口创建的bean取代用户定义的bean。

通过类图可以看到AnnotationAwareAspectJAutoProxyCreator正是间接实现了这个接口,直接实现这个接口的父类AbstractAutoProxyCreator创建代理bean对象的代码如下。

publicabstractclassAbstractAutoProxyCreatorextendsProxyProcessorSupportimplementsSmartInstantiationAwareBeanPostProcessor,BeanFactoryAware{@OverridepublicObjectpostProcessBeforeInstantiation(ClassbeanClass,StringbeanName){ObjectcacheKey=getCacheKey(beanClass,beanName);if(!StringUtils.hasLength(beanName)||!this.targetSourcedBeans.contains(beanName)){if(this.advisedBeans.containsKey(cacheKey)){returnnull;}if(isInfrastructureClass(beanClass)||shouldSkip(beanClass,beanName)){this.advisedBeans.put(cacheKey,Boolean.FALSE);returnnull;}}TargetSourcetargetSource=getCustomTargetSource(beanClass,beanName);if(targetSource!=null){//TargetSource不为空才创建代理if(StringUtils.hasLength(beanName)){this.targetSourcedBeans.add(beanName);}Object[]specificInterceptors=getAdvicesAndAdvisorsForBean(beanClass,beanName,targetSource);Objectproxy=createProxy(beanClass,beanName,specificInterceptors,targetSource);this.proxyTypes.put(cacheKey,proxy.getClass);returnproxy;}returnnull;}}

AbstractAutoProxyCreator在bean实例化前判断是否定义了获取TargetSource的TargetSourceCreator,如果是则创建代理作为bean实例。不过很明显,我们使用Spring时并不会自己定义一个TargetSourceCreator。因此这里通常也会返回一个null。

不过也不用担心,AbstractAutoProxyCreator还实现了接口SmartInstantiationAwareBeanPostProcessor,Spring还会通过这个接口获取bean的早期引用,SpringAOP通过这个接口为早期创建的bean创建了代理对象。跟踪源码。

publicabstractclassAbstractAutoProxyCreatorextendsProxyProcessorSupportimplementsSmartInstantiationAwareBeanPostProcessor,BeanFactoryAware{@OverridepublicObjectgetEarlyBeanReference(Objectbean,StringbeanName){ObjectcacheKey=getCacheKey(bean.getClass,beanName);this.earlyProxyReferences.put(cacheKey,bean);returnwrapIfNecessary(bean,beanName,cacheKey);}}

这里将原始bean进行包装,继续跟踪包装原始bean的#wrapIfNecessary方法。

publicabstractclassAbstractAutoProxyCreatorextendsProxyProcessorSupportimplementsSmartInstantiationAwareBeanPostProcessor,BeanFactoryAware{protectedObjectwrapIfNecessary(Objectbean,StringbeanName,ObjectcacheKey){if(StringUtils.hasLength(beanName)&&this.targetSourcedBeans.contains(beanName)){//给定bean已经被包装为代理,无需再次包装returnbean;}if(Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))){//给定bean无需被代理,不再包装returnbean;}if(isInfrastructureClass(bean.getClass)||shouldSkip(bean.getClass,beanName)){this.advisedBeans.put(cacheKey,Boolean.FALSE);//给定bean无需被代理,不再包装returnbean;}//Createproxyifwehaveadvice.Object[]specificInterceptors=getAdvicesAndAdvisorsForBean(bean.getClass,beanName,null);if(specificInterceptors!=DO_NOT_PROXY){this.advisedBeans.put(cacheKey,Boolean.TRUE);Objectproxy=createProxy(bean.getClass,beanName,specificInterceptors,newSingletonTargetSource(bean));this.proxyTypes.put(cacheKey,proxy.getClass);returnproxy;}this.advisedBeans.put(cacheKey,Boolean.FALSE);returnbean;}}

#wrapIfNecessary方法主要判断是否需要创建代理,如果需要则调用#getAdvicesAndAdvisorsForBean收集Advisor,然后调用#createProxy方法创建代理。创建代理的逻辑比较简单,主要是使用PointcutAdvisor中的Pointcut确定要拦截的目标方法,然后将Advisor中的Advice转换为MethodInvocation拦截目标方法的执行。这里我们将重点放到与AspectJ相关的收集Advisor的代码。

#getAdvicesAndAdvisorsForBean是一个抽象方法,查看子类实现如下。

publicabstractclassAbstractAdvisorAutoProxyCreatorextendsAbstractAutoProxyCreator{@Override@NullableprotectedObject[]getAdvicesAndAdvisorsForBean(ClassbeanClass,StringbeanName,@NullableTargetSourcetargetSource){Listadvisors=findEligibleAdvisors(beanClass,beanName);if(advisors.isEmpty){returnDO_NOT_PROXY;}returnadvisors.toArray;}}

这里主要调用#findEligibleAdvisors方法收集Advisor,继续跟踪。

publicabstractclassAbstractAdvisorAutoProxyCreatorextendsAbstractAutoProxyCreator{protectedListfindEligibleAdvisors(ClassbeanClass,StringbeanName){ListcandidateAdvisors=findCandidateAdvisors;ListeligibleAdvisors=findAdvisorsThatCanApply(candidateAdvisors,beanClass,beanName);extendAdvisors(eligibleAdvisors);if(!eligibleAdvisors.isEmpty){eligibleAdvisors=sortAdvisors(eligibleAdvisors);}returneligibleAdvisors;}}

#findEligibleAdvisors方法主要调用#findCandidateAdvisors收集可以应用到目标对象的Advisor。而AnnotationAwareAspectJAutoProxyCreator覆盖了#findCandidateAdvisors这个方法通过AspectJ的注解来收集Advisor。查看源码如下。

publicclassAnnotationAwareAspectJAutoProxyCreatorextendsAspectJAwareAdvisorAutoProxyCreator{@OverrideprotectedListfindCandidateAdvisors{//AddalltheSpringadvisorsfoundaccordingtosuperclassrules.Listadvisors=super.findCandidateAdvisors;//BuildAdvisorsforallAspectJaspectsinthebeanfactory.if(this.aspectJAdvisorsBuilder!=null){advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors);}returnadvisors;}}

aspectJAdvisorsBuilder.buildAspectJAdvisors方法就是用来通过AspectJ注解收集Advisor的,其实现相对复杂一些,这里不再分析。

总结

到了这里,总结如下。

Spring@EnableAspectJAutoProxy注解引入类型为AnnotationAwareAspectJAutoProxyCreator的bean,这个bean会根据AspectJ的注解收集Advisor,通过收集的Advisor创建代理,底层又会把Advisor中的Advice转换为MethodInvocation拦截目标方法的执行,感兴趣的可以自行阅读相关源码。



展开

上一篇:传奇游戏打金教学,怎么样?

下一篇:手游复古传奇攻略,有新区吗?

相关内容更多
热门攻略更多
返回顶部