Loading

Spring AOP的基本使用和工作原理

1 基本使用

1.1 依赖

Spring的AOP功能是在IoC的基础上建立起来的,因此需要引入最基础的依赖如下:

<dependency>  
    <groupId>org.springframework</groupId>  
    <artifactId>spring-context</artifactId>  
</dependency>  
<dependency>  
    <groupId>org.springframework</groupId>  
    <artifactId>spring-aspects</artifactId>  
</dependency>

1.2 开启AOP功能

默认情况下,Spring并不会开启AOP功能,需要我们在配置类中使用@EnableAspectJAutoProxy手动开启:

@EnableAspectJAutoProxy  
@Configuration  
public class AopConfig {  
}

1.3 定义切面逻辑

在正常业务逻辑中,我们首先会有一个服务类,其中定义着业务逻辑:

@Component  
public class ComponentA {  
    public void test() {  
        System.out.println("test");  
    }  
}

然后,我们会定义切面类,在其中定义拦截的规则,以及BeforeAfterAround等各种拦截方法:

@Aspect  
@Component  
public class AspectA {  
    @Pointcut("execution(* *(..))")  
    public void pointcut() {}  
  
    @Before("pointcut()")  
    public void before() {  
         System.out.println("before");  
     }  
}

在上面的例子中,在调用ComponentA#test()方法之前,总是会先执行AspectA#before()方法。

1.4 启动

需要注意的是,最基础的BeanFactory(例如DefaultListableBeanFactory)并没有集成AOP功能。为了使用Spring AOP功能,我们必须使用ApplicationContext实现类。

例如:

ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);  
ComponentA componentA = context.getBean(ComponentA.class);  
componentA.test();

会打印如下结果:

before
test

在Spring Boot项目中,默认会使用ApplicationContext实现类作为容器,所以也可以使用AOP功能。

2 工作原理

2.1 注册ConfigurationClassPostProcessor

ApplicationContext实现类读取配置类时,可以使用AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner

AnnotatedBeanDefinitionReader在在初始化时,就会注册ConfigurationClassPostProcessor

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {  
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");  
   Assert.notNull(environment, "Environment must not be null");  
   this.registry = registry;  
   this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);  
   AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);  
}

ClassPathBeanDefinitionScanner在扫描路径时,默认也会注册ConfigurationClassPostProcessor

public int scan(String... basePackages) {  
   int beanCountAtScanStart = this.registry.getBeanDefinitionCount();  
  
   doScan(basePackages);  
  
   // Register annotation config processors, if necessary.  
   if (this.includeAnnotationConfig) {  
      AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);  
   }  
  
   return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);  
}

注册的实际逻辑位于AnnotationConfigUtils#registerAnnotationConfigProcessors():`

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(  
      BeanDefinitionRegistry registry, @Nullable Object source) {  
   // ……省略
   Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);  
  
   if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {  
      RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);  
      def.setSource(source);  
      beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));  
   }    
   // ……省略
   return beanDefs;  
}

2.2 执行ConfigurationClassPostProcessor

ConfigurationClassPostProcessorBeanDefinitionRegistryPostProcessor实现类。

因此,在AbstractApplicationContext#refresh()invokeBeanFactoryPostProcessors阶段,会执行它的postProcessBeanDefinitionRegistry()方法。该方法会遍历所有已经注册的beanDefinition,对其中标注@Configuration的使用ConfigurationClassParser进行处理。

其中与AOP功能有关的是,它会解析@Import注解,注册相关配置类。

对于@EnableAspectJAutoProxy注解,它会引入AspectJAutoProxyRegistrar配置类:

@Target(ElementType.TYPE)  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Import(AspectJAutoProxyRegistrar.class)  
public @interface EnableAspectJAutoProxy {  
   boolean proxyTargetClass() default false;  
   boolean exposeProxy() default false;  
}

AspectJAutoProxyRegistrarImportBeanDefinitionRegistrar实现类,ConfigurationClassParser会调用它的registerBeanDefinitions()方法。该方法会注册AnnotationAwareAspectJAutoProxyCreator,并且根据@EnableAspectJAutoProxy的属性设置代理类型:

public void registerBeanDefinitions(  
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {  
   // 注册AnnotationAwareAspectJAutoProxyCreator,并且设置优先级为最高
   AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);  
  
   // 根据@EnableAspectJAutoProxy的属性设置配置
   AnnotationAttributes enableAspectJAutoProxy =  
         AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);  
   if (enableAspectJAutoProxy != null) {  
      if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {  
         AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);  
      }  
      if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {  
         AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);  
      }  
   }  
}

AnnotationAwareAspectJAutoProxyCreator是Spring AOP功能的核心类,它是BeanPostProcessor实现类,一方面会缓存切面信息,另一方面会将匹配的bean封装成对应代理对象。

2.3 AnnotationAwareAspectJAutoProxyCreator

2.3.1 初始化

AnnotationAwareAspectJAutoProxyCreator在注册时,设置的权重为最大值。因此,它会先被容器创建。

AnnotationAwareAspectJAutoProxyCreator实现类BeanFactoryAware接口,容器创建时会触发其setBeanFactory()方法,其中调用的initBeanFactory()方法会对其成员变量进行初始化。:

public void setBeanFactory(BeanFactory beanFactory) {  
   super.setBeanFactory(beanFactory);  
   if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {  
      throw new IllegalArgumentException(  
            "AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);  
   }  
   initBeanFactory((ConfigurableListableBeanFactory) beanFactory);  
}

AbstractAdvisorAutoProxyCreator#initBeanFactory()

protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {  
   this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory);  
}

AnnotationAwareAspectJAutoProxyCreator#initBeanFactory()

protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {  
   super.initBeanFactory(beanFactory);  
   if (this.aspectJAdvisorFactory == null) {  
      this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);  
   }  
   this.aspectJAdvisorsBuilder =  
         new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);  
}

advisorRetrievalHelper可以获取容器中所有Advisor实现类,获取其中的切面信息。BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans()

public List<Advisor> findAdvisorBeans() {  
   // Determine list of advisor bean names, if not cached already.  
   String[] advisorNames = this.cachedAdvisorBeanNames;  
   if (advisorNames == null) {  
      // Do not initialize FactoryBeans here: We need to leave all regular beans  
      // uninitialized to let the auto-proxy creator apply to them!      
      advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(  
            this.beanFactory, Advisor.class, true, false);  
      this.cachedAdvisorBeanNames = advisorNames;  
   }  
   if (advisorNames.length == 0) {  
      return new ArrayList<>();  
   }  
  
   List<Advisor> advisors = new ArrayList<>();  
   for (String name : advisorNames) {  
      if (isEligibleBean(name)) {  
         if (this.beanFactory.isCurrentlyInCreation(name)) {  
            if (logger.isTraceEnabled()) {  
               logger.trace("Skipping currently created advisor '" + name + "'");  
            }  
         }  
         else {  
            try {  
               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.isTraceEnabled()) {  
                        logger.trace("Skipping advisor '" + name +  
                              "' with dependency on currently created bean: " + ex.getMessage());  
                     }  
                     // Ignore: indicates a reference back to the bean we're trying to advise.  
                     // We want to find advisors other than the currently created bean itself.                     
                     continue;  
                  }  
               }  
               throw ex;  
            }  
         }  
      }  
   }  
   return advisors;  
}

aspectJAdvisorsBuilder可以获取容器中所有标注@Aspect注解的bean,并通过aspectJAdvisorFactory创建成Advisor对象缓存起来。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 ArrayList<>();  
            aspectNames = new ArrayList<>();  
            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<?> beanType = this.beanFactory.getType(beanName, false);  
               if (beanType == null) {  
                  continue;  
               }  
               if (this.advisorFactory.isAspect(beanType)) {  
                  aspectNames.add(beanName);  
                  AspectMetadata amd = new AspectMetadata(beanType, beanName);  
                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {  
                     MetadataAwareAspectInstanceFactory factory =  
                           new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);  
                     List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);  
                     if (this.beanFactory.isSingleton(beanName)) {  
                        this.advisorsCache.put(beanName, classAdvisors);  
                     }  
                     else {  
                        this.aspectFactoryCache.put(beanName, factory);  
                     }  
                     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;  
            return advisors;  
         }  
      }  
   }  
  
   if (aspectNames.isEmpty()) {  
      return Collections.emptyList();  
   }  
   List<Advisor> advisors = new ArrayList<>();  
   for (String aspectName : aspectNames) {  
      List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);  
      if (cachedAdvisors != null) {  
         advisors.addAll(cachedAdvisors);  
      }  
      else {  
         MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);  
         advisors.addAll(this.advisorFactory.getAdvisors(factory));  
      }  
   }  
   return advisors;  
}

2.3.2 postProcessAfterInitialization

AnnotationAwareAspectJAutoProxyCreator创建后,才会创建其他bean对象。

由于AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor接口,每个bean实例化后,都会调用它的postProcessAfterInitialization()方法:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {  
   if (bean != null) {  
      Object cacheKey = getCacheKey(bean.getClass(), beanName);  
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {  
         return wrapIfNecessary(bean, beanName, cacheKey);  
      }  
   }  
   return bean;  
}

实际业务逻辑在AbstractAutoProxyCreator#wrapIfNecessary()

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;  
   }  
  
   // 判断指定bean是否需要进行代理,并返回对应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;  
}

AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean()方法中,它会调用AbstractAdvisorAutoProxyCreator#findEligibleAdvisors()方法查找指定bean对应的切面信息:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {  
   // 调用advisorRetrievalHelper和aspectJAdvisorsBuilder从容器中查找所有切面信息
   List<Advisor> candidateAdvisors = findCandidateAdvisors();  
   // 筛选出指定bean对应的切面信息
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);  
   extendAdvisors(eligibleAdvisors);  
   if (!eligibleAdvisors.isEmpty()) {  
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);  
   }  
   return eligibleAdvisors;  
}

通过AbstractAutoProxyCreator#createProxy()方法,可以为bean对象创建AOP代理:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,  
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {  
  
   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {  
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);  
   }  
  
   ProxyFactory proxyFactory = new ProxyFactory();  
   proxyFactory.copyFrom(this);  
  
   if (proxyFactory.isProxyTargetClass()) {  
      // Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)  
      if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {  
         // Must allow for introductions; can't just set interfaces to the proxy's interfaces only.  
         for (Class<?> ifc : beanClass.getInterfaces()) {  
            proxyFactory.addInterface(ifc);  
         }  
      }  
   }  
   else {  
      // No proxyTargetClass flag enforced, let's apply our default checks...  
      if (shouldProxyTargetClass(beanClass, beanName)) {  
         proxyFactory.setProxyTargetClass(true);  
      }  
      else {  
         evaluateProxyInterfaces(beanClass, proxyFactory);  
      }  
   }  
  
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);  
   proxyFactory.addAdvisors(advisors);  
   proxyFactory.setTargetSource(targetSource);  
   customizeProxyFactory(proxyFactory);  
  
   proxyFactory.setFrozen(this.freezeProxy);  
   if (advisorsPreFiltered()) {  
      proxyFactory.setPreFiltered(true);  
   }  
  
   // Use original ClassLoader if bean class not locally loaded in overriding class loader  
   ClassLoader classLoader = getProxyClassLoader();  
   if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {  
      classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();  
   }  
   return proxyFactory.getProxy(classLoader);  
}

根据@EnableAspectJ配置和bean对象是否实现接口等信息,最终会根据JdkDynamicAopProxyCglibAopProxy创建AOP代理对象。

对于JdkDynamicAopProxy,它的代理逻辑位于JdkDynamicAopProxy#invoke()方法,它会创建Advisor的调用链,然后按顺序进行触发:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
   Object oldProxy = null;  
   boolean setProxyContext = false;  
  
   TargetSource targetSource = this.advised.targetSource;  
   Object target = null;  
  
   try {  
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {  
         // The target does not implement the equals(Object) method itself.  
         return equals(args[0]);  
      }  
      else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {  
         // The target does not implement the hashCode() method itself.  
         return hashCode();  
      }  
      else if (method.getDeclaringClass() == DecoratingProxy.class) {  
         // There is only getDecoratedClass() declared -> dispatch to proxy config.  
         return AopProxyUtils.ultimateTargetClass(this.advised);  
      }  
      else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&  
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {  
         // Service invocations on ProxyConfig with the proxy config...  
         return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);  
      }  
  
      Object retVal;  
  
      if (this.advised.exposeProxy) {  
         // Make invocation available if necessary.  
         oldProxy = AopContext.setCurrentProxy(proxy);  
         setProxyContext = true;  
      }  
  
      // 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<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);  
  
      // Check whether we have any advice. If we don't, we can fall back on direct  
      // reflective invocation of the target, and avoid creating a MethodInvocation.      
      if (chain.isEmpty()) {  
         // We can skip creating a MethodInvocation: just invoke the target directly  
         // Note that the final invoker must be an InvokerInterceptor so we know it does         
         // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.         
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);  
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);  
      }  
      else {  
         // We need to create a method invocation...  
         MethodInvocation invocation =  
               new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
         // Proceed to the joinpoint through the interceptor chain.  
         retVal = invocation.proceed();  
      }  
  
      // Massage return value if necessary.  
      Class<?> returnType = method.getReturnType();  
      if (retVal != null && retVal == target &&  
            returnType != Object.class && returnType.isInstance(proxy) &&  
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {  
         // Special case: it returned "this" and the return type of the method  
         // is type-compatible. Note that we can't help if the target sets         
         // a reference to itself in another returned object.         
         retVal = proxy;  
      }  
      else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {  
         throw new AopInvocationException(  
               "Null return value from advice does not match primitive return type for: " + method);  
      }  
      return retVal;  
   }  
   finally {  
      if (target != null && !targetSource.isStatic()) {  
         // Must have come from TargetSource.  
         targetSource.releaseTarget(target);  
      }  
      if (setProxyContext) {  
         // Restore old proxy.  
         AopContext.setCurrentProxy(oldProxy);  
      }  
   }  
}

3 小结

以上介绍了Spring AOP的基本使用和工作原理,更多细节可以查看相关源码。

Spring AOP是对IoC基础扩展点使用的典型案例。

它基于ApplicationContext实现类的基础功能ConfigurationClassPostProcessor,通过@Import注解注册自定义的AnnotationAwareAspectJAutoProxyCreator

由于AnnotationAwareAspectJAutoProxyCreatorBeanPostProcessor,可以创建并缓存所有切面bean,并且在业务bean实例化后动态决定是否选择是否进行AOP代理。

posted @ 2023-01-30 14:59  Xianuii  阅读(352)  评论(1编辑  收藏  举报