您的位置:軟件測(cè)試 > 開源軟件測(cè)試 > 開源單元測(cè)試工具 > junit
為什么多線程、junit中無法使用spring 依賴注入?
作者:等你歸去來 發(fā)布時(shí)間:[ 2017/6/20 11:04:25 ] 推薦標(biāo)簽:單元測(cè)試 Junit 日志

  為什么多線程、junit 中無法使用spring 依賴注入? 這個(gè)問題,其實(shí)體現(xiàn)了,我們對(duì)spring已依賴太深,以至于不想自己寫實(shí)例了。 那么到底是為什么在多線程和junit單元測(cè)試中不能使用依賴注入呢?
  一、為什么多線程下spring的依賴注入失效了呢?
  答:因?yàn)閟pring為了考慮安全性問題,在多線程情況下,不支持直接使用 @Resouce 注解方式進(jìn)行直接的bean注入,那么也是說,如果在多線程調(diào)用該注入實(shí)例化的變量時(shí),將會(huì)報(bào)NullPointerException 。
  解決辦法: 多線程情況下,通過調(diào)用的service進(jìn)行傳入需要操作的bean變量,而多線程只是將前臺(tái)工作轉(zhuǎn)移到后臺(tái)。如下:
  # CalculateServiceImpl.java 服務(wù)實(shí)現(xiàn),傳入需要調(diào)用的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                // 默認(rèn)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);                //異步簽署協(xié)議
  }
  }
  # SignContractThread.java 異步實(shí)現(xiàn)調(diào)用
 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());
  /**
  * 無法使用依賴注入實(shí)現(xiàn)多純種的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("===========>>>>> 異步調(diào)用, 參數(shù): {} ==============>>>>>>>>>", requestParamJson);
  try {
  ResponseEntity responseEntity = contractRemoteService.doSSt(certificateApplyDto);
  logger.debug("<<<<<<<<<<=========== 異步調(diào)用,method:doSSt,返回結(jié)果:{}", responseEntity);
  EE.logEvent("Monitor_signContract", "asyncSignContractResult", Event.SUCCESS, JSON.toJSONString(responseEntity));
  } catch (Exception e) {
  logger.error("==-------===異步調(diào)用,發(fā)生異常,請(qǐng)求參數(shù): {}, 異常-{}", requestParamJson, e););
  throw new RuntimeException("異步調(diào)用_doSSt,發(fā)生異常", e);
  } finally {
  // ... 調(diào)用完畢
  }
  }
  }
  這樣,通過傳入外部依賴注入的bean,線程進(jìn)行調(diào)用,即可避免線程無法注入bean的問題了。當(dāng)然了,你可能還會(huì)想到使用 getBean的方法獲取,其實(shí)也是可以的,不過應(yīng)該有一定的危險(xiǎn)性,因?yàn)橄喈?dāng)于你得再次將applicationContext里的東西再實(shí)例化一遍。
  二、junit中無法使用依賴注入的問題?
  答:因?yàn)閖unit一般會(huì)走小化的方式,而非每次都要將整個(gè)框架的東西載入,從而減少加載時(shí)間。當(dāng)然,如果確實(shí)需要,這個(gè)問題,其實(shí)目前在高版本的junit中,已經(jīng)不存在了,通過加載 SpringJUnit4ClassRunner,即可進(jìn)行注入值。
  解決方案1:使用高版本的junit進(jìn)行測(cè)試,如下:
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,因?yàn)閮H僅是單元測(cè)試,加載資源稍微多些也沒有關(guān)系。
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的問題了。
  注意的幾點(diǎn)是:
  1:多線程的執(zhí)行,面向C端的用戶,一定不能直接 new Thread() 進(jìn)行多線程操作,否則會(huì)死得很慘。
  2:多做好日志記錄,在出錯(cuò)的時(shí)候進(jìn)行排查真的很方便,但也得做日志的清理工作,否則服務(wù)器空間被占滿也不是很長時(shí)間的事。
  3:單元測(cè)試還是有必要做的,否則提交測(cè)試時(shí),自己哪來的底氣呢。
  4:多了解spring核心的東西,做到知其然知其所以然,不要脫離spring立刻變小白了。

軟件測(cè)試工具 | 聯(lián)系我們 | 投訴建議 | 誠聘英才 | 申請(qǐng)使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權(quán)所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd