您的位置:软件测试 > 开源软件测试 > 开源单元测试工具 > junit
为什么多线程、junit中无法使用spring 依赖注入?
作者:等你归去来 发布时间:[ 2017/6/20 11:04:25 ] 推荐标签:单元测试 Junit 日志

  为什么多线程、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<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立刻变小白了。

软件测试工具 | 联系我们 | 投诉建议 | 诚聘英才 | 申请使用列表 | 网站地图
沪ICP备07036474 2003-2017 版权所有 上海泽众软件科技有限公司 Shanghai ZeZhong Software Co.,Ltd