问题描述
在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 }