Spring中单例Bean与原型Bean的区别

1 单例Bean和原型Bean分别是什么?

在面向对象编程中,单例是指当前进程、当前生命周期中只存在一个对象实例(主观),单例Bean就是某个Bean始终是单例,由Spring IOC容器维护,不管是使用@AutoWired还是@Resource注解,注入的对象都是容器实例化的同一个对象。

单例Bean默认是在加载配置文件或配置类时创建,但可以给@Componenet标记的类以及@Bean标记的配置方法上机上@Lazy上实现懒加载,即第一次注入时实现对象的创建。

原型Bean即每次注入时都会创建一个对象,即使是调用同一个方法,不同位置、时间调用,每一次调用也会注入新的对象。

2 定义单例Bean和原型Bean

2.1 在配置中定义
@Configuration
public class MyConfiguration {

    @Bean
    public MySingletonBean mySingletonBean() {
        return new MySingletonBean();
    }

    @Bean
    @Scope("prototype")
    public MyPrototypeBean myPrototypeBean() {
        return new MyPrototypeBean();
    }
}

mySingletonBean是单例Bean,myPrototypeBean是原型Bean。

2.2 @Component注解定义

定义单例Bean

@Component
public class User {

    private int id;

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

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

定义原型Bean

@Component
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class User {

    private int id;

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

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

3 @Autowired注入原型Bean的特殊方法

3.1 使用@Scope注解proxyMode属性解决
  • ScopedProxyMode.TARGET_CLASS 代理目标类,也就是使用CGLIB完成动态代理。
  • ScopedProxyMode.TARGET_CLASS 代理目标类的所有接口,使用JDK动态代理去完成代理。
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
3.2 使用@Lookup方式解决

使用@Lookup注解需要配置在返回类型为具体类型的方法上

@Lookup
public User getUser() {
    return this.user;
}

调用

for (int i = 0; i < 5; i++) {
    System.out.println(getUser());
}

3.3 getBean手动获取原型Bean

public User getUser() {
    return beanFactory.getBean(User.class);
}

实现BeanFactoryAware或者是ApplicationContextAware接口去注入BeanFactory,手动在getter当中去进行getBean

3.4 使用TargetSource配置获取原型Bean

TargetSource,从这个名字当中我们可以知道,它是一个用来获取Target的Source,也就是一个用来获取"目标对象"的"源"。对于如何获取Target的Source,这当然是可以自定义的,这也是Spring留给我们的扩展点。使用TargetSource需要配置Spring容器中默认的执行SpringAOP的AbstractAutoProxyCreator组件的相关内容,我们如何去进行配置?我们可以手写一个BeanPostProcessor去拦截下来AbstractAutoProxyCreator的执行,然后去进行配置。需要注意的是:我们既然想对AbstractAutoProxyCreator进行拦截,那么我们肯定得优先级比它还高从而去保证比这个组件在创建时,AbstractAutoProxyCreator这个组件还没完成创建!因此我们这里采用了实现PriorityOrdered接口,去保证优先级比较高。我们类似编写如下的一个配置类:


@Configurationpublic 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;
    }}

我们主要通过setCustomTargetSourceCreators方法往AbstractAutoProxyCreator组件当中添加一个自定义的TargetSourceCreator。这个TargetSourceCreator组件的作用是根据自定义相关的逻辑,最终返回一个AbstractBeanFactoryBasedTargetSource类型的TargetSource对象,它是一个典型的工厂模式!对于我们想要实现的AbstractBeanFactoryBasedTargetSource组件,我们只需要实现它的getTarget方法即可,这个getTarget方法主要体现之前所讲述的那个词,"目标对象"的"源",“源”是从什么地方体现的呢?就是在这里,我们可以自定义逻辑获取Bean!但是这里的getBeanFactory().getBean(getTargetBeanName()),其实比较有意思,这个getBeanFactory获取到的并不是我们Spring的IOC容器,而是一个克隆出来的新的BeanFactory。

发表回复

您的电子邮箱地址不会被公开。