1. 程式人生 > >spring的bean在多執行緒中注入的問題

spring的bean在多執行緒中注入的問題

問題描述

在spring中,如果需要在非同步執行緒中注入bean,會發現bean是空的情況。原因據說是spring bean 出於執行緒安全考慮,不得注入bean至執行緒類(Runnable)。
程式碼如下:

public class DealThreadTask implements Runnable{



    @Autowired
    private DealService dealService;


    @Override
    public void run() {

    //  DealService dealService=holder.getBean("dealService");
System.out.println("dealService-->"+dealService); dealService.deal("andy", "李琳", 100d); } }

在controller層中,對上述的service進行呼叫。


    public static void main(String[] args){

        ApplicationContext context=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-aware.xml"
); DealThreadTask task=new DealThreadTask(); new Thread(task).start(); }

執行結果:

dealService-->null
Exception in thread "Thread-1" java.lang.NullPointerException
    at com.test.spring.tx.multi.DealThreadTask.run(DealThreadTask.java:38)
    at java.lang.Thread.run(Thread.java
:744)

說明spring在DealThreadTask中未能將dealService注入進去。

解決方法

Spring API 中有ApplicationContextAware 這個介面,實現了這個介面的類,可以在容器初始化完成中獲得容器,從而可以獲得容器中所有的bean。

public class ApplicationContextHolder implements ApplicationContextAware {

     private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextHolder.applicationContext=applicationContext;
        System.out.println("applicationContext---->"+applicationContext);
    }

     public static <T> T getBean(Class<T> clazz){
         return applicationContext.getBean(clazz);
   }

     public static <T> T getBean(String name) {

         if (applicationContext==null) {
             System.out.println("applicationContext為空");
        }
            return (T) applicationContext.getBean(name);

     }
}

然後,在xml配置檔案中,需要將這個類配置進去。

<bean id="applicationContextHolder"  class="com.test.spring.tx.multi.ApplicationContextHolder"></bean>

這樣,在非同步執行緒中的DealThreadTask 中,通過手動的applicationContextHolder的getBean方法,就可以獲取所需要的bean。

public class DealThreadTask implements Runnable{



    @Autowired
    private ApplicationContextHolder holder;



    @Override
    public void run() {

        DealService dealService=holder.getBean("dealService");
        System.out.println("dealService-->"+dealService);
        dealService.deal("andy", "李琳", 100d);


    }

}

提示

上述的程式碼中


    public static void main(String[] args){

        ApplicationContext context=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-aware.xml");
         DealThreadTask task=new DealThreadTask();
         new Thread(task).start();

    }

用了main方法,而沒有用junit的test註解進行測試,是因為junit是不會等待非同步執行緒執行完然後結束,而是junit自己本執行緒的程式碼執行完就結束了。由於程式碼中設計到資料庫操作,因此如果簡單的用junit進行測試,可能的結果是測試完成,但是資料庫操作還沒有進行。如果一定要用junit進行測試,可以用其他的手段,比如thread的join操作,或者在junit的測試方法中等待鍵盤輸入才結束,這些都是為了讓非同步執行緒執行完後才結束junit測試。