为何多线程、junit 中没法使用spring 依赖注入?

  为何多线程、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就马上变小白了。

  积跬步,致千里!

相关文章
相关标签/搜索