1. 前言
Spring 在配置文件方面的支持非常强大,本文不再赘述,有需求可以查看 Spring Boot 的官方文档。本文的叙述内容是如何在程序中使用 Spring Boot 配置文件中的参数值。常见的手法有三种:
-
@Value
注解 -
对象
-
@ConfigurationProperties
注解
2. @Value
先从 @Value
说起,通过配置完整名称的形式即可获取需要的值,如:
"${myConfig.name}")
(private String name;
但是需要注意的一点是 @Value
注入的时间一般在 Bean 构造完成之后。如果构造 Bean 的方法需要使用到配置文件里的参数,那么可以把这些参数作为构造函数或者 @Bean
注解修饰的方法的传入参数,并以 @Value
注解来指定注入的参数。如:
"client1")
(public Client getClient( ("${endPoint.beijing}") String endPoint) {
Client client = new Client();
client.setEndPoint(endPoint);
return client;
}
3. org.springframework.core.env.Environment
@Value
注解在获取少量配置的时候还是相当方便的,但是如果我们需要从配置文件中获取大量配置的时候往往需要定义大量的实例变量,就不如直接从 Environment
获取来的方便。Environment
对象可以直接通过 @Autowired
注解注入得到。从 Environment
获取配置的方式也相当简单,如:
String myConfigNameOfEnv = environment.getProperty("myConfig.name");
getProperty
方法还有可以指定默认值和参数类型的重载方法,此处不展开。
4. @ConfigurationProperties
通过 @ConfigurationProperties
注解获取配置有两种形式:
-
将某一个前缀下的所有配置映射为一个对象(Type-safe Configuration Properties)
-
将某一个前缀下的所有配置扁平化结构作为一个 Map(Merging Complex Types)
下面给出一个示例,对于如下配置:
myConfig
name myConfig
list
a
byourConfig
name yourConfig
list
c d
如果我们想使用 myConfig
前缀下的所有配置,那么两种方案分别可以按照如下形式获取。
方案一:
prefix = "myConfig")
(
public class MyConfig {
private String name;
private List<String> list;
}
这里通过前缀和字段的名字来映射配置。
方案二:
prefix = "")
(
public class ConfigAsMap {
private Map<String, String> myConfig;
private Map<String, String> yourConfig;
}
这里我们将所有所有配置映射为一个对象,每一个 namespace
下的配置以 Map 的形式来存储。使用的时候可以按照如下方式获取特定配置:
Map<String, String> myConfigMap = configAsMap.getMyConfig();
String nameOfConfigMap = myConfigMap.get("name");
这里还要记述一下 Environment
对象和 @ConfigurationProperties
映射为 Map 后两者在获取列表时的区别。
对于 Environemnt
对象,我们要获取 myConfig.list
对象时的方式如下:
List<String> myConfigListOfEnv = Lists.newArrayList(environment.getProperty("myConfig.list[0]"),
environment.getProperty("myConfig.list[1]"));
而通过 @ConfigurationProperties
映射为 Map 后获取的方式则是:
List<String> listOfConfigMap = Lists.newArrayList(myConfigMap.get("list.0"),
myConfigMap.get("list.1"));
两者在处理列表的索引时不一致,使用时需要我们注意。
5. 跨 Module 引用配置
最近开始接手改造一个项目,改造过程中有这样一个问题:该项目原来所有的配置都采用集中管理的方式,每一个项目的配置都从配置服务器的接口获取然后初始化 Spring 的容器。该项目将从配置服务器获取配置的部分抽出为一个单独的 Maven Module。
但是这个配置集中管理的项目即将下线,于是配置本地化的改造就迎面而来了。
我的改造方案是以不同的 profile 的形式将配置固化到配置 Module 的配置文件中。在线上环境和测试环境通过指定不同的 profile 达到配置的切换。
对于运行时指定的 profile 能否作用到依赖的 Maven 模块并没有绝对把握,而改造的范围比较广,所以还是捏了一把汗的。于是写了一个小的 Demo 作为原型验证。
首先给出配置文件:
application.yaml
myConfig
name myConfig
list
a
byourConfig
name yourConfig
list
c d
application-test.yaml
hisConfig
name hisConfig-test
list
g h
application-prod.yaml
hisConfig
name hisConfig-prod
list
e f
两种通过 @ConfigurationProperties
获取配置的方式:
package com.avaloninc.springconfigexample.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.List;
prefix = "myConfig")
(
public class MyConfig {
private String name;
private List<String> list;
}
以及:
package com.avaloninc.springconfigexample.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
prefix = "")
(
public class ConfigAsMap {
private Map<String, String> myConfig;
private Map<String, String> yourConfig;
private Map<String, String> hisConfig;
}
先在当前 Module 测试一下:
package com.avaloninc.springconfigexample;
import com.avaloninc.springconfigexample.config.ConfigAsMap;
import com.avaloninc.springconfigexample.config.MyConfig;
import junit.framework.TestCase;
import org.assertj.core.util.Lists;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import java.util.Map;
SpringRunner.class)
(value = "test")
(public class MainTest extends TestCase {
"${myConfig.name}")
( private String name;
private Environment environment;
private ConfigAsMap configAsMap;
private MyConfig myConfig;
public void test() {
System.out.println("this.name = " + this.name);
String myConfigNameOfEnv = environment.getProperty("myConfig.name");
List<String> myConfigListOfEnv = Lists.newArrayList(environment.getProperty("myConfig.list[0]"),
environment.getProperty("myConfig.list[1]"));
System.out.println("myConfigNameOfEnv = " + myConfigNameOfEnv);
System.out.println("myConfigListOfEnv = " + myConfigListOfEnv);
String myConfigName = myConfig.getName();
List<String> myConfigList = myConfig.getList();
Map<String, String> myConfigMap = configAsMap.getMyConfig();
String nameOfConfigMap = myConfigMap.get("name");
List<String> listOfConfigMap = Lists.newArrayList(myConfigMap.get("list.0"),
myConfigMap.get("list.1"));
System.out.println("myConfig = " + myConfig);
System.out.println("configAsMap = " + configAsMap);
assertEquals(this.name, myConfigNameOfEnv);
assertEquals(myConfigNameOfEnv, myConfigName);
assertEquals(myConfigName, nameOfConfigMap);
assertEquals(myConfigListOfEnv, myConfigList);
assertEquals(myConfigList, listOfConfigMap);
}
}
然后我们在另一个 Module 中引用这里的配置,下面是单元测试:
package com.avaloninc.springtestexample;
import com.avaloninc.springconfigexample.config.ConfigAsMap;
import com.avaloninc.springconfigexample.config.MyConfig;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
SpringRunner.class)
("test")
(ConfigAsMap.class, MyConfig.class})
({public class MainTest extends TestCase {
private ConfigAsMap configAsMap;
private MyConfig myConfig;
public void test() {
System.out.println("configAsMap = " + configAsMap);
assertEquals(configAsMap.getHisConfig().get("name"), "hisConfig-test");
System.out.println("myConfig = " + myConfig);
}
}