1. 程式人生 > 實用技巧 >單例bean依賴原型bean的 如何保證原型bean生命週期

單例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.