在开发中经常会使用Spring的@Autowired来实现对象的自动注入,但是在最近的开发中在多线程中用Spring的@Autowired来自动注入时总是注入不进去,代码如下:
package com.common.base.utils.SpringUtils; import org.springframework.beans.factory.annotation.Autowired; import java.util.concurrent.atomic.AtomicInteger; public class ThreadRunner implements Runnable{ @Autowired private ServiceBean serviceBean; private static AtomicInteger count = new AtomicInteger(0); @Override public void run(){ if (serviceBean ==null){ return; } serviceBean.log(); count.addAndGet(1); System.out.println("当前线程为:" + Thread.currentThread().getName() + "count:" + count); } public ServiceBean getServiceBean() { return serviceBean; } public void setServiceBean(ServiceBean serviceBean) { this.serviceBean = serviceBean; } }
其中,ServiceBean定义如下:
package com.common.base.utils.SpringUtils; import org.springframework.stereotype.Service; @Service("serviceBean") public class ServiceBean{ public void log(){ System.out.println("this is service bean."); } }
只是简单的输出语句。然后在主线程中,启动线程,如下:
package com.common.base.utils.SpringUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath*:Service-*.xml"}) public class SpringMultiThreadTest{ @Test public void testSpringBean(){ for (int i=0; i<10000000; i++){ new Thread(new ThreadRunner()).start(); } try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } }
此时,不会有打印信息,serviceBean为空。
原因:在主线程中使用了:
new ThreadRunner()
新建了一个实例,并不在Spring容器中,也就没法获得Spring中的bean。
解决办法:
1、将ThreadRunner类也作为一个bean注入到spring容器中,如下:
package com.common.base.utils.SpringUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath*:Service-*.xml"}) public class SpringMultiThreadTest{ @Autowired private ThreadRunner threadRunner; @Test public void testSpringBean(){ for (int i=0; i<10000000; i++){ new Thread(threadRunner).start(); } try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } }
问题解决。
2、使用Spring手动获得ServiceBean,首先写一个手动获得Spring bean的工具类:
package com.common.base.utils.SpringUtils; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * 直接通过Spring 上下文获取SpringBean,用于多线程环境 * by jingquan @20160405 */ public class SpringBeanUtil implements ApplicationContextAware{ private static ApplicationContext applicationContext = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringBeanUtil.applicationContext = applicationContext; } public static Object getBeanByName(String beanName) { if (applicationContext == null){ return null; } return applicationContext.getBean(beanName); } public static <T> T getBean(Class<T> type) { return applicationContext.getBean(type); } }
然后在ThreadRunner类中不自动获取,而是手动获取,代码如下:
package com.common.base.utils.SpringUtils; import org.springframework.beans.factory.annotation.Autowired; import java.util.concurrent.atomic.AtomicInteger; public class ThreadRunner implements Runnable{ private ServiceBean serviceBean; private static AtomicInteger count = new AtomicInteger(0); public ThreadRunner(){ this.serviceBean = (ServiceBean)SpringBeanUtil.getBeanByName("serviceBean"); } @Override public void run(){ if (serviceBean ==null){ return; } serviceBean.log(); count.addAndGet(1); System.out.println("当前线程为:" + Thread.currentThread().getName() + "count:" + count); } public ServiceBean getServiceBean() { return serviceBean; } public void setServiceBean(ServiceBean serviceBean) { this.serviceBean = serviceBean; } }
问题解决。