铿然架构 | 作者 / 铿然一叶 这是 铿然架构 的第 118 篇原创文章
1. 介绍
在应用系统中通常都需要通过获取配置参数来增加灵活性,例如数据库的用户名和密码、连接池配置,ssl配置等等。
配置参数有多种配置方式,如通过开源软件Nacos、Apollo配置,在K8S中通过Configmap或Secret配置,而Spring也提供了自身的参数配置和获取能力,本文将介绍Spring的配置参数能力和使用方式。
2. Spring配置参数
2.1 能力
● 参数来源:配置文件和环境变量。
● 文件格式:yml和properties文件。
● 参数类型:单值,数组,List,Map。
2.2 相关注解
Spring配置参数要使用注解,涉及注解如下:
注解 | 描述 |
---|---|
PropertySource | 用于指定配置参数来源文件,依赖Configuration注解才会生效。 |
Value | 指定配置参数名或环境变量名,依赖Component注解才会生效。 |
ConfigurationProperties | 指定配置参数获取时配置节点的起始位置,依赖Component注解才会生效。 |
注:因为Configuration注解包含了Component注解,因此Value和ConfigurationProperties注解依赖Configuration注解也一样有效。
2.3 配置例子
2.3.1 指定配置文件
如果不指定配置文件,默认从application.yml和application.properties中获取。
2.3.1.1 properties文件
java
@Configuration
@PropertySource(value = "classpath:cluster.properties")
public class PropertiesConfiguration {
}
文件路径可以是classpath或绝对路径,绝对路径例子:
java
"file:/home/app/cluster.properties"
2.3.1.2 yml文件
PropertySource注解默认不支持解析yaml文件,需要自定义一个工厂类来解析。
java
@Configuration
@PropertySource(value = "classpath:cluster.yml", factory = YAMLPropertySourceFactory.class)
public class YAMLConfiguration {
}
工厂类代码:
java
public class YAMLPropertySourceFactory implements PropertySourceFactory {
public org.springframework.core.env.PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return name != null ? new PropertiesPropertySource(name, properties) :
new PropertiesPropertySource(encodedResource.getResource().getFilename(),properties);
}
}
2.3.2 指定配置节点起始位置
如下代码:
java
@Component
@ConfigurationProperties(prefix = "db")
public class DBMapParam {
表示从db节点之后,即connectInfo开始读取配置参数:
java
db:
connectInfo:
user: kengcoder
password: XDIxde!322332ec
如果配置为:
java
@ConfigurationProperties(prefix = "db.connectInfo")
则从user开始读取。
2.3.3 Value注解配置方式
Value注解可以从文件或环境变量获取参数,支持单值,数组,List,Map。
代码:
java
@Component
public class AppValueAndEnvParam {
// 获取单值参数
@Value("${app.name}")
private String name;
// 单值参数设置默认值,如果没有配置则取默认值
@Value("${app.id:000}")
private String defaultAppId;
// 获取环境变量,如果配置和环境变量都有,优先取环境变量
@Value("${JAVA_HOME}")
private String javaHome;
// 获取数组配置
@Value("${app.whitelistIps}")
private String[] whitelistIps;
// 获取列表配置
@Value("${app.whitelistIps}")
private List<String> whitelistIpList;
// 获取map配置
@Value("#{${db.connectInfoForValue}}")
private Map<String, String> dbConnectInfo;
// 获取所有环境变量
@Value("#{systemProperties}")
private Map<String, String> systemPropertiesMap;
}
配置:
java
app:
name: myapp
whitelistIps: 10.192.8.3,10.192.8.4,10.192.8.5
db:
connectInfoForValue: '{"user": "kengcoder", "password": "XDIxde!322332ec", "url": "jdbc:mysql://10.9."}'
输出:
java
(name=myapp, defaultAppId=000, javaHome=C:\Program Files\Java, whitelistIps=[10.192.8.3, 10.192.8.4, 10.192.8.5], whitelistIpList=[10.192.8.3, 10.192.8.4, 10.192.8.5], dbConnectInfo={user=kengcoder, password=XDIxde!322332ec, url=jdbc:mysql://10.9.})
2.3.4 Bean配置方式
Bean配置方式从配置文件获取参数,支持单值,数组,List,Map。
通过Bean方式配置,通常要指定配置参数起始节点。
2.3.4.1 复杂Bean
这里演示一个嵌套Bean配置,包括单值,数组和List参数。相关类如下:
代码:
java
@Component
@ConfigurationProperties(prefix = "app")
public class ClusterObjectParam {
private String name;
private Cluster cluster;
}
@Data
@ToString
public class Cluster {
private String id;
private String name;
private List<Node> nodes;
}
@Data
@ToString
public class Node {
private String nodeId;
private String nodeType;
private Service[] services;
}
@Data
@ToString
public class Service {
private String name;
private int port;
}
配置:
java
app:
name: myapp
cluster:
id: c-101
name: my-cluster
nodes:
- nodeId: n-101
nodeType: app
services:
- name: account
port: 8001
- name: customer
port: 8002
- nodeId: n-102
nodeType: db
输出:
java
name: myapp
Cluster(id=c-101, name=my-cluster, nodes=[Node(nodeId=n-101, nodeType=app, services=[Service(name=account, port=8001), Service(name=customer, port=8002)]), Node(nodeId=n-102, nodeType=db, services=null)])
2.3.4.2 Map
代码:
java
@Component
@ConfigurationProperties(prefix = "db")
public class DBMapParam {
private Map<String, String> connectInfo;
}
配置:
java
db:
connectInfo:
user: kengcoder
password: XDIxde!322332ec
url: "jdbc:mysql://10.9.2.7:3306/accountdb"
输出:
java
{password=XDIxde!322332ec, url=jdbc:mysql://10.9.2.7:3306/accountdb, user=kengcoder}
2.3.5 两种配置方式比较
● 便利性
对于对象参数,Bean配置方式比较简单,只需要对象属性名和参数名一致,同时指定配置起点则可。
Value注解则需要一个个的指定配置参数名。
● 能力
两者都支持单值、数组、List、Map参数,Value支持从文件和环境变量获取参数,而Been配置方式只支持从文件获取参数。
● Map配置格式差异
两者Map参数的配置格式不同,配置不对会解析错误。
Value注解配置方式,必须是json格式的键值对:
java
db:
connectInfoForValue: '{"user": "kengcoder", "password": "XDIxde!322332ec", "url": "jdbc:mysql://10.9."}'
对象获取参数配置方式,直接配置键值对,不能是json格式:
java
db:
connectInfo:
user: kengcoder
password: XDIxde!322332ec
url: "jdbc:mysql://10.9.2.7:3306/accountdb"
3. 总结
本文介绍了Spring的配置参数能力,其参数来源支持从配置文件和环境变量获取,文件格式支持properties和yaml,参数格式支持单值,数组,List和Map。
总体来看Spring的配置属于静态配置,缺少动态刷新(例如定期修改数据库密码),版本管理等能力,在选型时要结合实际业务场景判断是否满足要求。
其他阅读:
萌新快速成长之路
如何编写软件设计文档
Spring Cache架构、机制及使用
布隆过滤器适配Spring Cache及问题与解决策略
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(五)函数式接口-复用,解耦之利刃