栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring源码系列(五)——@Aspect源码解析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring源码系列(五)——@Aspect源码解析

首先我们看一下@Aspect的简单实例代码
首先到Config类中添加@EnableAspectJAutoProxy注解打开AOP功能

@ComponentScan(basePackages = "com.kennor.test")
@EnableAspectJAutoProxy
public class Config {
}

自定义注解StudyTrainAnnotation用于标识连接点Joinpoint

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface StudyTrainAnnotation {
}

自定义切面StudyTrainAspect类
在切面方法studyTrain中对目标方法进行增强,具体业务逻辑是增加分数。

@Component
@Aspect
public class StudyTrainAspect {
   
   @Pointcut("@annotation(StudyTrainAnnotation)")
   public void studyTrainPointcut(){
   }
   
   @Around("studyTrainPointcut()")
   public Object studyTrain(ProceedingJoinPoint joinPoint) throws Throwable {
      System.out.println("进行考前强化训练,强化训练后初始分数增加30分");
      Object[] args = joinPoint.getArgs();
      args[0] = (Integer)args[0] + 30;
      Integer score = (Integer) joinPoint.proceed(args);
      System.out.println("考后老师评分失误,多评10分");
      score+=10;
      return score;
   }
}

增加Student接口

在StudentA和StudentB实现Student接口
在StudentA的examing方法中增加@StudyTrainAnnotation注解,标识StudentA的examing是需要增强的目标对象Target。


然后调用

运行结果如下:

可以看到StudentA考试成绩增加到了90分,而StudentB没有使用AOP所以仍是50分。
为了做对比,看一下StudentB和StudentA的区别

通过Debug可以看到,StudentA的引用实际上是代理对象,然后代理对象的targetSource属性保存着StudentA。接下来我们将分析一下整个流程到底是怎么样的。
接下来我们就着这个实例,分析一下源码,看看Spring底层源码是如何实现的。
首先我们看到Config类上的@EnableAspectJAutoProxy注解


可以看到EnableAspectJAutoProxy使用了@import标签导入了AspectJAutoProxyRegistrar类
而关于@import标签的处理在AnnotationConfigApplicationContext的构造方法中创建的ConfigurationClassPostProcessor中进行的,跟处理@ComponentScan注解一样,具体看下面方法注释的第4点,会将AspectJAutoProxyRegistrar注册到Spring中,这里不再赘述。

@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {
   // 1.处理类上的@Component注解
   if (configClass.getmetadata().isAnnotated(Component.class.getName())) {
      // Recursively process any member (nested) classes first
      processMemberClasses(configClass, sourceClass);
   }
   // Process any @PropertySource annotations
   // 2.处理类上的@PropertySource注解
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getmetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
      if (this.environment instanceof ConfigurableEnvironment) {
         processPropertySource(propertySource);
      }
      ... ...
   }
   // Process any @ComponentScan annotations
   // 3.处理类上的@ComponentScan注解
   Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getmetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionevaluator.shouldSkip(sourceClass.getmetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      ... ...
   }
   // Process any @import annotations
   // 4.处理@import注解
   processimports(configClass, sourceClass, getimports(sourceClass), true);
   // Process any @importResource annotations
   // 5.处理@importResource注解
   AnnotationAttributes importResource =
         AnnotationConfigUtils.attributesFor(sourceClass.getmetadata(), importResource.class);
   if (importResource != null) {
      ... ...
   }
   // Process individual @Bean methods
   // 6.处理@Bean注解
   Set beanMethods = retrieveBeanMethodmetadata(sourceClass);
   for (Methodmetadata methodmetadata : beanMethods) {
      configClass.addBeanMethod(new BeanMethod(methodmetadata, configClass));
   }
   ... ...
   return null;
}

接着我们看一下AspectJAutoProxyRegistrar

可以看到AspectJAutoProxyRegistrar实现了importBeanDefinitionRegistrar接口,所以registerBeanDefinitions方法会在ApplicationContext的refresh方法中的invokeBeanFactoryPostProcessors里被调用。

在AspectJAutoProxyRegistrar的registerBeanDefinitions方法中主要是往Spring注册了AnnotationAwareAspectJAutoProxyCreator

// AOP的处理入口
// 注册AnnotationAwareAspectJAutoProxyCreator,主要用于处理@AspectJ注解
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);



可以看到最终AnnotationAwareAspectJAutoProxyCreator被封装到BeanDefinition注册到Spring中。
而在AnnotationAwareAspectJAutoProxyCreator所继承的父类AbstractAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor接口

其中实现了一个很重要的方法:postProcessAfterInitialization,这里会创建返回Bean对应的代理对象。

通过方法的名称我们也可以知道postProcessAfterInitialization会在Bean初始化完成后调用,这是我们回到Bean的创建方法doCreateBean中,我们在之前的文章有提过此方法中的第5点注释处的方法就是处理生成代理对象的。

接着我们继续看一下initializeBean的具体源码

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
   ... ...

   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
      // 1.调用BeanPostProcessor实现类的postProcessBeforeInitialization方法
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }

   try {
      // 2.调用InitializingBean的afterPropertiesSet方法
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   ... ...
   if (mbd == null || !mbd.isSynthetic()) {
      // 3.调用BeanPostProcessor实现类的postProcessBeforeInitialization方法
      // 如果配置了代理,则AbstractAutoProxyCreator会在此处调用,生成代理对象
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }

   return wrappedBean;
}

我们可以看到注释第3点处的applyBeanPostProcessorsAfterInitialization方法

最终调用到我们一开始所介绍的AnnotationAwareAspectJAutoProxyCreator父类AbstractAutoProxyCreator的postProcessAfterInitialization方法中

可以看到核心流程在wrapIfNecessary里,具体源码如下:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   // 判断当前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.
   // 1.根据当前beanMame对应的bean去扫描匹配bean类中的方法是否有符合Pointcut规则(@Pointcut)的切面方法(@Around、@Before、@After...)
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if (specificInterceptors != DO_NOT_PROXY) {
      // 将当前Bean标识为有切面方法
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      // 2.创建代理对象
      Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   // 将当前Bean标识为无切面方法
   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

首先我们看wrapIfNecessary方法中注释第1点处的getAdvicesAndAdvisorsForBean方法的具体代码


继续看findEligibleAdvisors的具体是如何获取Advisor

这里我们继续看一下aspectJAdvisorsBuilder的buildAspectJAdvisors方法

public List buildAspectJAdvisors() {
   List aspectNames = this.aspectBeanNames;

   if (aspectNames == null) {
      synchronized (this) {
         aspectNames = this.aspectBeanNames;
         if (aspectNames == null) {
            List advisors = new ArrayList<>();
            aspectNames = new ArrayList<>();
            // 获取容器中所有的beanName
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                  this.beanFactory, Object.class, true, false);
            for (String beanName : beanNames) {
               if (!isEligibleBean(beanName)) {
                  continue;
               }
               // We must be careful not to instantiate beans eagerly as in this case they
               // would be cached by the Spring container but would not have been weaved.
               // 获取Class对象
               Class beanType = this.beanFactory.getType(beanName);
               if (beanType == null) {
                  continue;
               }
               // 1.判断类上是否有@Aspect注解
               if (this.advisorFactory.isAspect(beanType)) {
                  aspectNames.add(beanName);
                  // 解析封装@Aspect注解信息
                   Aspectmetadata amd = new Aspectmetadata(beanType, beanName);
                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                     metadataAwareAspectInstanceFactory factory =
                           new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                     // 2.获取所有切面方法封装类Advisor集合
                     List classAdvisors = this.advisorFactory.getAdvisors(factory);
                     if (this.beanFactory.isSingleton(beanName)) {
                        this.advisorsCache.put(beanName, classAdvisors);
                     }
                     else {
                        this.aspectFactoryCache.put(beanName, factory);
                     }
                     // 3.将Advisor添加到列表中
                     advisors.addAll(classAdvisors);
                  }
                  else {
                     // Per target or per this.
                     if (this.beanFactory.isSingleton(beanName)) {
                        throw new IllegalArgumentException("Bean with name '" + beanName +
                              "' is a singleton, but aspect instantiation model is not singleton");
                     }
                     metadataAwareAspectInstanceFactory factory =
                           new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                     this.aspectFactoryCache.put(beanName, factory);
                     advisors.addAll(this.advisorFactory.getAdvisors(factory));
                  }
               }
            }
            this.aspectBeanNames = aspectNames;
            // 4.返回Advisor列表
            return advisors;
         }
      }
   }
   ... ...
   return advisors;
}

此时我们示例代码中的StudyTrainAspect会通过buildAspectJAdvisors()注释1处的判断,走到注释2处的方法里面。

@Override
public List getAdvisors(metadataAwareAspectInstanceFactory aspectInstanceFactory) {
   Class aspectClass = aspectInstanceFactory.getAspectmetadata().getAspectClass();
   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 ArrayList<>();
   // 1. getAdvisorMethods返回没有@Pointcut注解的方法集合
   for (Method method : getAdvisorMethods(aspectClass)) {
      // 2. 创建Advisor
      Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }
   ... ...
   return advisors;
}

首先我们getAdvisors()注释1处getAdvisorMethods方法

getAdvisorMethods获取到没有@Pointcut的方法,所以method列表中会记录到示例代码StudyTrainAspect的studyTrain方法
接着我们返回getAdvisors()注释2处。

getPointcut方法会去解析studyTrain上的@Around注解的信息。


@Nullable
public static  A findAnnotation(Method method, @Nullable Class annotationType) {
   Assert.notNull(method, "Method must not be null");
   if (annotationType == null) {
      return null;
   }
   AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType);
   A result = (A) findAnnotationCache.get(cacheKey);

   if (result == null) {
      Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
      // 从当前方法上寻找annotationType类型的注解信息
      result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType);
      if (result == null) {
         // 从当前类实现的接口中寻找annotationType类型的注解
         result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces());
      }
      // 从父类中寻找annotationType类型的注解信息
      Class clazz = method.getDeclaringClass();
      while (result == null) {
         ... ...
      }

      ... ...
   }

   return result;
}

最后可以看到注释2处将信息封装到AspectJAnnotation中返回到getPointcut方法中,此时getPointcut注释1处获取到的aspectJAnnotation对象如下:

最后再到注释2处创建AspectJexpressionPointcut返回出去。

可以看到AspectJexpressionPointcut保存着示例代码StudyTrainAspect的studyTrain方法@Around注解的pointcut表达式:studyTrainPointcut()。
接着我们看回到getAdvisor方法的注释2处

通过入参我们可以发现这里的InstantiationModelAwarePointcutAdvisorImpl装了示例代码pointcut表达式(studyTrainPointcut())和Advice增强方法(studyTrain())等信息。
接着我们看一下InstantiationModelAwarePointcutAdvisorImpl类


可以看到这个类最终实现了Advisor接口,而且类中有几个重要的属性,我们看一下在构造方法中instantiatedAdvice属性是如何赋值的。


getAdvice具体代码如下

@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJexpressionPointcut expressionPointcut,
      metadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

   Class candidateAspectClass = aspectInstanceFactory.getAspectmetadata().getAspectClass();
   validate(candidateAspectClass);

   // 获取Method方法上的注解
   AspectJAnnotation aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   if (aspectJAnnotation == null) {
      return null;
   }
   ... ...
   AbstractAspectJAdvice springAdvice;

   switch (aspectJAnnotation.getAnnotationType()) {
      case AtPointcut:
          // 忽略@Pointcut注解
         return null;
      case AtAround:
         // 封装@Around注解
         // AspectJAroundAdvice实现了MethodInterceptor接口
         springAdvice = new AspectJAroundAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
      case AtBefore:
         // 封装@Before注解
         // AspectJMethodBeforeAdvice实现了MethodBeforeAdvice接口
         springAdvice = new AspectJMethodBeforeAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
      case AtAfter:
         // 封装@After注解
         // AspectJAfterAdvice实现了MethodInterceptor接口
         springAdvice = new AspectJAfterAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
      case AtAfterReturning:
         // 封装@afterReturning注解
         // AspectJAfterReturningAdvice实现了AfterReturningAdvice接口
         springAdvice = new AspectJAfterReturningAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterReturningAnnotation.returning())) {
            springAdvice.setReturningName(afterReturningAnnotation.returning());
         }
         break;
      case AtAfterThrowing:
         // 封装@afterThrowing注解
         // AspectJAfterThrowingAdvice实现了MethodInterceptor接口
         springAdvice = new AspectJAfterThrowingAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
            springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
         }
         break;
      default:
         throw new UnsupportedOperationException(
               "Unsupported advice type on method: " + candidateAdviceMethod);
   }

   // Now to configure the advice...
   springAdvice.setAspectName(aspectName);
   springAdvice.setDeclarationOrder(declarationOrder);
   String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
   if (argNames != null) {
      springAdvice.setArgumentNamesFromStringArray(argNames);
   }
   springAdvice.calculateArgumentBindings();

   return springAdvice;
}

可以看到在getAdvice方法中根据注解封装成对应的Advice,其中封装Around注解和After注解的Advice实现了MethodInterceptor接口,而封装After注解的Before实现的是MethodBeforeAdvice接口。
所以在我们示例代码中StudyTrainAspect中的带有@Around注解的studyTrain方法会被封装成AspectJAroundAdvice对象

接着getAdvisor方法将Advisor返回到buildAspectJAdvisors中,buildAspectJAdvisors再将Advisor保存到列表里继续获取其他Advisor,最后将Advisor列表返回到findEligibleAdvisors。

接着我们继续看findEligibleAdvisors注释第2点处,看看Bean和Advisor是如何匹配,在我们示例代码就是StudentA的examing方法与StudyTrainAspect的studyTrain的匹配过程。

findEligibleAdvisors这块的匹配比较复杂,我们跟着断点大概看一下






上面这个方法中的FuzzyBoolean类会记录匹配结果



这里通过hasAnnotation判断examing方法是否包含StudyTraninAnnotation注解,由于examing是有添加@StudyTraninAnnotation注解的,所以会返回YES



然后将match封装到ShadowMatchImpl中返回出去








最后通过匹配,把Advisor添加到eligibleAdvisors中返回出去
接着回到findEligibleAdvisors方法中

此时就将StudentA匹配到的Advisor返回出去

接着就回到了一开始的wrapIfNecessary中

我们接下来将用注释1处获取到的Advisor用于注释2处创建StudentA的代理对象。

在createProxy方法中可以看到创建了代理工厂proxyFacotry,并且将Advisor添加到代理工厂中,然后通过proxyFacotry获取代理对象。

我们先看一下getProxy中的注释1处创建AOP代理


由于我们的StudentA实现了Student接口,所以采用JDK代理模式,因此会创建一个JDKDynamicAopProxy代理对象返回
而JDKDynamicAopProxy实现了动态代理的关键接口InvocationHandler,因此StudentA代理对象调用接口中的examing方法的时候会调用回此类的invoke方法中,这个我们在后面介绍。
接着我们先回到getProxy方法中的注释第2处,看看getProxy方法

选择JDKDynamicAopProxy对象

可以看到需要代理的接口中就包含了Student接口,接着继续往下走到Proxy.newProxyInstance方法中,接下来就是我们熟悉的动态代理相关的代码了


最后生成动态代理类返回到一开始提到的wrapIfNecessary方法中。




最后返回到doCreateBean方法中,此时exposedObject持有着StudentA的代理对象。

最后doCreateBean将exposedObject返回出去

接着进入到getSingleton中,最终studentA缓存的对象是StudentA的代理对象。

这也就是为什么在示例代码中我们获取到的StudentA是个代理对象的原因。
接着我们在前面分析的时候已经提到,StudentA代理对象调用接口中的examing方法的时候会调用代理对象的invoke方法,也就是JDKDynamicAopProxy的invoke方法。
接着我们看一下在JDKDynamicAopProxy的invoke方法中是如何调用到StudentA的examing方法和StudyTrainAspect的studyTrain方法

@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
       // 从advised中获取targetSource
   // 代理实例就保存在targetSource的target属性中
   TargetSource targetSource = this.advised.targetSource;
   Object target = null;
   try {
      ... ...
      // Get as late as possible to minimize the time we "own" the target,
      // in case it comes from a pool.
      // 获取代理示例
      target = targetSource.getTarget();
      Class targetClass = (target != null ? target.getClass() : null);

      // Get the interception chain for this method.
      // 获取符合当前代理示例方法的切面执行链
      List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      if (chain.isEmpty()) {
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         // We need to create a method invocation...
         // 创建MethodInvocation
         MethodInvocation invocation =
               new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         // Proceed to the joinpoint through the interceptor chain.
         // 1.调用切面执行链方法
         retVal = invocation.proceed();
      }
      ... ...
      return retVal;
   }
   ... ...
}
 

看到注释1处继续调用proceed方法

调用到我们的@Around注解的方法studyTrain的封装类AspectJAroundAdvice的invoke方法,继续进入到AspectJAroundAdvice的invoke方法中


这时就调用到StudyTrainAspect的studyTrain方法中了

然后我们修改参数后调用了joinPoint的proceed方法

再次来到proceed方法中

此时链条已经执行完毕,进入到invokeJoinponit方法中,最后会反射调用到StudentA的examing方法


然后返回到proceed方法中

然后继续返回到StudyTrainAspect的studyTrain方法中的joinPoint.proceed处

然后执行往studyTrain继续往上返回出去


最后执行完毕。

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号