多环境配置文件
spring mvc
项目中有一种方式是通过maven中的profile
去激活插件放不同的配置文件打包,样例,里面有5个分支, master
分支就是SSM项目支持maven激活环境配置的实现
但Spring Boot
提供了更优雅的方案
spring:
profiles:
active: dev
...
---
spring:
profiles: dev
...
---
spring:
profiles: prod
...
通过这种方式来激活不同环境的配置,线上启动的时候可以用java -jar xxx.jar -spring.profiles.active=xxx
来指定使用什么环境,或者xxx.jar start -spring.profiles.active=xxx
,jar自启动需要额外配置,不建议使用
配置加密
通常数据库的用户名,密码都需要额外加密,我个人喜欢用jasypt-spring-boot-starter
来对关键配置加密
具体使用方法首先配置文件中加这么一段:
jasypt:
encryptor:
password: RjY2RDgyMzU3NDY*****************QzB3MDA=
property:
prefix: "ENC("
suffix: ")"
其中password
表示加密的密钥,prefix
和suffix
就表示加密开始和结束后面会用到
然后要用这个密钥计算出账号密码加密后的值,可以本地控制台跑,也可以通过junit test
来跑:
public static void main(String[] args) {
BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
//加密所需的salt(盐)
textEncryptor.setPassword("RjY2RDgyMzU3NDY*****************QzB3MDA=");
//要加密的数据(数据库的用户名或密码)
String username = textEncryptor.encrypt("root");
String password = textEncryptor.encrypt("12345678");
System.out.println("username:" + username);
System.out.println("password:" + password);
}
这个账号密码同样root
,每次计算都是不一样的值,很有意思的算法
然后配置datasource
的时候就可以用加密后的值来配:
spring:
datasource:
hikari:
username: ENC(cKLR2CaV**************1yNDA)
password: ENC(ckbTZkh53******************YbkpZ9YDk=)
url: jdbc:mysql://******************
这样就可以了,但你会发现这个密钥在配置文件中,不配还不行,那还是不够安全,可以在启动过程中通过java -jar xxx.jar -jasypt.encryptor.password=RjY2RDgyMzU3NDY*****************QzB3MDA=
的方式来配进去
如果还是不放心,那写到系统profile中,然后通过java -jar xxx.jar -jasypt.encryptor.password='$echo jasypt.encryptor.password'
来取出来,部署全流程不携带密码,启动的时候才在后台把密钥填进去,这种安全级别一般中型公司都能满足,唯一的问题就是需要一个可信的运维人员来保管密码
优化配置
我们编写application.yml时都有各种提醒,告诉你都有什么配置项,那这个是怎么做到的,我们自己能不能写出来一个工具,让Springboot引入时能够提醒你的配置文件呢?
想要实现这点,首先要引入一个依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
这个依赖作用就是分析additional-spring-configuration-metadata.json
文件,并生成对应的提醒到applicaiton.yml
里,这个文件的位置在resources/META-INF/additional-spring-configuration-metadata.json
(需要自己创建路径和文件),
具体如何配置,参考官网文档
给出我的例子:
{
"groups": [
{
"name": "spring.el-util",
"type": "com.el.utils.auto.configuration.ElApplicationConfiguration",
"sourceType": "com.el.utils.auto.configuration.ElApplicationConfiguration"
}
],
"properties": [
{
"name": "spring.el-util.enable-auto-config",
"type": "java.lang.Boolean",
"sourceType": "com.el.utils.auto.configuration.ElApplicationConfiguration",
"defaultValue": false
}
],
"hints": [
{
"name": "spring.el-util.enable-auto-config",
/* 该配置的可选项 true和false */
"values": [
{
"value": true,
"description": "开启."
},
{
"value": false,
"description": "关闭."
}
]
}
]
}
配置好运行一下,让configuration-processor
解析一下你的json配置,就会有这样的提示了,同理,你开发的二方包或三方包都可以用这种方式去写配置,配默认值等等,因为自己可能会有多个二方包,都依赖这个版本可能会有冲突,最好是让引用方去提供这个依赖
效果如下:
自动配置
首先抛出一个问题,为什么有些包引入后只要配置了,就可以自动把核心Bean注入到容器中,例如jdbc包,只要maven依赖引入了,如果没配置jdbc url这些就会报错,配置了就能在项目中直接使用了?
OK,SpringBoot约定大于配置,那一定是在什么地方有约定,spring和工具包提供商有约定,在什么地方告诉我需要把那些Bean加入到IOC容器中,在Bean初始化的过程中就会对配置进行一些校验。
那么思路清晰了之后,来看下具体时怎么实现的,通过观察,发现springboot项目的依赖库中有一个spring-boot-autoconfigure-2.2.2.RELEASE.jar
, 这个依赖中 META-INF/spring.factories
大概长这个样子:
这里看到jdbc相关的配置在org.springframework.boot.autoconfigure.EnableAutoConfiguration
有约定,DataSourceAutoConfiguration
,那这个类这个样子:
终于看到真相了,在官方autoconfig
包中,有大量的初始化声明,springboot
启动过程中回去挨个找,进去之后由条件注解做判断,如果符合某些条件,就自动搭建好对应的环境,如果搭建过程中缺少必要参数就报错,例如jdbc url缺失。
那看起来好像是官方约定的,我们没法改官方autoconfig
包的样子,其实只要我们在resources/META-INF
下新建一个spring.factories
文件,自己写个org.springframework.boot.autoconfigure.EnableAutoConfiguration=
即可,就相当于spring启动时会加载你这个类,具体做什么,需不需要条件,看你自己这个类怎么实现。
全局异常
现在大多数开发者都喜欢自定义异常体系,这样灵活,每个异常符合业务,不会跨业务线错误的拦截到其他人的异常,不过会带来一个问题,只拦截自己的异常,那真的出现NPE这种系统程序抛出的异常怎么处理,不会是每个接口外都要做一次try-catch
吧
/**
* @author eddie
* @description 全局异常处理类
*/
@Slf4j
@RestControllerAdvice
public class GlobalDefaultExceptionHandler {
@ExceptionHandler(Exception.class)
public Response defaultExceptionHandler(HttpServletRequest request, Exception e) {
if (e instanceof NullPointerException) {
log.error("NPE系统异常:{}", e.getMessage());
return new Response(Constants.STATE_FAIL, e.getMessage(), "失败");
}
log.error("全局拦截器截获异常", e);
return new Response(Constants.STATE_FAIL, "系统异常", null);
}
}
可以这样单独做一个处理器来处理异常,这个就相当于请求结束回到客户端过程的拦截器,如果发现携带异常,就会抓过来,那我们处理通过instanceof
或者自定义异常里面的内容就可以处理所有异常了,最后来一个兜底,保证不会把错误信息透露给外面,除非是我们希望外界知道的错误。
以上就是SpringBoot的一些技巧,还有一些不知道算不算技巧,也是太晚了,等以后有机会再续实用技巧时再聊吧