spring通过TargetSourceCreator提前生成代理

前言

在spring aop中,如果我们的类属符合如下条件:被切面的pointcut匹配到、或者属于自定义的Advisor接口实现类,那么spring在bean完成实例化之后,会为类生成代理对象。这是众所周知的aop流程。
此外,spring还为我们提供了TargetSourceCreator接口,该接口的功能是:在bean实例化之前,就为类生成代理。
现在我们通过查看源码的方式,来了解该接口的功能。

源码剖析

1.提前生成代理

在bean实例化之前,有一个resolveBeforeInstantiation方法,如下
在这里插入图片描述如果方法返回bean不为空,则实例化结束,不进行下面的doCreateBean方法。
我们进入方法看看

在这里插入图片描述再进入applyBeanPostProcessorsBeforeInstantiation方法,
在这里插入图片描述
发现spring会遍历所有BeanPostProcessor,如果是InstantiationAwareBeanPostProcessor类型,则进入红框处方法。
AbstractAutoProxyCreator是InstantiationAwareBeanPostProcessor类型,所以此时进入AbstractAutoProxyCreator的方法,方法核心逻辑如下
在这里插入图片描述
getCustomTargetSource方法如果返回TargetSource类型,那么if逻辑里会为当前类生成代理,那么getCustomTargetSource方法逻辑是什么呢?我们进去看看
在这里插入图片描述如果customTargetSourceCreators成员有值,于是遍历该成员,调用getTargetSource方法,方法逻辑如下
在这里插入图片描述

第一个红框会调用到AbstractBeanFactoryBasedTargetSourceCreator的具体子类方法中,返回TargetSource。AbstractBeanFactoryBasedTargetSourceCreator类就是开头提到的TargetSourceCreator接口的一个抽象实现类。
第二个红框会创建一个新的BeanFactory,新的BeanFactory会拥有老的BeanFactory的属性。但需要注意的是,新BeanFactory的BeanPostProcessor集合里,去除了AbstractAutoProxyCreator类型,如下
在这里插入图片描述

到此,类的代理对象就生成了,之后的spring的实例化逻辑也不会走了。

2.调用代理对象方法

假设生成的是jdk代理。当调用对象方法时,会进入invoke方法。方法首先获取TargetSource对象
在这里插入图片描述然后调用getTarget获取原来的对象
在这里插入图片描述
在前言里提到的aop流程中,最终生成的TargetSource是SingletonTargetSource类型,于是获取到的target就是原来的对象。

而在上述流程中,我们是提前生成代理的,生成的TargetSource类型是我们钩子方法里自定义的,所以获取到的target取决于我们如何实现。

下面我们自己来实现TargetSource相关接口。

自定义TargetSource实现类

1.编写类StudentServiceImpl
@Service
public class StudentServiceImpl implements StudentService{

    @Override
    public void eat(String a) {
        System.out.println("=====StudentServiceImpl.eat");
    }
}
2.继承AbstractBeanFactoryBasedTargetSourceCreator

通过上述源码剖析流程,我们可以知道继承AbstractBeanFactoryBasedTargetSourceCreator抽象类并重写方法,就能返回自定义TargetSource。
我们此处只针对StudentServiceImpl类型

public class CustomTargetSourceCreator extends AbstractBeanFactoryBasedTargetSourceCreator {

    @Override
    protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(Class<?> beanClass, String beanName) {
        if (getBeanFactory() instanceof ConfigurableListableBeanFactory) {
            if(beanClass.isAssignableFrom(StudentServiceImpl.class)) {
                return new CustomTargetSource();
            }
        }
        return null;
    }
}

CustomTargetSource代码如下

public class CustomTargetSource extends AbstractBeanFactoryBasedTargetSource {
    @Override
    public Object getTarget() throws Exception {
        return getBeanFactory().getBean(getTargetBeanName());
    }
}

其中getBeanFactory()的值,是上述流程提到的新的BeanFactory。
由于该BeanFactory的BeanPostProcessor没有了AbstractAutoProxyCreator,所以getBeanFactory().getBean(getTargetBeanName())获取的bean,一定是原生的bean,而不是代理对象。

3.设置CustomTargetSourceCreator

光写了CustomTargetSourceCreator 还不行,因为它必须作为AbstractAutoProxyCreator类的成员,所以我们需要给AbstractAutoProxyCreator的成员赋值。此处我们自定义BeanPostProcessor

@Component
public class SetCustomTargetSourceCreator implements BeanPostProcessor, PriorityOrdered, BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public int getOrder() {
        return 45;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof AnnotationAwareAspectJAutoProxyCreator) {
            AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator = (AnnotationAwareAspectJAutoProxyCreator)bean;
            CustomTargetSourceCreator customTargetSourceCreator = new CustomTargetSourceCreator();
            customTargetSourceCreator.setBeanFactory(beanFactory);
            annotationAwareAspectJAutoProxyCreator.setCustomTargetSourceCreators(customTargetSourceCreator);
        }
        return bean;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}

到此,我们就完成了对StudentServiceImpl类型提前生成代理功能的整合。

ScopedProxyMode介绍

1.scope多例问题

在spring中,如果对象的@Scope注解的value值是DefaultListableBeanFactory.SCOPE_PROTOTYPE,表示spring启动时不会创建对象,而是使用时才创建,并且是每次都创建一个。这是众所周知的。
然而,在如下例子,虽然对象是多例,但是每次使用时,都是同一个对象。

先定义一个多例类ScopedProxyBean

@Component
@Scope(value = DefaultListableBeanFactory.SCOPE_PROTOTYPE)
public class ScopedProxyBean {

    public void code() {
        System.out.println(this.hashCode());
    }
}

再在另一个类中依赖注入ScopedProxyBean

@Component
public class MyBean {

    @Autowired
    private ScopedProxyBean scopedProxyBean;

    public void tet() {
        scopedProxyBean.code();
    }
}

开始测试

  @Test
    public void test3() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanBean.class);
        MyBean bean = applicationContext.getBean(MyBean.class);
        for (int i = 0; i < 10; i++) {
            bean.tet();
        }
    }

通过打印结果,发现每次的hashcode都是一样的,说明ScopedProxyBean对象只有一份。

2.修改注解

现在,我们将ScopedProxyBean注解改为如下,

@Scope(value = DefaultListableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)

多加了ScopedProxyMode.TARGET_CLASS
再进行测试,发现每次hashcode都是不一样,说明每次都生成了新的对象。

3.源码剖析

为什么加了ScopedProxyMode.TARGET_CLASS之后,每次都生成新的对象呢?
我们分析源码发现,当spring完成@Component注解扫描,将bean加入BeanDefinition集合之前,会进入如下方法
在这里插入图片描述
如果scope注解有ScopedProxyMode.TARGET_CLASS,那么会继续进入如下核心方法
在这里插入图片描述
第2个红框的属性设置,表示原来的ScopedProxyBean对象即使被加入了BeanDefinition集合,但最终不会被依赖注入。
第1个红框创建ScopedProxyFactoryBean,该类是一个FactoryBean类型,表示当bean实例化完成后会调用getObject方法;同时ScopedProxyFactoryBean实现了BeanFactoryAware接口,接口实现逻辑如下
在这里插入图片描述

其中设置的TargetSource是spring内置的SimpleBeanTargetSource
在这里插入图片描述
最终通过pf.getProxy生成代理对象,并加入spring的BeanDefinition集合。

4.结论

所以,当我们调用ScopedProxyBean对象的code方法时,实际进入的是代理对象的方法。通过上面我们分析的“提前生成代理”可知,代理对象方法里会通过SimpleBeanTargetSource获取target,由于是多例,所以每次获取的都是新的对象。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值