【小家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;
  }
}
相关文章
|
8天前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
|
29天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
170 73
|
16天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
68 8
|
29天前
|
Java Spring
【Spring配置相关】启动类为Current File,如何更改
问题场景:当我们切换类的界面的时候,重新启动的按钮是灰色的,不能使用,并且只有一个Current File 项目,下面介绍两种方法来解决这个问题。
|
2月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
99 5
|
2月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
54 5
|
8月前
|
安全 Java Spring
Spring之Aop的底层原理
Spring之Aop的底层原理
|
8月前
|
设计模式 Java uml
Spring AOP 原理
Spring AOP 原理
45 0
|
5月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
2月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。