爲何多線程、junit 中沒法使用spring 依賴注入? 這個問題,其實體現了,咱們對spring已依賴太深,以致於不想本身寫實例了。 那麼究竟是爲何在多線程和junit單元測試中不能使用依賴注入呢?java
1、爲何多線程下spring的依賴注入失效了呢?spring
答:由於spring爲了考慮安全性問題,在多線程狀況下,不支持直接使用 @Resouce 註解方式進行直接的bean注入,那麼也就是說,若是在多線程調用該注入實例化的變量時,將會報NullPointerException 。json
解決辦法: 多線程狀況下,經過調用的service進行傳入須要操做的bean變量,而多線程只是將前臺工做轉移到後臺。以下:api
# CalculateServiceImpl.java 服務實現,傳入須要調用的bean
package com.xx.op.user; import com.xx.note.dubbo.dto.CertificateApplyDto; import com.xx.con.dubbo.api.ContractRemoteService; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Resource; public class CalculateServiceImpl implements CalculateService { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Resource(name = "threadPoolTaskExecutor") private ThreadPoolTaskExecutor threadPoolTaskExecutor; @Resource // 默認name可寫可不寫 private ContractRemoteService contractRemoteService; @Override public void doSth(String userId) throws Exception CertificateApplyDto certificateApplyDto = new CertificateApplyDto(); certificateApplyDto.setBorrowNid(userId); SignContractThread signContractThread = new SignContractThread(contractRemoteService, certificateApplyDto); threadPoolTaskExecutor.execute(signContractThread); //異步簽署協議 } }
# SignContractThread.java 異步實現調用安全
package com.xx.cc.common.async; import com.alibaba.fastjson.JSON; import com.dianping.cat.message.Event; import com.xx.framework.dto.ResponseEntity; import com.xx.no.dubbo.dto.CertificateApplyDto; import com.xx.con.dubbo.api.ContractRemoteService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SignContractThread implements Runnable { private Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 沒法使用依賴注入實現多純種的bean, 從外部傳入方式 */ private ContractRemoteService contractRemoteService; private CertificateApplyDto certificateApplyDto; public SignContractThread(ContractRemoteService contractRemoteService, CertificateApplyDto certificateApplyDto) { this.contractRemoteService = contractRemoteService; this.certificateApplyDto = certificateApplyDto; } @Override public void run() { String requestParamJson = JSON.toJSONString(this.doSSt); logger.debug("===========>>>>> 異步調用, 參數: {} ==============>>>>>>>>>", requestParamJson); try { ResponseEntity responseEntity = contractRemoteService.doSSt(certificateApplyDto); logger.debug("<<<<<<<<<<=========== 異步調用,method:doSSt,返回結果:{}", responseEntity); EE.logEvent("Monitor_signContract", "asyncSignContractResult", Event.SUCCESS, JSON.toJSONString(responseEntity)); } catch (Exception e) { logger.error("==-------===異步調用,發生異常,請求參數: {}, 異常-{}", requestParamJson, e);); throw new RuntimeException("異步調用_doSSt,發生異常", e); } finally { // ... 調用完畢 } } }
這樣,經過傳入外部依賴注入的bean,線程進行調用,便可避免線程沒法注入bean的問題了。固然了,你可能還會想到使用 getBean的方法獲取,其實也是能夠的,不過應該有必定的危險性,由於至關於你得再次將applicationContext裏的東西再實例化一遍。服務器
2、junit中沒法使用依賴注入的問題?mybatis
答:由於junit通常會走最小化的方式,而非每次都要將整個框架的東西載入,從而減小加載時間。固然,若是確實須要,這個問題,其實目前在高版本的junit中,已經不存在了,經過加載 SpringJUnit4ClassRunner,便可進行注入值。多線程
解決方案1:使用高版本的junit進行測試,以下:app
package com.xx.mybatis3spring3intg.junit; import java.util.List; import com.xx.mybatis3spring.bean.User; import com.xx.mybatis3spring.service.UserService; 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({"/config/application*.xml"}) public class UserServiceTest { @Resource private UserService userService; @Test public void c1() { List<User> userList = userService.query(new User()); System.out.println(userList); } }
解決方案2:經過getBean的方式獲取須要的bean,由於僅僅是單元測試,加載資源稍微多些也沒有關係。框架
package com.xx.c.order; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationCoimport com.xx.c.service.order.OrderService; public class OrderServiceTest { private static ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); private static OrderService orderService = (OrderService)context.getBean("orderService"); private static Object getBean(String name) { if (context == null) { context = new ClassPathXmlApplicationContext("applicationContext.xml"); } return context.getBean(name); } public static void main(String[] args) { String borrowNid = "11111111111"; com.xx.c.dubboapi.biz.OutApiManager service = (com.xx.c.dubboapi.biz.OutApiManager)getBean("outApiManagerImpl"); System.out.println("========"+service.getWang(borrowNid)); System.out.println("========"+service.getRepayList("111111111111111111")); } }
以上,就基本解決了如題所問,固然也能夠做爲全部沒法注入bean的問題的解決方案。信不信由你,反正我是信了。
注意的幾點就是:
1, 多線程的執行,面向C端的用戶,必定不能直接 new Thread() 進行多線程操做,不然會死得很慘。
2,多作好日誌記錄,在出錯的時候進行排查真的很方便,但也得作日誌的清理工做,不然服務器空間被佔滿也不是很長時間的事。
3,單元測試仍是有必要作的,不然提交測試時,本身哪來的底氣呢。
4,多瞭解spring核心的東西,作到知其然知其因此然,不要脫離spring就馬上變小白了。
積跬步,致千里!