Spring扩展点-TargetSource
1. 什么是TargetSource
TargetSource
我们从字面意思去进行翻译,翻译为"目标源",也就是说它是一个用来获取"目标对象"的"源头",既然是"源头",我们猜测,可以自定义"源"。
要使用TargetSource
,我们必须自定义自己的TargetSourceCreator
(工厂模式去创建目标对象),并将其加入到Spring
的默认的核心AOP
组件(AbstractAutoProxyCreator
)当中去。
既然要去进行配置,我们就要在这个组件创建时去进行干预,往其中加入我们自定义的TargetSourceCreator
。如何对这个组件的创建去进行干预?通过Spring
为我们提供的BeanPostProcessor
这个扩展点就很显然可以进行干预,我们想要对AbstractAutoProxyCreator
进行干预,我们得比它先创建啊,因此我们要保证优先级比它还高,我们实现PriorityOrdered
接口去自定义优先级,保证了优先级一定比AbstractAutoProxyCreator
高。
来感受一下TargetSource
:
@Configuration
public class Config implements BeanPostProcessor, PriorityOrdered, BeanFactoryAware {
BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
AbstractBeanFactoryBasedTargetSource targetSource = new AbstractBeanFactoryBasedTargetSource() {
@Override
public Object getTarget() throws Exception {
return getBeanFactory().getBean(getTargetBeanName());
}
};
if (bean instanceof AbstractAutoProxyCreator) {
AbstractBeanFactoryBasedTargetSourceCreator creator = new AbstractBeanFactoryBasedTargetSourceCreator() {
@Override
protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(Class<?> beanClass, String beanName) {
if (User.class.isAssignableFrom(beanClass)) {
return targetSource;
}
return null;
}
};
creator.setBeanFactory(beanFactory);
((AbstractAutoProxyCreator) bean).setCustomTargetSourceCreators(creator);
}
return bean;
}
}
我们往容器中加入了一个自己实现的AbstractBeanFactoryBasedTargetSourceCreator
,并且如果对象匹配的话,就返回一个自定义的AbstractBeanFactoryBasedTargetSource
对象。
这样,不管我们的User
对象配置的是否是单例的,当我们从容器中getBean
去获取User
这个对象时,都会变成原型的,这也是实现Autowired
注入原型Bean
的一种实现方法。
2. TargetSource
这个扩展点从何而来?
首先我们要知道Spring
的AOP
的核心组件就是AbstractAutoProxyCreator
,我们用到的所有的AOP
组件(图中框选的,就是会用到的AOP
组件),全部都是基于AbstractAutoProxyCreator
组件提供的模板方法去进行的重写,因此AbstractAutoProxyCreator
这个组件称它为SpringAOP
的核心组件,一点毛病都没有。
很明显,要想实现对Bean
进行干预,那么我们可以确定的是AbstractAutoProxyCreator
它本身肯定是一个BeanPostProcessor
,但是它不仅仅是一个BeanPostProcessor
,它实现了BeanPostProcessor
的子接口SmartInstantiationAwareBeanPostProcessor
,从这个接口的名字当中我们可以理解它的意思,"对Bean去进行很智能的实例化的后置处理器",而实现这个接口自然会支持对每个常规Bean
的实例化前后的干预、初始化前后的干预功能。
我们主要关注它在实例化之前做了什么干预?
image.png它会先从自定义的TargetSource
当中去判断当前Bean
是否有匹配的TargetSource
,如果有配置TargetSource
,那么就会获取这个对象的Advisor
以及Advice
的列表,接着就使用createProxy
方法去创建代理(createProxy
方法就是SpringAOP
当中创建代理的核心逻辑,去匹配JDK
动态代理/CGLIB
动态代理,然后创建代理对象)。
我们来看它是怎么获取TargetSource
的?
其实很简单对吧,一个策略模式,遍历容器中自己配置的TargetSourceCreator
,挨个调用它的getTargetSource
方法去获取TargetSource
。
有个需要主要的点,它只要匹配了TargetSource
,一定就会创建代理并return
代理对象,如果不匹配TargetSource
,那么直接return null
。
我们需要在大环境下去看看Spring
是在什么时候完成实例化之前的方法的回调的?
我们可以看到它是在doGetBean
调用之前去进行的干预,而如果有TargetSource
,那么它直接完成代理返回了,后续的所有Bean
的创建和初始化逻辑都不走了!
这些逻辑都不走有什么问题吗?代表了XXXAware
接口所有都不会生效,@Autowired
/@Resource
/@Value
这些注解标注的属性赋值,都不会走!
3. AbstractBeanFactoryBasedTargetSourceCreator
AbstractBeanFactoryBasedTargetSourceCreator
这个组件很值得我们进行研究。
第一步获取TargetSource
的逻辑,是需要我们进行实现的。第二步获取的内部BeanFactory是什么BeanFactory?
我们可以看到是克隆一个原来的BeanFactory
出来,然后将里面所有的AopInfrastructureBean
组件都进行移除,而这个组件是什么?其实就是我们的AOP
的核心组件,相当于就是将克隆出来的BeanFactory
当中的所有AOP
的组件全部remove
掉。
第3步当中,将拷贝出来的BeanDefinition
注册到我们内部的TargetSource
内部的BeanFactory
当中,并且设置了作用域为原型,这也是之前我们看到的从容器中获取User
对象获取到的是原型对象的原因。第4步是设置targetBeanName
和内部BeanFactory
到TargetSource
对象。
相当于就是说,TargetSource
隔离了一套BeanFactory
,还维护了targetBeanName
,当我们使用getBeanFactory().getBean(getTargetBeanName())
这样的代码去获取Bean
时,实际上就是使用的隔离的BeanFactory
,从里面去getBean
。
我们来看CGLIB
拦截目标方法是如何去执行代理方法的(JDK
代理拦截方式同理)?我们可以看到,它就是调用了getTarget
方法去获取的目标对象。也就是去调用的getBeanFactory().getBean(getTargetBeanName())
这样的代码去获取目标对象。
也就是说,执行目标方法的目标Bean
,是从TargetSource
隔离的BeanFactory
当中去获取对象的,而隔离的BeanFactory
中AOP
相关的组件已经被移除了,因此不会存在getTarget
获取到代理对象的可能性(如果获取到的是代理对象,那么很明显会出现StackOverFlow,因此不断的调用代理方法,没有尽头)。
4. TargetSource
可以做什么?
实际上TargetSource
它实现的功能就是,运行时动态获取对象。比如User
对象,虽然我往容器中加入的只是一个单纯的单例JavaBean
,但是我们可以在运行时获取到的是原型的User
对象,在运行时从ThreadLocal
当中获取对象,甚至是运行时从对象池当中去获取对象。
比如我们完全可以自定义一个ThreadLocal
的来源,我们将目标对象放入容器中,然后Autowired
注入后的,我们在业务代码中获取到的目标对象就是来自于ThreadLocal
当中,不用再进行ThreadLocal
对象再去进行手动get
等操作了,看起来是不是很!!!
Spring
当中为这些常见的场景都做了相应的实现,当我们需要这些场景时,我们完全可以使用Spring
为我们提供的相应的TargetSource
。如果我们对Spring
给我们提供的组件不满意,我们完全可以,重写Spring
框架提供的一些内部组件的模板方法,甚至是自己定义一个TargetSourceCreator
以及TargetSource
去自定义目标对象的来源。