Spring扩展点-TargetSource

2021-12-30  本文已影响0人  Wannay

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这个扩展点从何而来?

首先我们要知道SpringAOP的核心组件就是AbstractAutoProxyCreator,我们用到的所有的AOP组件(图中框选的,就是会用到的AOP组件),全部都是基于AbstractAutoProxyCreator组件提供的模板方法去进行的重写,因此AbstractAutoProxyCreator这个组件称它为SpringAOP的核心组件,一点毛病都没有。

image.png

很明显,要想实现对Bean进行干预,那么我们可以确定的是AbstractAutoProxyCreator它本身肯定是一个BeanPostProcessor,但是它不仅仅是一个BeanPostProcessor,它实现了BeanPostProcessor的子接口SmartInstantiationAwareBeanPostProcessor,从这个接口的名字当中我们可以理解它的意思,"对Bean去进行很智能的实例化的后置处理器",而实现这个接口自然会支持对每个常规Bean的实例化前后的干预、初始化前后的干预功能。

我们主要关注它在实例化之前做了什么干预?

image.png

它会先从自定义的TargetSource当中去判断当前Bean是否有匹配的TargetSource,如果有配置TargetSource,那么就会获取这个对象的Advisor以及Advice的列表,接着就使用createProxy方法去创建代理(createProxy方法就是SpringAOP当中创建代理的核心逻辑,去匹配JDK动态代理/CGLIB动态代理,然后创建代理对象)。

我们来看它是怎么获取TargetSource的?

image.png

其实很简单对吧,一个策略模式,遍历容器中自己配置的TargetSourceCreator,挨个调用它的getTargetSource方法去获取TargetSource

有个需要主要的点,它只要匹配了TargetSource,一定就会创建代理并return代理对象,如果不匹配TargetSource,那么直接return null

我们需要在大环境下去看看Spring是在什么时候完成实例化之前的方法的回调的?

image.png image.png

我们可以看到它是在doGetBean调用之前去进行的干预,而如果有TargetSource,那么它直接完成代理返回了,后续的所有Bean的创建和初始化逻辑都不走了!

这些逻辑都不走有什么问题吗?代表了XXXAware接口所有都不会生效,@Autowired/@Resource/@Value这些注解标注的属性赋值,都不会走!

3. AbstractBeanFactoryBasedTargetSourceCreator

AbstractBeanFactoryBasedTargetSourceCreator这个组件很值得我们进行研究。

image.png

第一步获取TargetSource的逻辑,是需要我们进行实现的。第二步获取的内部BeanFactory是什么BeanFactory?

image.png

我们可以看到是克隆一个原来的BeanFactory出来,然后将里面所有的AopInfrastructureBean组件都进行移除,而这个组件是什么?其实就是我们的AOP的核心组件,相当于就是将克隆出来的BeanFactory当中的所有AOP的组件全部remove掉。

第3步当中,将拷贝出来的BeanDefinition注册到我们内部的TargetSource内部的BeanFactory当中,并且设置了作用域为原型,这也是之前我们看到的从容器中获取User对象获取到的是原型对象的原因。第4步是设置targetBeanName和内部BeanFactoryTargetSource对象。

相当于就是说,TargetSource隔离了一套BeanFactory,还维护了targetBeanName,当我们使用getBeanFactory().getBean(getTargetBeanName())这样的代码去获取Bean时,实际上就是使用的隔离的BeanFactory,从里面去getBean

我们来看CGLIB拦截目标方法是如何去执行代理方法的(JDK代理拦截方式同理)?我们可以看到,它就是调用了getTarget方法去获取的目标对象。也就是去调用的getBeanFactory().getBean(getTargetBeanName())这样的代码去获取目标对象。

也就是说,执行目标方法的目标Bean,是从TargetSource隔离的BeanFactory当中去获取对象的,而隔离的BeanFactoryAOP相关的组件已经被移除了,因此不会存在getTarget获取到代理对象的可能性(如果获取到的是代理对象,那么很明显会出现StackOverFlow,因此不断的调用代理方法,没有尽头)。

image.png

4. TargetSource可以做什么?

实际上TargetSource它实现的功能就是,运行时动态获取对象。比如User对象,虽然我往容器中加入的只是一个单纯的单例JavaBean,但是我们可以在运行时获取到的是原型的User对象,在运行时从ThreadLocal当中获取对象,甚至是运行时从对象池当中去获取对象。

比如我们完全可以自定义一个ThreadLocal的来源,我们将目标对象放入容器中,然后Autowired注入后的,我们在业务代码中获取到的目标对象就是来自于ThreadLocal当中,不用再进行ThreadLocal对象再去进行手动get等操作了,看起来是不是很!!!

Spring当中为这些常见的场景都做了相应的实现,当我们需要这些场景时,我们完全可以使用Spring为我们提供的相应的TargetSource。如果我们对Spring给我们提供的组件不满意,我们完全可以,重写Spring框架提供的一些内部组件的模板方法,甚至是自己定义一个TargetSourceCreator以及TargetSource去自定义目标对象的来源。

image.png

个人博客:http://wanna1314y.top:8090/

上一篇下一篇

猜你喜欢

热点阅读