一个使用@Value(${...})的例子
下面是一段简单的服务器配置的代码:
@Data
@Component
public class ServerConfig {
/** 是否启用服务 */
@Value("${ali.server.enabled}")
private boolean enabled;
/** 服务器的远程地址 */
@Value("${ali.server.remote-address}")
private InetAddress remoteAddress;
/** 服务器的安全配置 */
@Autowired
private Security security;
@Component
@Data
public static class Security {
/** 服务器的用户名 */
@Value("${ali.server.security.username}")
private String username;
/** 用户密码 */
@Value("${ali.server.security.username}")
private String password;
/** 角色列表 */
@Value("${ali.server.security.roles}")
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
}
}
这段代码定义了如下的配置key:
key | 类型 | 描述 |
---|---|---|
server.enabled | boolean | 是否启用服务 |
server.remote-address | InetAddress | 绑定的ip地址 |
server.security.username | String | 安全用户名 |
server.security.password | String | 安全密码 |
server.security.roles | List<String> | 安全角色 |
Spring提供的ConversionService会将这些key的字符串配置值自动转换为对应的类型
另外@Value注解还支持SpEL表达式,可以说为Spring项目提供了强大的外部配置绑定能力。但对于复杂配置的管理能力不足:
- 基于字符串的表达式没有编译时类型检查
- 多处引用配置key时,需要重复引用字面量,容易出错
- 不容易重构,对key的命名变更
- 对复杂的对象配置比较困难
看上面的例子中Security需要声明为Bean才能支持@Value绑定,然后注入到Server中。如果是更复杂的场景,管理Bean会更困难
- 没有配置的元信息,想全局查看配置比较困难,编写配置文件时也没有任何可靠这自动校验很容易写错配置项
SpringBoot提供的类型安全配置方案
SpringBoot提供了@ConfigurationProperties注解,提供类型安全的配置声明方案。
利用@ConfigurationProperties修改上面的例子:
@Data
@Component
@ConfigurationProperties("ali.server")
public class ServerConfig {
/** 是否启用服务 */
private boolean enabled;
/** 服务器的远程地址 */
private InetAddress remoteAddress;
/** 服务器的安全配置 */
private Security security = new Security();
@Data
public static class Security {
/** 服务器的用户名 */
private String username;
/** 用户密码 */
private String password;
/** 角色列表 */
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
}
}
上述代码定义的配置key与使用@Value注解的例子是相同的,但具有如下特点:
- 代码更简单,不依赖key的字面量
- Security不需要声明为Bean,不需要注入到Server,可以支持更复杂的声明
- 结合
spring-boot-configuration-processor
提供的注解处理器可以自动生成配置的元信息
元信息中包含key的命名及从javadoc中读取的说明信息以及默认值。
有了这些元信息,在编写配置文件是使用IDEA编辑器可以自动弹出提示
有了编辑的辅助,相对不容易写错配置,且可以很容易看到看有哪些配置可以使用
因为有了这些元信息,IDEA的重构(重命名)命令可以自动做全局重构
- 避免使用字面量,不易出错,鼓励使用Bean注入来使用配置信息
- 多处引用Bean来读取配置时,都是Java代码级别的引用有编译时检查
ConfigurationProperties支持Spring Validation
@Data
@Component
@Validated
@ConfigurationProperties("ali.server")
public class Server {
/** 是否启用服务 */
private boolean enabled;
/** 服务器的远程地址 */
@NotNull
private InetAddress remoteAddress;
/** 服务器的安全配置 */
private Security security = new Security();
@Data
public static class Security {
/** 服务器的用户名 */
@NotBlank
private String username;
/** 用户密码 */
@Pattern(regexp = "\\w+\\d+")
private String password;
/** 角色列表 */
@Size(min = 1, max = 10)
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
}
}
利用Spring Validation集成,可以对配置提供灵活强大的校验能力。
支持第三方组件配置
如果依赖一个需要配置的第三方组件,要如何为其编写外部依赖?下面以DruidDataSource为例:
首先使用@Value来实现,那么代码如下:
@Data
@Component
public class DataSourceConfig {
@Value("db.url")
private String url;
@Value("db.username")
private String username;
@Value("db.password")
private String password;
@Value("db.driver-class-name")
private String driverClassName;
}
@Configuration
@ComponentScan
public class DataSourceConfiguration {
@Bean
public DruidDataSource dataSource(DataSourceConfig config) {
DruidDataSource ds = new DruidDataSource();
ds.setUrl(config.getUrl());
ds.setUsername(config.getUsername());
ds.setPassword(config.getPassword());
ds.setDriverClassName(config.getDriverClassName());
return ds;
}
}
db.url=jdbc://....
db.username=root
db.password=....
db.driver-class-name=DriverClassName
上述代码具有如下特点:
- 需要一个额外的配置类来做值绑定
- 创建DruidDataSource实例时需要做属性拷贝(可以使用BeanUtils.copyProperties替代)
- 灵活性差,增加配置需要修改DataSourceConfig类
加入现在要配置数据源的最大连接数,那么需要给DataSourceConfig类增加一个maxActive属性
下面使用@ConfigurationProperties来改写,代码如下:
@Configuration
public class DataSourceConfiguration {
@Bean
@ConfigurationProperties("db")
public DruidDataSource dataSource() {
return new DruidDataSource();
}
}
SpringBoot会自动识别DruidDataSource的所有属性,自动绑定到约定的key上。
例如有DruidDataSource.setMaxActive定义了一个maxActive属性,那么可以使用db.max-active来对其进行配置。
所以这段代码可以使用与上面使用@Value注解时同样的配置进行初始化。