當前位置:
首頁 > 知識 > 為什麼多線程、junit 中無法使用spring 依賴注入?

為什麼多線程、junit 中無法使用spring 依賴注入?

為什麼多線程、junit 中無法使用spring 依賴注入? 這個問題,其實體現了,我們對spring已依賴太深,以至於不想自己寫實例了。 那麼到底是為什麼在多線程和junit單元測試中不能使用依賴注入呢?

一、為什麼多線程下spring的依賴注入失效了呢?

答:因為spring為了考慮安全性問題,在多線程情況下,不支持直接使用 @Resouce 註解方式進行直接的bean注入,那麼也就是說,如果在多線程調用該注入實例化的變數時,將會報NullPointerException 。

解決辦法: 多線程情況下,通過調用的service進行傳入需要操作的bean變數,而多線程只是將前台工作轉移到後台。如下:

# 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里的東西再實例化一遍。

二、junit中無法使用依賴注入的問題?

答:因為junit一般會走最小化的方式,而非每次都要將整個框架的東西載入,從而減少載入時間。當然,如果確實需要,這個問題,其實目前在高版本的junit中,已經不存在了,通過載入 SpringJUnit4ClassRunner,即可進行注入值。

解決方案1:使用高版本的junit進行測試,如下:


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 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就立刻變小白了。

積跬步,致千里!

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 科技優家 的精彩文章:

OS X 和iOS 中的多線程技術(上)
View Components as Tag Helpers,離在線模板編輯又進一步
淺談MVC Form認證
asp.net core中負載均衡場景下http重定向https的問題
原生JS+Canvas實現五子棋遊戲

TAG:科技優家 |

您可能感興趣

eclipse使用ant + ivy 配置項目jar包和依賴關係
Spring源碼系列:依賴注入(二)createBean
擺脫蘋果依賴症,Dialog或將與Synaptics合併
自己致敬自己?被依賴的Tinker Hatfield
自己致敬自己? 被依賴的Tinker Hatfield
adidas是否過於依賴Boost?
windows離線狀態下源碼安裝Robotframework 環境及依賴包
「依賴的」是「dependent」還是「dependant」?別再寫錯啦!
TicWatch Pro體驗:不依賴手機也強大
Saurik與Electra 越獄工具開發者不和:將不提供依賴支持
Oncogenesis:二氫青蒿素能夠通過JARID2/miR-7/miR-34a依賴的Axl下調來抑制前列腺癌
不再依賴iPhone 蘋果watchOS 6支持OTA更新
一文搞清Gradle依賴
Apple Watch 新專利:整隻表都是屏/蘋果高層調整,為減少對 iPhone 依賴/卡梅隆:想看《三體》電影版
極簡Light Phone:兩千元治療手機依賴,了解一下?
Red Velvet成員Joy:所有成員之中,我最依賴的人就是Wendy姐姐!
蘋果擺脫了 iPhone 依賴?
Go 1.11 將支持的 Go module:解決包依賴管理問題
蘋果為 iPhone 研發 MicroLED 屏幕,徹底擺脫對三星的依賴
不依賴iPhone?談何容易?