关于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); }
从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、事务都会创建代理对象),最终这个对象代理可能是对层的:如下所示:
另外,如果你想用自己注册的@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; } }