屏蔽外部依赖
第一个是屏蔽外部依赖
,什么意思呢?
比如大家日常开发时候有没有这样的烦恼:
项目是基于 SpringCloud
或者是 dubbo
这样的分布式服务,你需要依赖许多基础服务。
比如说某个订单号的生成、获取用户信息等。
由于服务拆分,这些功能都是在其他应用中以接口的形式提供,单测还好我还可以利用 Mock
把它屏蔽掉。
但如果自己想把应用启动起来同时把自己相关的代码跑一遍呢?
通常有几种做法:
- 本地把所有的服务都启动起来。
- 把注册中心换为开发环境,依赖开发环境的服务。
- 直接把代码推送到开发环境自测。
看起来三种都可以,以前我也是这么干的。但还是有几个小问题:
- 本地启动有可能服务很多,全部起来电脑能不能撑住还两说,万一服务有问题就进行不下去了。
- 依赖开发环境的前提是网络打通,还有一个问题就是开发环境代码很不稳定很大可能会影响你的测试。
- 推送到开发环境应该是比较靠谱的方案,但如果想调试只有日志大法,没有本地
debug
的效率高效。
那如何解决问题呢?既可以在本地调试也不用启动其他服务。
其实也可以利用单测的做法,把其他外部依赖 Mock
掉就行了。
大致的流程分为以下几步:
SpringBoot
启动之后在Spring
中找出你需要屏蔽的那个API
的bean
(通常情况下这个接口都是交给Spring
管理的)。
- 手动从
bean
容器中删除该bean
。
- 重新创建一个该
API
的对象,只不过是通过Mock
出来的。
- 再手动注册进
bean
容器中。
以下面这段代码为例:
@Override public BaseResponse<OrderNoResVO> getUserByHystrix(@RequestBody UserReqVO userReqVO) { OrderNoReqVO vo = new OrderNoReqVO(); vo.setAppId(123L); vo.setReqNo(userReqVO.getReqNo()); BaseResponse<OrderNoResVO> orderNo = orderServiceClient.getOrderNo(vo); return orderNo; }
这是一个 SpringCloud 应用。
它依赖于 orderServiceClient
获取一个订单号。
其中的 orderServiceClient
就是一个外部 API,也是被 Spring 所管理。
替换原有的 Bean
下一步就是替换原有的 Bean。
@Component public class OrderMockServiceConfig implements CommandLineRunner { private final static Logger logger = LoggerFactory.getLogger(OrderMockServiceConfig.class); @Autowired private ApplicationContext applicationContext; @Value("${excute.env}") private String env; @Override public void run(String... strings) throws Exception { // 非本地环境不做处理 if ("dev".equals(env) || "test".equals(env) || "pro".equals(env)) { return; } DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); OrderServiceClient orderServiceClient = defaultListableBeanFactory.getBean(OrderServiceClient.class); logger.info("======orderServiceClient {}=====", orderServiceClient.getClass()); defaultListableBeanFactory.removeBeanDefinition(OrderServiceClient.class.getCanonicalName()); OrderServiceClient mockOrderApi = PowerMockito.mock(OrderServiceClient.class, invocationOnMock -> BaseResponse.createSuccess(DateUtil.getLongTime() + "", "mock orderNo success")); defaultListableBeanFactory.registerSingleton(OrderServiceClient.class.getCanonicalName(), mockOrderApi); logger.info("======mockOrderApi {}=====", mockOrderApi.getClass()); } }
其中实现了 CommandLineRunner
接口,可以在 Spring
容器初始化完成之后调用 run()
方法。
代码非常简单,简单来说首先判断下是什么环境,毕竟除开本地环境其余的都是需要真正调用远程服务的。
之后就是获取 bean
然后手动删除掉。
关键的一步:
OrderServiceClient mockOrderApi = PowerMockito.mock(OrderServiceClient.class, invocationOnMock -> BaseResponse.createSuccess(DateUtil.getLongTime() + "", "mock orderNo success")); defaultListableBeanFactory.registerSingleton(OrderServiceClient.class.getCanonicalName(), mockOrderApi)