問題描述
在Java Web應用中采用多線程處理數據,發現Spring注入的服務一直報NullPointerException。使用注解式的聲明@Resource和XML配置的bean聲明,都報空指針。然後尋找萬能的網絡尋找幫助,有的說spring因為考慮到線程安全問題,不支持注入,以及spring的bean聲明周期,在工程啟動時,沒有檢測到線程中的bean,進行注入。看來只能主動去獲取spring聲明的周期。
解決辦法:
(1)主動實例化對象(不推薦)
private TestService testService = new TestServiceImpl();
每次加載這個類,就會重新創建一次,會過多耗費資源。
(2)把線程設置為主程序的內部類,或者是利用線程的構造方法把bean傳遞過去
主程序在web容器加載時,肯定是可以注入Spring bean的,那麼將線程的實現類放在主程序的類中便可以“共享”Spring的bean,將生成線程的線程池定義在主程序的類中,每個線程的實現類作為內部類也定義在主程序中。
1 public class Test implements InitializingBean{ 2 3 @Resource 4 private TestService testService 5 6 public void close(){ 7 } 8 9 public void afterPropertiesSet() throws Exception { 10 // 利用構造方法把bean傳遞過去 11 new Thread(testService); 12 } 13 }
(3)用靜態方法直接取的容器中的spring對象
寫一個SpringContextUtil類,實現ApplicationContextAware
1 package com.test.utils; 2 3 import java.util.Locale; 4 import java.util.Map; 5 6 import org.springframework.beans.BeansException; 7 import org.springframework.context.ApplicationContext; 8 import org.springframework.context.ApplicationContextAware; 9 10 public class SpringContextUtil implements ApplicationContextAware { 11 12 private static ApplicationContext applicationContext = null; 13 14 15 public void setApplicationContext(ApplicationContext context) throws BeansException { 16 applicationContext = context; 17 } 18 19 20 /** 21 * 獲取applicationContext對象 22 * @return 23 */ 24 public static ApplicationContext getApplicationContext() { 25 return applicationContext; 26 } 27 28 29 /** 30 * 根據bean的id來查找對象 31 * @param id 32 * @return 33 */ 34 35 public static <T> T getBeanById(String id) { 36 return (T) applicationContext.getBean(id); 37 } 38 39 40 /** 41 * 根據bean的class來查找對象 42 * @param c 43 * @return 44 */ 45 public static <T> T getBeanByClass(Class c) { 46 return (T) applicationContext.getBean(c); 47 } 48 49 50 /** 51 * 根據bean的class來查找所有的對象(包括子類) 52 * @param c 53 * @return 54 */ 55 public static Map getBeansByClass(Class c) { 56 return applicationContext.getBeansOfType(c); 57 } 58 59 public static String getMessage(String key) { 60 return applicationContext.getMessage(key, null, Locale.getDefault()); 61 } 62 63 }
在applicationContext.xml中聲明SpringContextUtil的bean
<bean id="springContextUtil" class="com.test.utils.SpringContextUtil"/>
在線程中或線程調用的其他服務中可以主動加載bean,然後可以直接使用(testService必須是spring中配置的bean)
1 public void run() { 2 TestService testService = (TestService ) SpringContextUtil.getBean("testService"); 3 testService.queryData();
4 }
(4)看到網上說還可以通過BeanFactory來加載bean,沒有去實現過,上個代碼做個參考
1 public class SpringBeanFactoryUtils implements BeanFactoryAware { 2 3 private static BeanFactory beanFactory = null; 4 private static SpringBeanFactoryUtils factoryUtils = null; 5 6 public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 7 SpringBeanFactoryUtils.beanFactory = beanFactory; 8 } 9 public static BeanFactory getBeanFactory() { 10 return beanFactory; 11 } 12 public static SpringBeanFactoryUtils getInstance(){ 13 if(factoryUtils==null){ 14 //factoryUtils = (SpringBeanFactoryUtils)beanFactory.getBean("springBeanFactoryUtils"); 15 factoryUtils = new SpringBeanFactoryUtils(); 16 } 17 return factoryUtils; 18 } 19 public static Object getBean(String name){ 20 return beanFactory.getBean(name); 21 } 22 }