spring的bean在多執行緒中注入的問題
阿新 • • 發佈:2018-11-28
問題描述
在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測試。