最近分享的一些源码、框架设计的东西。我发现你们热情不是特别高,想一想大多数应该仍是正儿八经写代码的居多;此次就分享一点接地气的: SpringBoot
使用中的一些小技巧。java
算不上多高大上的东西,但都还挺有用。git
第一个是屏蔽外部依赖
,什么意思呢?github
好比你们平常开发时候有没有这样的烦恼:spring
项目是基于 SpringCloud
或者是 dubbo
这样的分布式服务,你须要依赖许多基础服务。数据库
好比说某个订单号的生成、获取用户信息等。springboot
因为服务拆分,这些功能都是在其余应用中以接口的形式提供,单测还好我还能够利用 Mock
把它屏蔽掉。bash
但若是本身想把应用启动起来同时把本身相关的代码跑一遍呢?网络
一般有几种作法:app
看起来三种均可以,之前我也是这么干的。但仍是有几个小问题:框架
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。
@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);
复制代码
建立了一个新的 OrderServiceClient
对象并手动注册进了 Spring
容器中。
第一段代码使用的是 PowerMockito.mock
的 API,他能够建立一个代理对象,让全部调用 OrderServiceClient
的方法都会作默认的返回。
BaseResponse.createSuccess(DateUtil.getLongTime() + "", "mock orderNo success"))
复制代码
测试一下,当咱们没有替换时调用刚才那个接口而且本地也没有启动 OrderService
:
由于没有配置 fallback 因此会报错,表示找不到这个服务。
替换掉 bean 时:
再次请求没有报错,而且得到了咱们默认的返回。
经过日志也会发现 OrderServiceClient
最后已经被 Mock
代理了,并不会去调用真正的方法。
下一个则是配置加密,这应该算是一个基本功能。
好比咱们配置文件中的一些帐号和密码,都应该是密文保存的。
所以此次使用了一个开源组件来实现加密与解密,而且对 SpringBoot
很是友好只须要几段代码便可完成。
使用该包也只须要引入一个依赖便可:
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>1.14</version>
</dependency>
复制代码
同时写一个单测根据密码生成密文,密码也可保存在配置文件中:
jasypt.encryptor.password=123456
复制代码
接着在单测中生成密文。
@Autowired
private StringEncryptor encryptor;
@Test
public void getPass() {
String name = encryptor.encrypt("userName");
String password = encryptor.encrypt("password");
System.out.println(name + "----------------");
System.out.println(password + "----------------");
}
复制代码
以后只须要使用密文就行。
因为我这里是对数据库用户名和密码加密,因此还得有一个解密的过程。
利用 Spring Bean
的一个加强接口便可实现:
@Component
public class DataSourceProcess implements BeanPostProcessor {
@Autowired
private StringEncryptor encryptor;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSourceProperties){
DataSourceProperties dataSourceProperties = (DataSourceProperties) bean;
dataSourceProperties.setUsername(encryptor.decrypt(dataSourceProperties.getUsername())) ;
dataSourceProperties.setPassword(encryptor.decrypt(dataSourceProperties.getPassword()));
return dataSourceProperties ;
}
return bean;
}
}
复制代码
这样就能够在真正使用时还原为明文。
同时也能够在启动命令中配置刚才的密码:
java -Djasypt.encryptor.password=password -jar target/jasypt-spring-boot-demo-0.0.1-SNAPSHOT.jar
复制代码
这样两个小技巧就讲完了,你们有 SpringBoot
的更多使用技巧欢迎留言讨论。
上文的一些实例代码能够在这里找到:
欢迎关注公众号一块儿交流: