前言
最近分享的一些源码、框架设计的东西。我发现大家热情不是特别高,想想大多数应该还是正儿八经写代码的居多;这次就分享一点接地气的: springboot 使用中的一些小技巧。
算不上多高大上的东西,但都还挺有用。
屏蔽外部依赖
第一个是屏蔽外部依赖,什么意思呢?
比如大家日常开发时候有没有这样的烦恼:
项目是基于 springcloud 或者是 dubbo 这样的分布式服务,你需要依赖许多基础服务。
比如说某个订单号的生成、获取用户信息等。
由于服务拆分,这些功能都是在其他应用中以接口的形式提供,单测还好我还可以利用 mock 把它屏蔽掉。
但如果自己想把应用启动起来同时把自己相关的代码跑一遍呢?
通常有几种做法:
•本地把所有的服务都启动起来。
•把注册中心换为开发环境,依赖开发环境的服务。
•直接把代码推送到开发环境自测。
看起来三种都可以,以前我也是这么干的。但还是有几个小问题:
•本地启动有可能服务很多,全部起来电脑能不能撑住还两说,万一服务有问题就进行不下去了。
•依赖开发环境的前提是网络打通,还有一个问题就是开发环境代码很不稳定很大可能会影响你的测试。
•推送到开发环境应该是比较靠谱的方案,但如果想调试只有日志大法,没有本地 debug 的效率高效。
那如何解决问题呢?既可以在本地调试也不用启动其他服务。
其实也可以利用单测的做法,把其他外部依赖 mock 掉就行了。
大致的流程分为以下几步:
•springboot 启动之后在 spring 中找出你需要屏蔽的那个 api 的 bean(通常情况下这个接口都是交给 spring 管理的)。
•手动从 bean 容器中删除该 bean。
•重新创建一个该 api 的对象,只不过是通过 mock 出来的。
•再手动注册进 bean 容器中。
以下面这段代码为例:
1
2
3
4
5
6
7
8
9
|
@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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
@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 然后手动删除掉。
关键的一步:
1
2
3
|
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 的方法都会做默认的返回。
1
|
baseresponse.createsuccess(dateutil.getlongtime() + "" , "mock orderno success" ))
|
测试一下,当我们没有替换时调用刚才那个接口并且本地也没有启动 orderservice:
因为没有配置 fallback 所以会报错,表示找不到这个服务。
替换掉 bean 时:
再次请求没有报错,并且获得了我们默认的返回。
通过日志也会发现 orderserviceclient 最后已经被 mock 代理了,并不会去调用真正的方法。
配置加密
下一个则是配置加密,这应该算是一个基本功能。
比如我们配置文件中的一些账号和密码,都应该是密文保存的。
因此这次使用了一个开源组件来实现加密与解密,并且对 springboot 非常友好只需要几段代码即可完成。
•首先根据加密密码将需要加密的配置加密为密文。
•替换原本明文保存的配置。
•再使用时进行解密。
使用该包也只需要引入一个依赖即可:
1
2
3
4
5
|
<dependency>
<groupid>com.github.ulisesbocchio</groupid>
<artifactid>jasypt-spring-boot-starter</artifactid>
<version> 1.14 </version>
</dependency>
|
同时写一个单测根据密码生成密文,密码也可保存在配置文件中:
jasypt.encryptor.password=123456
接着在单测中生成密文。
1
2
3
4
5
6
7
8
9
10
|
@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 的一个增强接口即可实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@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;
}
}
|
这样就可以在真正使用时还原为明文。
同时也可以在启动命令中配置刚才的密码:
1
|
java -djasypt.encryptor.password=password -jar target/jasypt-spring-boot-demo- 0.0 . 1 -snapshot.jar
|
总结
上文的一些实例代码可以在这里找到:
https://github.com/crossoverjie/springboot-cloud
以上所述是小编给大家介绍的值得收藏的springboot 实用的小技巧,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
原文链接:https://www.cnblogs.com/crossoverJie/p/9789020.html