单例bean依赖原型bean的 如何保证原型bean生命周期

简介:

  将原型bean注入到单例bean,会破坏原型bean的生命周期,使其的生命周期变成与单例bean相同。

  好了,废话不多少,直接上栗子,边吃边说。

情况模拟:

1、单例bean

@Component
public class SingletonBean {
    @Autowired
    private PrototypeBean prototypeBean;

    public void printTime() {
        System.out.println("SingletonBean: "+this.hashCode());
        System.out.println("prototypeBean 注入给单例的:"+prototypeBean.hashCode());
        prototypeBean.printTime();
    }

}

2、原型bean

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
     Long timeMilis;

    public PrototypeBean() {
        //System.out.println("PrototypeBean Constructor");
        this.timeMilis = System.currentTimeMillis();
    }

    public void printTime() {
        System.out.println("PrototypeBean原始bean:"+this.hashCode());
        System.out.println("timeMils:" + timeMilis);
    }
}

3、测试类

public class PrototypeTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext=
                new AnnotationConfigApplicationContext(PrototypeBeanConfig.class);

        SingletonBean singletonBean = (SingletonBean) applicationContext.getBean("singletonBean");
        SingletonBean singletonBean1 = (SingletonBean) applicationContext.getBean("singletonBean");
        SingletonBean singletonBean2 = (SingletonBean) applicationContext.getBean("singletonBean");


        singletonBean.printTime();
        singletonBean1.printTime();
        singletonBean2.printTime();
    }
}

4、测试结果:

 

 

 

5、原因分析:  

要知道, SingletonBean 是单例模式的,只会被Spring进行类扫面的时候创建一次,然后存入到单例池SingletonObjects。之后每次使用SingletonBean,都会从单例池中获取,不会再次创建。因为属性的注入发生在创建bean的过程中,所以也只会在创建的时候注入一次。也就是说,在SingletonBean创建过程中,将生成一个PrototypeBean类的bean作为属性注入到SingletonBean中。而以后使用SingletonBean 的 prototypeBean 属性时不会重新生成PrototypeBean类的bean,只会使用已经注入到SingletonBean属性上的bean。因此PrototypeBean失去了原型bean的生命周期。

 

解决方式:

一、单例bean实现ApplicationContextAware接口

  单例bean不让spring进行属性原型bean的属性注入。而是每次使用的时候,通过getBean的方式获取原型bean。  

 

1、单例bean

@Component
public class SingletonBeanImplementApplicationContextAware implements ApplicationContextAware {

    ApplicationContext applicationContext;

    private PrototypeBean prototypeBean;

    public void printTime() {
        System.out.println("SingletonBean: " + this.hashCode());
        prototypeBean= (PrototypeBean) applicationContext.getBean("prototypeBean");
        System.out.println("prototypeBean 注入给单例的:"+ prototypeBean.hashCode());
        prototypeBean.printTime();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

 

2、原型bean

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
     Long timeMilis;

    public PrototypeBean() {
        //System.out.println("PrototypeBean Constructor");
        this.timeMilis = System.currentTimeMillis();
    }

    public void printTime() {
        System.out.println("PrototypeBean原始bean:"+this.hashCode());
        System.out.println("timeMils:" + timeMilis);
    }
}

3、测试类

public class PrototypeTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext=
                new AnnotationConfigApplicationContext(PrototypeBeanConfig.class);


        SingletonBeanImplementApplicationContextAware singletonBean = (SingletonBeanImplementApplicationContextAware) applicationContext.getBean("singletonBeanImplementApplicationContextAware");
        SingletonBeanImplementApplicationContextAware singletonBean1 = (SingletonBeanImplementApplicationContextAware) applicationContext.getBean("singletonBeanImplementApplicationContextAware");
        SingletonBeanImplementApplicationContextAware singletonBean2 = (SingletonBeanImplementApplicationContextAware) applicationContext.getBean("singletonBeanImplementApplicationContextAware");


        singletonBean.printTime();
        singletonBean1.printTime();
        singletonBean2.printTime();
    }
}

4、测试结果:

 

 

 

5、原因分析:

  让单例bean实现ApplicationContextAware接口,重写setApplicationContext方法,获取到ApplicationContex,每次使用 prototypeBean的时候,都从ApplicationContext中通过getBean的方式来获取最新的原型bean,每次获取的原型bean都是Spring新创建的bean。因此可以保证原型bean的生命周期。

  

二、@Lookup

  通过加有@Lookup的抽象方法来获取prototypeBean对象。

1、单例bean

@Component
public  abstract   class SingletonBeanByLookup {

    private PrototypeBean prototypeBean;
    @Lookup
    public abstract  PrototypeBean getPrototypeBean();

    public void printTime() {
        System.out.println(this.hashCode());
        prototypeBean= getPrototypeBean()  ;
        System.out.println(prototypeBean.hashCode());
        prototypeBean.printTime();
    }

}

 

2、原型bean

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
     Long timeMilis;

    public PrototypeBean() {
        //System.out.println("PrototypeBean Constructor");
        this.timeMilis = System.currentTimeMillis();
    }

    public void printTime() {
        System.out.println("PrototypeBean原始bean:"+this.hashCode());
        System.out.println("timeMils:" + timeMilis);
    }
}

3、测试类

public class PrototypeTest {
    public static void main(String[] args) throws InterruptedException {
        AnnotationConfigApplicationContext applicationContext=
                new AnnotationConfigApplicationContext(PrototypeBeanConfig.class);


        SingletonBeanByLookup singletonBean = (SingletonBeanByLookup) applicationContext.getBean("singletonBeanByLookup");
        SingletonBeanByLookup singletonBean1 = (SingletonBeanByLookup) applicationContext.getBean("singletonBeanByLookup");
        SingletonBeanByLookup singletonBean2 = (SingletonBeanByLookup) applicationContext.getBean("singletonBeanByLookup");

        singletonBean.printTime();
        Thread.sleep(1000);
        singletonBean1.printTime();
        Thread.sleep(1000);
        singletonBean2.printTime();
    }
}

4、测试结果:

 

 

 

5、原因分析:

系统会生成SingletonBeanByLookup的代理对象作为 SingletonBeanByLookup对象的bean,
系统的代理对象是应用了Srping应用了CGLIB(动态代理)类库。Spring在初始化容器的时候对配置@lookup的bean做了特殊处理,Spring会对bean指定的class做动态代理,代理@lookup标签中name属性所指定的方法,返回bean属性指定的bean实例对象。
每次我们调用@lookup方法时,其实是调用了CGLIB生成的动态代理类的方法。
就是进入org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy) 方法中
在此拦截器方法中 通过getBean 获取对应的bean。

 

三、原型bean使用代理模式

  在原型bean的@Scope 标签中 添加“proxyMode = ScopedProxyMode.TARGET_CLASS”,如:  @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,proxyMode = ScopedProxyMode.TARGET_CLASS)

1、单例bean

@Component
public class SingletonBean {
    @Autowired
    private PrototypeBean prototypeBean;


    public void printTime() {
        System.out.println("SingletonBean: "+this.hashCode());
        System.out.println("prototypeBean 注入给单例的:"+prototypeBean.hashCode());
        prototypeBean.printTime();
    }


}

 

2、原型bean

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeBean {
     Long timeMilis;

    public PrototypeBean() {
        //System.out.println("PrototypeBean Constructor");
        this.timeMilis = System.currentTimeMillis();
    }

    public void printTime() {
        System.out.println("PrototypeBean原始bean:"+this.hashCode());
        System.out.println("timeMils:" + timeMilis);
    }
}

3、测试类

public class PrototypeTest {
    public static void main(String[] args) throws InterruptedException {
        AnnotationConfigApplicationContext applicationContext=
                new AnnotationConfigApplicationContext(PrototypeBeanConfig.class);




        SingletonBean singletonBean = (SingletonBean) applicationContext.getBean("singletonBean");

        SingletonBean singletonBean1 = (SingletonBean) applicationContext.getBean("singletonBean");

        SingletonBean singletonBean2 = (SingletonBean) applicationContext.getBean("singletonBean");



        singletonBean.printTime();
        Thread.sleep(1000);
        singletonBean1.printTime();
        Thread.sleep(1000);
        singletonBean2.printTime();
    }
}

4、测试结果:

 

 

5、原因分析:

这种方法将prototypeBean类生成一个代理对象赋值给singletonBean,每次调用代理方法的时候,会通过getBean去获取一个原始的PrototypeBean。
*
* This will lead to the creation of a proxy. That proxy is created once and will be
* returned for each call to getBean. As soon as you invoke a method on the proxy it will,
* based on the scope, either create a new one or reuse an existing one.
* As you have specified the scope as prototype each method invocation will lead to a new object.

 

posted on 2021-01-03 12:27  AlexGeng  阅读(348)  评论(0编辑  收藏  举报

导航