在上文Spring Aop之Target Source详解中,咱们讲解了Spring是如何经过封装Target Source
来达到对最终获取的目标bean进行封装的目的。其中咱们讲解到,Spring Aop对目标bean进行代理是经过AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization()
进行的,Spring Aop的代理主要分为三个步骤:获取全部的Advisor,过滤可应用到当前bean的Adivsor和使用Advisor为当前bean生成代理对象。本文主要对这三步中的第一步获取全部的Advisor进行讲解。java
首先咱们看看postProcessAfterInitialization()
方法的实现:express
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException { if (bean != null) { // 获取当前bean的key:若是beanName不为空,则以beanName为key,若是为FactoryBean类型, // 前面还会添加&符号,若是beanName为空,则以当前bean对应的class为key Object cacheKey = getCacheKey(bean.getClass(), beanName); // 判断当前bean是否正在被代理,若是正在被代理则不进行封装 if (!this.earlyProxyReferences.contains(cacheKey)) { // 对当前bean进行封装 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
从上述代码能够看出,对目标bean的封装是主要是经过wrapIfNecessary()
方法进行的,该方法就是Spring对目标bean进行代理的骨架方法。以下是该方法的实现:数组
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 判断当前bean是否在TargetSource缓存中存在,若是存在,则直接返回当前bean。这里进行如此判断的 // 缘由是在上文中,咱们讲解了如何经过本身声明的TargetSource进行目标bean的封装,在封装以后其实 // 就已经对封装以后的bean进行了代理,而且添加到了targetSourcedBeans缓存中。于是这里判断获得 // 当前缓存中已经存在当前bean,则说明该bean已经被代理过,这样就能够直接返回当前bean。 if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } // 这里advisedBeans缓存了已经进行了代理的bean,若是缓存中存在,则能够直接返回 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } // 这里isInfrastructureClass()用于判断当前bean是否为Spring系统自带的bean,自带的bean是 // 不用进行代理的;shouldSkip()则用于判断当前bean是否应该被略过 if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { // 对当前bean进行缓存 this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // 获取当前bean的Advices和Advisors Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { // 对当前bean的代理状态进行缓存 this.advisedBeans.put(cacheKey, Boolean.TRUE); // 根据获取到的Advices和Advisors为当前bean生成代理对象 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); // 缓存生成的代理bean的类型,而且返回生成的代理bean this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
在上述骨架方法中,Spring主要进行了三件事:缓存
关于上述骨架方法,这里须要说明两个点:post
shouldSkip()
方法中对当前bean判断是否应该略过期,其主要作了两件事:a. 为当前bean生成须要代理的Advisors;b. 判断生成的Advisor是否为AspectJPointcutAdvisor
类型。于是实际上判断略过的过程就是判断是否为AspectJPointcutAdvisor
,判断这个类的缘由在于Spring Aop的切面和切点的生成也能够经过在xml文件中使用<aop:config/>
标签进行。这个标签最终解析获得的Adivsor类型就是``AspectJPointcutAdvisor类型的,由于其在解析
aop:config/的时候就已经生成了Advisor,于是这里须要对这种类型的Advisor进行略过。这里
aop:config/`也是一种自定义标签,关于其解析过程,读者能够参照本人前面的博文自行阅读器源码;getAdvicesAndAdvisorsForBean()
方法就其名称而言是获取Advisors和Advices,但实际上其返回值是一个Advisor的数组。Spring Aop在为目标bean获取须要进行代理的切面逻辑的时候最终获得的是Advisor,这里Advice表示的是每一个切面逻辑中使用@Before
、@After
和@Around
等须要织入的代理方法。由于每一个代理方法都表示一个Advice,而且每一个代理方法最终都会生成一个Advisor,于是Advice和Advisor就本质而言其实没有太大的区别。Advice表示须要织入的切面逻辑,而Advisor则表示将切面逻辑进行封装以后的织入者。 虽然在shouldSkip()
方法中会为当前bean生成Advisor,可是在getAdvicesAndAdvisorsForBean()
中也仍是会获取一次,只不过在第一次生成的时候会将获得的Advisor都进行缓存,于是第二次获取时能够直接从缓存中获取。咱们这里仍是以getAdvicesAndAdvisorsForBean()
方法为准来进行讲解。以下是该方法的源码:优化
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { // 为目标bean生成Advisor List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); }
咱们继续看findEligibleAdvisors()
方法:ui
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 将当前系统中全部的切面类的切面逻辑进行封装,从而获得目标Advisor List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 对获取到的全部Advisor进行判断,看其切面定义是否能够应用到当前bean,从而获得最终须要应用的Advisor List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // 提供的hook方法,用于对目标Advisor进行扩展 extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { // 对须要代理的Advisor按照必定的规则进行排序 eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
在上述方法中,Spring Aop首先获取到了系统中全部的切面逻辑,并将其封装为了Advisor对象,而后经过遍历Advisor判断哪些Advisor是能够应用到当前bean的,最后将须要织入的Advisor返回。这里咱们看看findCandidateAdvisors()
的源码:this
protected List<Advisor> findCandidateAdvisors() { // 找到系统中实现了Advisor接口的bean List<Advisor> advisors = super.findCandidateAdvisors(); if (this.aspectJAdvisorsBuilder != null) { // 找到系统中使用@Aspect标注的bean,而且找到该bean中使用@Before,@After等标注的方法, // 将这些方法封装为一个个Advisor advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; }
能够看到,findCandidateAdvisors()
主要是经过两种方式获取切面逻辑,一种是在系统中找到实现了Advisor接口的全部类,另外一种是在找到系统中使用@Aspect
标注的类,并将其切面逻辑封装为Advisor,这两种Advisor都有多是咱们须要进行织入的切面逻辑。这里super.findCandidateAdvisors()
方法最终调用的是BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()
方法,咱们首先看看该方法的实现:.net
public List<Advisor> findAdvisorBeans() { String[] advisorNames = null; synchronized (this) { advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // 获取当前BeanFactory中全部实现了Advisor接口的bean的名称 advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } } if (advisorNames.length == 0) { return new LinkedList<>(); } // 对获取到的实现Advisor接口的bean的名称进行遍历 List<Advisor> advisors = new LinkedList<>(); for (String name : advisorNames) { // isEligibleBean()是提供的一个hook方法,用于子类对Advisor进行过滤,这里默认返回值都是true if (isEligibleBean(name)) { // 若是当前bean还在建立过程当中,则略过,其建立完成以后会为其判断是否须要织入切面逻辑 if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isDebugEnabled()) { logger.debug("Skipping currently created advisor '" + name + "'"); } } else { try { // 将当前bean添加到结果中 advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { // 对获取过程当中产生的异常进行封装 Throwable rootCause = ex.getMostSpecificCause(); if (rootCause instanceof BeanCurrentlyInCreationException) { BeanCreationException bce = (BeanCreationException) rootCause; String bceBeanName = bce.getBeanName(); if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) { if (logger.isDebugEnabled()) { logger.debug("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } continue; } } throw ex; } } } } return advisors; }
这里findAdvisorBeans()
方法逻辑其实很是简单,其主要是在BeanFactory中找打实现了Advisor接口的类,而后经过hook方法判断子类是否须要对Advisor进行过滤,最后将过滤以后的Advisor返回。debug
接下来咱们看看BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()
的实现:
public List<Advisor> buildAspectJAdvisors() { List<String> aspectNames = this.aspectBeanNames; if (aspectNames == null) { synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { List<Advisor> advisors = new LinkedList<>(); aspectNames = new LinkedList<>(); // 获取当前BeanFactory中全部的bean String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); // 对获取到的全部bean进行循环遍历 for (String beanName : beanNames) { // 判断当前bean是否为子类定制的须要过滤的bean if (!isEligibleBean(beanName)) { continue; } // 获取当前遍历的bean的Class类型 Class<?> beanType = this.beanFactory.getType(beanName); if (beanType == null) { continue; } // 判断当前bean是否使用了@Aspect注解进行标注 if (this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); // 对于使用了@Aspect注解标注的bean,将其封装为一个AspectMetadata类型。 // 这里在封装的过程当中会解析@Aspect注解上的参数指定的切面类型,如perthis // 和pertarget等。这些被解析的注解都会被封装到其perClausePointcut属性中 AspectMetadata amd = new AspectMetadata(beanType, beanName); // 判断@Aspect注解中标注的是否为singleton类型,默认的切面类都是singleton // 类型 if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { // 将BeanFactory和当前bean封装为MetadataAwareAspect- // InstanceFactory对象,这里会再次将@Aspect注解中的参数都封装 // 为一个AspectMetadata,而且保存在该factory中 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 经过封装的bean获取其Advice,如@Before,@After等等,而且将这些 // Advice都解析而且封装为一个个的Advisor List<Advisor> classAdvisors this.advisorFactory.getAdvisors(factory); // 若是切面类是singleton类型,则将解析获得的Advisor进行缓存, // 不然将当前的factory进行缓存,以便再次获取时能够经过factory直接获取 if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // 若是@Aspect注解标注的是perthis和pertarget类型,说明当前切面 // 不多是单例的,于是这里判断其若是是单例的则抛出异常 if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect " + "instantiation model is not singleton"); } // 将当前BeanFactory和切面bean封装为一个多例类型的Factory MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); // 对当前bean和factory进行缓存 this.aspectFactoryCache.put(beanName, factory); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } this.aspectBeanNames = aspectNames; return advisors; } } } if (aspectNames.isEmpty()) { return Collections.emptyList(); } // 经过全部的aspectNames在缓存中获取切面对应的Advisor,这里若是是单例的,则直接从advisorsCache // 获取,若是是多例类型的,则经过MetadataAwareAspectInstanceFactory当即生成一个 List<Advisor> advisors = new LinkedList<>(); for (String aspectName : aspectNames) { List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); // 若是是单例的Advisor bean,则直接添加到返回值列表中 if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } else { // 若是是多例的Advisor bean,则经过MetadataAwareAspectInstanceFactory生成 MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; }
对于经过@Aspect注解获取切面逻辑的方法,这里的逻辑也比较简单,Spring首先会过滤获得BeanFactory中全部标注有@Aspect的类,而后对该注解参数进行解析,判断其环绕的目标bean是单例的仍是多例的。若是是单例的,则直接缓存到advisorsCache中;若是是多例的,则将生成Advisor的factory进行缓存,以便每次获取时都经过factory获取一个新的Advisor。上述方法中主要是对@Aspect注解进行了解析,咱们前面讲过,Spring Aop的Advisor对应的是Advice,而每一个Advice都是对应的一个@Before或者@After等标注方法的切面逻辑,这里对这些切面逻辑的解析过程就在上述的advisorFactory.getAdvisors(factory)
方法调用中。这里咱们看看该方法的实现:
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { // 获取当前切面类的Class类型 Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); // 获取当前切面bean的名称 String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); // 对当前切面bean进行校验,主要是判断其切点是否为perflow或者是percflowbelow,Spring暂时不支持 // 这两种类型的切点 validate(aspectClass); // 将当前aspectInstanceFactory进行封装,这里LazySingletonAspectInstanceFactoryDecorator // 使用装饰器模式,主要是对获取到的切面实例进行了缓存,保证每次获取到的都是同一个切面实例 MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); List<Advisor> advisors = new LinkedList<>(); // 这里getAdvisorMethods()会获取全部的没有使用@Pointcut注解标注的方法,而后对其进行遍历 for (Method method : getAdvisorMethods(aspectClass)) { // 判断当前方法是否标注有@Before,@After或@Around等注解,若是标注了,则将其封装为一个Advisor Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } } // 这里的isLazilyInstantiated()方法判断的是当前bean是否应该被延迟初始化,其主要是判断当前 // 切面类是否为perthis,pertarget或pertypewithiin等声明的切面。由于这些类型所环绕的目标bean // 都是多例的,于是须要在运行时动态判断目标bean是否须要环绕当前的切面逻辑 if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { // 若是Advisor不为空,而且是须要延迟初始化的bean,则在第0位位置添加一个同步加强器, // 该同步加强器实际上就是一个BeforeAspect的Advisor Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // 判断属性上是否包含有@DeclareParents注解标注的须要新添加的属性,若是有,则将其封装为一个Advisor for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; }
在上述getAdvisors()
方法中,Spring会遍历当前切面类全部的方法,包括父类和父接口的方法,找到其中没有使用@Pointcut
注解标注的方法,而后对找到的方法进行遍历,将其封装为一个Advisor。这里咱们继续看封装为Advisor的方法:
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { // 校验当前切面类是否使用了perflow或者percflowbelow标识的切点,Spring暂不支持这两种切点 validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 获取当前方法中@Before,@After或者@Around等标注的注解,而且获取该注解的值,将其 // 封装为一个AspectJExpressionPointcut对象 AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } // 将获取到的切点,切点方法等信息封装为一个Advisor对象,也就是说当前Advisor包含有全部 // 当前切面进行环绕所须要的信息 return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
到这里Spring才将@Before,@After或@Around标注的方法封装为了一个Advisor对象。须要说明的是,这里封装成的Advisor对象只是一个半成品。所谓的半成品指的是此时其并无对切点表达式进行解析,其还只是使用一个字符串保存在AspectJExpressionPointcut对象中,只有在真正使用当前Advice逻辑进行目标bean的环绕的时候才会对其进行解析。
本文主要讲解了Spring是如何获取全部的Advisor的,即首先获取BeanFactory中全部实现了Advisor接口的bean,而后获取BeanFactory中全部标注了@Aspect注解的bean,解析该bean中的全部的切面逻辑,而且封装为一个个Advisor,这两种方式获得的Advisor都有多是最终会应用到目标bean上的切面逻辑。须要注意的是,这里获取到的Advisor并无对切点表达式进行解析,实际的解析过程是在判断当前bean是否能够应用到目标bean时进行的。这也是一个小小的优化,由于解析切点表达式的过程是一个比较复杂的过程。