【小家Spring】Spring AOP的核心类:AbstractAdvisorAutoProxy自动代理创建器深度剖析(AnnotationAwareAspectJAutoProxyCreator)(中)

简介: 【小家Spring】Spring AOP的核心类:AbstractAdvisorAutoProxy自动代理创建器深度剖析(AnnotationAwareAspectJAutoProxyCreator)(中)

关于TargetSource以及TargetSourceCreator 的使用,以及为何使用它们?这里我推荐想了解原因的,请参照这里:

为什么需要TargetSource

Spring Aop之Target Source详解


这两篇文章我看了讲得还是不错的,能到到扫盲的目的~

我们当然可以自定义TargetSource,一般我们就这么用,使用ProxyFactoryBean去指定配置某个Bean,而不是用自动代理创建器(它会使用默认的TargetSource,当然你可以重写它的)


<!-- 这个我们也可以自己实现,也可以用Spring为我们提供好的 -->
<bean id="poolTargetSource"    
    class="org.springframework.aop.target.CommonsPoolTargetSource">  
    <property name="targetBeanName"><value>businessObject</value></property>  
    <property name="maxSize"><value>25</value></property>  
</bean>  
<!-- 让具体的Bean代理,去使用我们自己指定的targetSource(一般只有非单例的需要自己去实现) -->
<bean id="businessObject"   class="org.springframework.aop.framework.ProxyFactoryBean">  
    <property name="targetSource"><ref local="poolTargetSource"/></property>  
    <property name="interceptorNames"><value>myInterceptor</value></property>  
</bean>  


但是有时候我们希望参与到自动代理层面去,使用我们自己的targetSource,其实我们可以这么做,自己实现一个TargetSourceCreator:(原理:AbstractAutoProxyCreator#getCustomTargetSource())


绝大多数情况下调用者不会自己去实现TargetSourceCreator,而是Spring采用默认的SingletonTargetSource去生产AOP对象 LazyInitTargetSource在JMX的MBean中使用较为广泛,所以是右边要了解的


// 它是根据BeanName强相关的,所以现在和Spring容器是强关联的
@FunctionalInterface
public interface TargetSourceCreator {
  @Nullable
  TargetSource getTargetSource(Class<?> beanClass, String beanName);
}


image.png


从Spring自带的实现中可议看出,都是和Spring容器相关的实现。LazyInitTargetSourceCreator:和@Lazy属性有关(LazyInitTargetSource)。QuickTargetSourceCreator:可能来自CommonsPool2TargetSource、可能来自ThreadLocalTargetSource、可能来自PrototypeTargetSource(和BeanName有关),可能是null。

public class QuickTargetSourceCreator extends AbstractBeanFactoryBasedTargetSourceCreator {
  // 可以看出,它的分类事根据BeanName以xxx开头来分辨的~~~这是一种约束
  public static final String PREFIX_COMMONS_POOL = ":";
  public static final String PREFIX_THREAD_LOCAL = "%";
  public static final String PREFIX_PROTOTYPE = "!";
  @Override
  @Nullable
  protected final AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(
      Class<?> beanClass, String beanName) {
    if (beanName.startsWith(PREFIX_COMMONS_POOL)) {
      CommonsPool2TargetSource cpts = new CommonsPool2TargetSource();
      cpts.setMaxSize(25);
      return cpts;
    }
    else if (beanName.startsWith(PREFIX_THREAD_LOCAL)) {
      return new ThreadLocalTargetSource();
    }
    else if (beanName.startsWith(PREFIX_PROTOTYPE)) {
      return new PrototypeTargetSource();
    }
    else {
      // No match. Don't create a custom target source.
      return null;
    }
  }
}


然后我们要想自定义一个,还是比较麻烦的,可议这么干~


@Order(Ordered.HIGHEST_PRECEDENCE) //我们希望自己定义的Bean最先被加载  所以这里优先级调到最高了 覆盖@EnableAspectJAutoProxy它自动导入的Bean
@Configuration
public class RootConfig {
    // 这里需要考虑到的是Spring对配置文件的加载顺序(@Import的解析顺序),我们必须在@EnableAspectJAutoProxy这个注解之前生效才有效
    // 同一个Configuration内解析顺序为:Process any @ComponentScan annotations ...  Process any @Import annotations  ... Process individual @Bean methods
    // 所以问题就在这里:如果我们把`@EnableAspectJAutoProxy`注解下载@Component这种Lite配置上,肯定会被先扫描进行的  所以这里:我极力不推荐这么做,而是放在Configuration正规的配置文件里
    // 然后又因为同一个@Import早于@Bean之前执行,所以我们必须放在不同的@Configaration文件内,然后通过控制配置类的顺序来达到目的~~~~(它支持@Order进行排序)
    // 参考链接:https://blog.csdn.net/f641385712/article/details/88554592 和  参考方法ConfigurationClassPostProcessor#processConfigBeanDefinitions
    @Bean(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME) //Bean名称务必只能是这个
    public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
        AnnotationAwareAspectJAutoProxyCreator creator = new AnnotationAwareAspectJAutoProxyCreator();
        // creator.setProxyTargetClass(true); 建议这里不要设置,还是取用注解的值来灵活控制最好了
        // 手动指定进去一个TargetSourceCreator(责任链模式:我们可以指定多个 ... )
        creator.setCustomTargetSourceCreators(new LazyInitTargetSourceCreator());
        return creator;
    }
}
@Order(Ordered.LOWEST_PRECEDENCE)
@EnableAspectJAutoProxy
@Configuration
public class AopConfig {
}


显示是比较麻烦的,而且涉及到了很多的Spring解析@Configuration底层原理东西,一般不建议这么弄吧~


AbstractAutoProxyCreator的创建步骤就是上面源码分析的,它就相当于一个代理创建的模版,规定了一些步骤。获取Advisor的getAdvicesAndAdvisorsForBean由各子类自己去实现~~~


接下来主要是根据对方法getAdvicesAndAdvisorsForBean()的实现不一样,策略也就有两种了。它有两个直接实现:BeanNameAutoProxyCreator和AbstractAdvisorAutoProxyCreator。


BeanNameAutoProxyCreator


顾名思义,它和Advisor无关,只和BeanName有关(只有名字匹配上了,都会给创建一个代理类)


所以我认为它是个半自动的,哪些需要创建代理,还需要我们自己指定(虽然支持*通配符)


从这篇博文:

【小家Spring】面向切面编程之—Spring AOP的原理讲解以及源码分析(Cannot find current proxy: Set ‘exposeProxy’ property on )

我们已经能知道,若我们使用@EnableAspectJAutoProxy启动自动代理的话,Spring自动会给我们注册一个Bean:AnnotationAwareAspectJAutoProxyCreator,它是一个AbstractAdvisorAutoProxyCreator,和AspectJ注解相关~


本文我们要Demo一下BeanNameAutoProxyCreator,很简单以这样做就可以了:


    @Bean
    public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        // 给所有以serviceImpl结尾的类创建代理对象(支持正则)  备注:aliases也是被支持的
        // 注意此处若只写`*Service` 是匹配不上helloServiceImpl
        beanNameAutoProxyCreator.setBeanNames("*ServiceImpl");
        // 备注:它要想使用拦截,只能通过setInterceptorNames,从容器内拿Advice的实现类(自己书写)
        beanNameAutoProxyCreator.setInterceptorNames("myMethodInteceptor");
        return beanNameAutoProxyCreator;
    }


就这样配置,MyMethodInteceptor这个拦截器,它就会作用于拦截所有的*ServiceImpl上。


备注:此时我们采用了BeanNameAutoProxyCreator,自然就不用再@EnableAspectJAutoProxy,自然@Aspect切面也就不生效了。 当然,也可以开启的,这样他俩就联合生效了(但不太建议去这么使用)


需要注意的是:如果一个对象被切多次(比如使用@Async、事务都会创建代理对象),最终这个对象代理可能是对层的:如下所示:


image.png


另外,如果你想用自己注册的@Bean代替@EnableAspectJAutoProxy默认给你注册的自动创建器,那么你可以注册一个Bean名称如下的Bean即可:


// 手动注册一个自动代理创建器,且名字务必叫AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME
@Bean(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME) 
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
  ...
}


特别提醒:首先我强烈不建议这么去做

然后,此处要注意Bean定义信息的加载循序,否则也是不会生效的~~~~~~(需要对原理更多的了解吧) 毕竟人家判断是根据Bean的定义信息来判断的:if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {} 而这个就和@Configuration配置文件的加载顺序有关喽(Spring环境下不好控制~~~)


BeanNameAutoProxyCreator源码一览:

//@since 10.10.2003  可以看出这个类出现得非常早
public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator {
  @Nullable
  private List<String> beanNames;
  public void setBeanNames(String... beanNames) {
    Assert.notEmpty(beanNames, "'beanNames' must not be empty");
    this.beanNames = new ArrayList<>(beanNames.length);
    for (String mappedName : beanNames) {
      // 对mappedName做取出空白处理
      this.beanNames.add(StringUtils.trimWhitespace(mappedName));
    }
  }
  // simpleMatch并不是完整的正则。但是支持*这种通配符,其余的不支持哦
  protected boolean isMatch(String beanName, String mappedName) {
    return PatternMatchUtils.simpleMatch(mappedName, beanName);
  }
  // 这里面注意一点:BeanNameAutoProxyCreator的此方法并没有去寻找Advisor,所以需要拦截的话
  // 只能依靠:setInterceptorNames()来指定拦截器。它是根据名字去Bean容器里取的
  @Override
  @Nullable
  protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    if (this.beanNames != null) {
      for (String mappedName : this.beanNames) {
        // 显然这里面,如果你针对的是FactoryBean,也是兼容的~~~
        if (FactoryBean.class.isAssignableFrom(beanClass)) {
          if (!mappedName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            continue;
          }
          // 对BeanName进行处理,去除掉第一个字符
          mappedName = mappedName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        // 匹配就返回PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS 而不是再返回null了
        if (isMatch(beanName, mappedName)) {
          return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
        }
        // 这里需要注意的是,如国存在Bean工厂,哪怕任意一个alias匹配都是可以的~~~
        BeanFactory beanFactory = getBeanFactory();
        if (beanFactory != null) {
          String[] aliases = beanFactory.getAliases(beanName);
          for (String alias : aliases) {
            if (isMatch(alias, mappedName)) {
              return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
            }
          }
        }
      }
    }
    return DO_NOT_PROXY;
  }
}
相关文章
|
7天前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
9天前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
21天前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
2月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
23 0
Spring高手之路22——AOP切面类的封装与解析
|
2月前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
38 0
|
2月前
|
缓存 安全 Java
Spring AOP 中两种代理类型的限制
【8月更文挑战第22天】
16 0
|
16天前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
34 1
|
4月前
|
前端开发 Java 数据库
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
|
25天前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
【9月更文挑战第9天】AOP(面向切面编程)通过分离横切关注点提高模块化程度,如日志记录、事务管理等。Micronaut AOP基于动态代理机制,在应用启动时为带有特定注解的类生成代理对象,实现在运行时拦截方法调用并执行额外逻辑。通过简单示例展示了如何在不修改 `CalculatorService` 类的情况下记录 `add` 方法的参数和结果,仅需添加 `@Loggable` 注解即可。这不仅提高了代码的可维护性和可扩展性,还降低了引入新错误的风险。
36 13
|
2月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
下一篇
无影云桌面