SpringBoot2 | Spring AOP 原理深度源码分析(八)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: SpringBoot2 | Spring AOP 原理深度源码分析(八)


概述

AOP(Aspect-Oriented Programming) 面向切面编程。Spring Aop 在 Spring框架中的地位举足轻重,主要用于实现事务、缓存、安全等功能。本篇主要是对源码进行深度分析。

主要介绍以下三个方面:

  • Spring AOP 多种代理机制相关核心类介绍。
  • Spring Boot 中AOP注解方式源码分析。
  • Spring Boot 1.x 版本和 2.x版本 AOP 默认配置的变动。

Spring AOP 多种代理机制相关核心类介绍

先介绍一些Spring Aop中一些核心类,大致分为三类:

  • advisorCreator,继承 spring ioc的扩展接口 beanPostProcessor,主要用来扫描获取 advisor。
  • advisor:顾问的意思,封装了spring aop中的切点和通知。
  • advice:通知,也就是aop中增强的方法。

对以上三类核心类对应的 UML 分别来看。

advisorCreator:

  • AbstractAutoProxyCreator:Spring 为Spring AOP 模块暴露的可扩展抽象类,也是 AOP 中最核心的抽象类。Nepxion Matrix 框架便是基于此类对AOP进行扩展和增强。
  • BeanNameAutoProxyCreator:根据指定名称创建代理对象(阿里大名鼎鼎的连接池框架druid也基于此类做了扩展)。通过设置 advisor,可以对指定的 beanName 进行代理。支持模糊匹配。
  • AbstractAdvisorAutoProxyCreator:功能比较强大,默认扫描所有Advisor的实现类。相对于根据Bean名称匹配,该类更加灵活。动态的匹配每一个类,判断是否可以被代理,并寻找合适的增强类,以及生成代理类。
  • DefaultAdvisorAutoProxyCreatorAbstractAdvisorAutoProxyCreator的默认实现类。可以单独使用,在框架中使用AOP,尽量不要手动创建此对象。
  • AspectJAwareAdvisorAutoProxyCreator:Aspectj的实现方式,也是Spring Aop中最常用的实现方式,如果用注解方式,则用其子类AnnotationAwareAspectJAutoProxyCreator
  • AnnotationAwareAspectJAutoProxyCreator:目前最常用的AOP使用方式。spring aop 开启注解方式之后,该类会扫描所有@Aspect()注释的类,生成对应的adviosr。目前SpringBoot框架中默认支持的方式,自动配置。

advisor:

  • StaticMethodMatcherPointcut:静态方法切面,抽象类。定义了一个classFilter,通过重写getClassFilter()方法来指定切面规则。另外实现了StaticMethodMatcher接口,通过重写matches来指定方法匹配规则。
  • StaticMethodMatcherPointcutAdvisor:静态方法匹配切面顾问,同未抽象类,扩展了切面排序方法。
  • NameMatchMethodPointcut:名称匹配切面,通过指定方法集合变量mappedNames,模糊匹配。
  • NameMatchMethodPointcutAdvisor:方法名称切面顾问,内部封装了 NameMatchMethodPointcut,通过设置方法名称模糊匹配规则和通知来实现切面功能。
  • RegexpMethodPointcutAdvisor:正则表达式切面顾问,可设置多个正则表达式规则,通过内部封装的 JdkRegexpMethodPointcut解析正则表达式。
  • DefaultPointcutAdvisor:默认切面顾问,比较灵活。可自由组合切面和通知。
  • InstantiationModelAwarePointcutAdvisorImplspringboot自动装配的顾问类型,也是最常用的一种顾问实现。在注解实现的切面中,所有@Aspect类,都会被解析成该对象。

advice:

  • AspectJMethodBeforeAdvice:前置通知,AspectJ中 before 属性对应的通知(@Before标注的方法会被解析成该通知),,在切面方法执行之前执行。
  • AspectJAfterReturningAdvice:后置通知,AspectJ中 afterReturning 属性对应的通知(@AfterReturning 标注的方法会被解析成该通知),在切面方法执行之后执行,如果有异常,则不执行。 注意:该通知与AspectJMethodBeforeAdvice对应。
  • AspectJAroundAdvice:环绕通知,AspectJ中 around 属性对应的通知(@Around标注的方法会被解析成该通知),在切面方法执行前后执行。
  • AspectJAfterAdvice:返回通知,AspectJ中 after 属性对应的通知(@After 标注的方法会被解析成该通知),不论是否异常都会执行。

可以看出 Spring AOP 提供的实现方式很多,但是殊途同归。

具体使用方式已上传至 github: github.com/admin801122…

Spring Boot 中AOP注解方式源码分析

Spring Aop使用方式很多,从上面的 API 也可以看出。本篇就基于最常用的注解实现方式,对源码深入分析。

@Aspect
@Component
public class LogableAspect {
    @Pointcut("@annotation(com.springboot2.spring5.springAop.aspect.Logable)")
    public void aspect() {
    }
    @Around("aspect()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        //...
        Object returnValue =  point.proceed(point.getArgs());
        //...
        return returnValue;
    }
}
复制代码

这是实际项目中,使用AOP最常见的形式,基于注解实现。如今springboot大行其道,我们就从springboot中的EnableAspectJAutoProxy自动配置开始。

大致流程主要分为三个步骤: 1: 创建AnnotationAwareAspectJAutoProxyCreator对象 2: 扫描容器中的切面,创建PointcutAdvisor对象 3: 生成代理类

分别来分析以上三个步骤。

1: 创建AnnotationAwareAspectJAutoProxyCreator对象

首先来看AnnotationAwareAspectJAutoProxyCreator对象初始化的过程。springboot中,aop同样以自动装配的方式,所以还是要从spring.factories开始:

# Auto Configure
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
复制代码
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
    AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
  @Configuration
  @EnableAspectJAutoProxy(proxyTargetClass = false)
  @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
  public static class JdkDynamicAutoProxyConfiguration {
  }
  @Configuration
  @EnableAspectJAutoProxy(proxyTargetClass = true)
  @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
  public static class CglibAutoProxyConfiguration {
  }
}
复制代码

具体来看: (1)该配置类的加载前提是什么?

@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
    AnnotatedElement.class })
复制代码

条件注解依赖的配置类均被引入到spring-boot-starter-aop中,只需引入该依赖即可自动装配。 而且可以看到spring.aop.auto默认为true,并不需要手动开启。 所以很多同学在springboot项目中使用 aop 的时候,习惯在启动类上引入@EnableAspectJAutoProxy,其实完全不必要。保证项目中有spring-boot-starter-aop依赖即可。

(2)上述代码通过spring.aop.proxy-target-class变量来控制proxyTargetClass的变量,最终都会加载@EnableAspectJAutoProxy配置。 spring.aop.proxy-target-class默认为true,该变量相当关键,控制 spring aop 代理类的生成方式,具体后面详细介绍。

继续跟进EnableAspectJAutoProxy

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
  @Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //注册 AnnotationAwareAspectJAutoProxyCreator
    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    AnnotationAttributes enableAspectJAutoProxy =
        AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    //将 aop 代理方式相关的变量设置到 AopConfigUtils,创建代理类时会读取变量
    if (enableAspectJAutoProxy != null) {
      if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
      }
      if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
        AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
      }
    }
  }
}
复制代码
@Nullable
  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
      @Nullable Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
  }
复制代码

上述代码可以看到注册了一个切面相关BeanDefinition,正是上面提到的类: AnnotationAwareAspectJAutoProxyCreator,并设置了代理方式配置变量: proxyTargetClass,默认为true。

这里只是创建BeanDefinition,并没有实例化和初始化该对象。那什么时候会触发呢? 上面的 uml 图可以看到,该类继承的顶层接口为 BeanPostProcessor。我们知道BeanPostProcessor实现类会提前初始化,由PostProcessorRegistrationDelegate触发,具体细节之前博客有提到: SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

该类又继承BeanFactoryAware,所以在其在实例化 bean 后,会触发setBeanFactory()方法,最终会触发 initBeanFactory方法:

@Override
  protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    super.initBeanFactory(beanFactory);
    if (this.aspectJAdvisorFactory == null) {
      //advisor 工厂类
      this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
    }
    //用于创建 advisor
    this.aspectJAdvisorsBuilder =
        new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
  }
复制代码

至此,AnnotationAwareAspectJAutoProxyCreator BeanDefinition创建完毕。

2: 扫描容器中的切面,创建PointcutAdvisor对象

在spring ioc流程加载的过程中,会触发 beanPostProcessor 扩展接口, 而AnnotationAwareAspectJAutoProxyCreator又是SmartInstantiationAwareBeanPostProcessor的子类,所以该扩展接口正是 aop 实现的入口。

该接口的触发在实例化 bean 之后,初始化 bean之前,具体来看:

@Override
  public Object 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)) {
        return null;
      }
      //判断当前bean是否可以被代理,然后存入advisedBeans
      if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return null;
      }
    }
    // 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());
      //如果最终可以获得代理类,则返回代理类,直接执行实例化后置通知方法
      return proxy;
    }
    return null;
  }
复制代码

来看一下判定 bean 是否被代理的方法依据:

@Override
  protected boolean isInfrastructureClass(Class<?> beanClass) {
    return (super.isInfrastructureClass(beanClass) ||
        (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));
  }
复制代码
private boolean hasAspectAnnotation(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() + "]");
    }
    return retVal;
  }
复制代码

重点来看 shouldSkip方法:

@Override
  protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    // TODO: Consider optimization by caching the list of the aspect names
    //获取所有的候选顾问类 Advisor
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    for (Advisor advisor : candidateAdvisors) {
      if (advisor instanceof AspectJPointcutAdvisor &&
          ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
        return true;
      }
    }
    return super.shouldSkip(beanClass, beanName);
  }
复制代码

上述代码通过findCandidateAdvisors()方法来获取所有的候选 advisor:

@Override
  protected List<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注解类, 解析成Advisor 
    if (this.aspectJAdvisorsBuilder != null) {
      advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
  }
复制代码

继续跟进buildAspectJAdvisors方法,会触发 ReflectiveAspectJAdvisorFactory中的getAdvisors方法:

@Override
  public List<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 =
        new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
    List<Advisor> advisors = new LinkedList<>();
    //遍历该类所有方法,根据方法判断是否能获取到对应 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 = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
      advisors.add(0, instantiationAdvisor);
    }
    // Find introduction fields.
    //获取 @DeclareParents 注解修饰的属性(并不常用)
    for (Field field : aspectClass.getDeclaredFields()) {
      Advisor advisor = getDeclareParentsAdvisor(field);
      if (advisor != null) {
        advisors.add(advisor);
      }
    }
    return advisors;
  }
复制代码

继续来看getAdvisor方法:

@Override
  @Nullable
  public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
      int declarationOrderInAspect, String aspectName) {
    validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
    //根据候选方法名,来获取对应的 pointCut
    AspectJExpressionPointcut expressionPointcut = getPointcut(
        candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
      return null;
    }
    //如果能获取到 pointCut,则将切点表达式 expressionPointcut、当前
    对象ReflectiveAspectJAdvisorFactory、 方法名等包装成 advisor 对象
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
        this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
  }
复制代码
protected static 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) {
        return foundAnnotation;
      }
    }
    return null;
  }
复制代码

InstantiationModelAwarePointcutAdvisorImpl的构造方法会触发构造通知对象:

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
      MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    //......
    //根据注解类型,匹配对应的通知类型
    switch (aspectJAnnotation.getAnnotationType()) {
      //前置通知
      case AtBefore:
        springAdvice = new AspectJMethodBeforeAdvice(
            candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
        break;
      //最终通知
      case AtAfter:
        springAdvice = new AspectJAfterAdvice(
            candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
        break;
      //后置通知
      case AtAfterReturning:
        springAdvice = new AspectJAfterReturningAdvice(
            candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
        AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
        if (StringUtils.hasText(afterReturningAnnotation.returning())) {
          springAdvice.setReturningName(afterReturningAnnotation.returning());
        }
        break;
      //异常通知
      case AtAfterThrowing:
        springAdvice = new AspectJAfterThrowingAdvice(
            candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
        AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
        if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
          springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
        }
        break;
      //环绕通知
      case AtAround:
        springAdvice = new AspectJAroundAdvice(
            candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
        break;
      //切面
      case AtPointcut:
        if (logger.isDebugEnabled()) {
          logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
        }
        return null;
      default:
        throw new UnsupportedOperationException(
            "Unsupported advice type on method: " + candidateAdviceMethod);
    }
    //......
  }
复制代码

可以看到,根据@Aspect类中方法的注解类型,生成对应的advice,并通过通知的构造方法,将通知增强方法,切面表达式传入到通知当中。

到这里InstantiationModelAwarePointcutAdvisorImpl对象构造完毕。

3: 生成代理类

上面创建advisor的逻辑发生在扩展接口中的postProcessBeforeInstantiation,实例化之前执行,如果有自定义的TargetSource指定类,则则直接生成代理类,并直接执行初始化之后的方法postProcessAfterInitialization。这种情况使用不多,常规代理类还是在postProcessAfterInitialization中创建,也就是 IOC 最后一个扩展方法。

@Override
  public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
    if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      //处理循环依赖的判断
      if (!this.earlyProxyReferences.contains(cacheKey)) {
        return wrapIfNecessary(bean, beanName, cacheKey);
      }
    }
    return bean;
  }
复制代码
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;
    }
    // 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());
      return proxy;
    }
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
  }
复制代码

上述方法通过调用getAdvicesAndAdvisorsForBean()方法来获取advisor,该方法最终会调用findEligibleAdvisors()Eligible意为有资格的,合适的。具体来看下:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    //这里会对获取的advisor进行筛选
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    //添加一个默认的advisor,执行时用到。
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
  }
复制代码

最终的筛选规则在AopUtils中:

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    //......
    for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor) {
        // already processed
        continue;
      } 
      //调用 canApply 方法,遍历所有的方法进行匹配
      if (canApply(candidate, clazz, hasIntroductions)) {
        eligibleAdvisors.add(candidate);
      }
    }
    //......
  }
复制代码

调用canApply方法,遍历被代理类的所有的方法,跟进切面表达式进行匹配,如果有一个方法匹配到,也就意味着该类会被代理。 匹配方法是借助org.aspectj.weaver.internal.tools实现,也就是AspectJ框架中的工具类,有兴趣的可以自行查看。

重点来看一下代理生成方式:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
        throw new AopConfigException("TargetSource cannot determine target class: " +
            "Either an interface or a target is required for proxy creation.");
      }
      //如果代理目标是接口或者Proxy类型,则走jdk类型
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
        return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
    }
    else {
      return new JdkDynamicAopProxy(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])));
  }
复制代码

主要就是判断AdvisedSupportinterfaces变量中是否设置了接口,

意思是如果一个类实现了接口,把接口设置到该方法的变量中,但是不是一定会设置到该变量中,具体设置接口的代码如下:

可以看到如果用默认配置也就是proxyTargetClass为true时,只有一种情况会走jdk代理方法,就是代理类为接口类型(注意:代理类是接口类型,并不是指接口类是否实现了接口)或者代理类是Proxy类型,否则全部走cglib代理。所以,平时使用中,代理类大部分还是用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的扩展接口中来创建代理类。



目录
相关文章
|
2天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
8 1
|
3天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
11 1
|
26天前
|
NoSQL Java Redis
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
这篇文章介绍了Redis的基本命令,并展示了如何使用Netty框架直接与Redis服务器进行通信,包括设置Netty客户端、编写处理程序以及初始化Channel的完整示例代码。
30 1
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
|
27天前
|
缓存 JavaScript Java
Spring之FactoryBean的处理底层源码分析
本文介绍了Spring框架中FactoryBean的重要作用及其使用方法。通过一个简单的示例展示了如何通过FactoryBean返回一个User对象,并解释了在调用`getBean()`方法时,传入名称前添加`&`符号会改变返回对象类型的原因。进一步深入源码分析,详细说明了`getBean()`方法内部对FactoryBean的处理逻辑,解释了为何添加`&`符号会导致不同的行为。最后,通过具体代码片段展示了这一过程的关键步骤。
Spring之FactoryBean的处理底层源码分析
|
11天前
|
Java Spring 容器
springboot @RequiredArgsConstructor @Lazy解决循环依赖的原理
【10月更文挑战第15天】在Spring Boot应用中,循环依赖是一个常见问题,当两个或多个Bean相互依赖时,会导致Spring容器陷入死循环。本文通过比较@RequiredArgsConstructor和@Lazy注解,探讨它们解决循环依赖的原理和优缺点。@RequiredArgsConstructor通过构造函数注入依赖,使代码更简洁;@Lazy则通过延迟Bean的初始化,打破创建顺序依赖。两者各有优势,需根据具体场景选择合适的方法。
26 4
|
18天前
|
Java 测试技术 开发者
springboot学习四:Spring Boot profile多环境配置、devtools热部署
这篇文章主要介绍了如何在Spring Boot中进行多环境配置以及如何整合DevTools实现热部署,以提高开发效率。
39 2
|
18天前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
39 1
|
18天前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
14 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
18天前
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
16 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
|
18天前
|
Java 测试技术 Spring
springboot学习三:Spring Boot 配置文件语法、静态工具类读取配置文件、静态工具类读取配置文件
这篇文章介绍了Spring Boot中配置文件的语法、如何读取配置文件以及如何通过静态工具类读取配置文件。
21 0
springboot学习三:Spring Boot 配置文件语法、静态工具类读取配置文件、静态工具类读取配置文件