在開發中經常會使用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;
}
}
問題解決。