前面读取yaml文件配置时,用过这个注解,先复习下它的用法。首先在yml配置文件中定义个属性:
# spring默认在用server,我加个s刻意回避下,别影响到服务启动
servers:
hostAddr: http://127.0.0.1
port: 9527
timeout: 6000
定义实体类接收这个参数:
OData
@Component //首先它得是受Spring管控的对象,即Bean,不然我哪怕拿到值,我怎么联系你给你
@ConfigurationProperties("servers")
public class ServerInfo {
private String hostAddr:
private int port:
private long timeout;
}
接下来看下效果,懒得写UT,再获取IoC容器对象了 ,直接借用启动类run方法返回的IoC对象来获取上面这个Bean:
@SpringBootApplication
public class MyApplication{
public static void main(String[] args){
ConfigurableApplicationContext applicationContext = SpringApplication.run(MyApplication.class,args);
ServerInfo bean = applicationContext.getBean(ServerInfo.class);
System.out.printin(bean);
}
}
@ConfigurationProperties中写你要加载的属性的上一层级的
,如果上一层还有父级,则xxx.xx就行。最后,@ConfigurationProperties注解使用时可能有warn信息:
要解除使用@ConfigurationProperties注释警告,引入以下这个依赖然后IDEA中点Hide隐藏就行:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
dependency>
有个关联知识点:@EnableConfigurationProperties注解
,修改上面的代码,启动类上加@EnableConfigurationProperties注解:
@SpringBootApplication
@EnableConfigurationProperties(ServerInfo.class)
public class MyApplication{
public static void main(String[] args){
ConfigurableApplicationContext applicationContext = SpringApplication.run(MyApplication.class,args);
ServerInfo bean = applicationContext.getBean(ServerInfo.class);
System.out.printin(bean);
}
}
此时,重启服务报错:excepted single matching bean but found 2
这里的原因是@EnableConfigurationProperties(ServerInfo.class)指定了ServerInfo.class,就会把这个类的Bean加载进来,而之前ServerInfo类中又用@Component注册了一次。解决报错把@Component注释掉就好。因此:
@EnableConfigurationProperties注解不能与@Component注解同时使用!
@ConfigurationProperties和@EnableConfigurationProperties,前者是做属性绑定的,后者是开启属性绑定,并设定对应的目标是谁。
@EnableConfigurationProperties(ServerInfo.class)
后者的好处是可以通过这一行代码清晰看到都有哪些类从yml中读取了属性,使得对于这种类的管理不松散。
以上是给我们自定义的Bean注入属性,那第三方Bean呢?@ConfigurationProperties也可以实现。先写个测试的yml配置:
datasource:
driverClassName: com.mysql.hhhh # 我只是测拿数据,不连数据库,驱动随便写
再定义第三方Bean,@SpringBootApplication注解本身往下追就有@Configuration注解,所以这里直接在启动类里定义第三方Bean
@SpringBootApplication
public class MyApplication{
@Bean
@ConfigurationProperties(prefix = "datasource")
public DruidDataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
return dataSource;
}
public static void main(String[] args){
ConfigurableApplicationContext applicationContext = SpringApplication.run(MyApplication.class,args);
DruidDataSource bean = applicationContext.getBean(DruidDataSource.class);
System.out.printin(bean);
System.out.printin(bean.getDriverClassName());
}
}
运行,属性绑定成功:
@ConfigurationProperties绑定属性支持属性名宽松绑定 ,即:
OData
@Component
@ConfigurationProperties("servers")
public class ServerInfo {
private String ipAddress:
private int port:
private long timeout;
}
属性的名字和配置文件的字段不用一模一样,以下配置都是生效的:
servers:
ipAddress: 192.168.1.1
port: 2345
timeout: -1
servers:
ip_address: 192.168.1.2
port: 2345
timeout: -1
servers:
ip-address: 192.168.1.2
port: 2345
timeout: -1
servers:
IP_ADDRESS: 192.168.1.2
port: 2345
timeout: -1
servers:
ip-ad-d_rEss: 192.168.1.2
Port: 2345
timeout: -1
当然实体类中的属性名也一样:
public class ServerInfo {
private String ipADDRESS:
private int port:
private long timeout;
}
但@ConfigurationProperties注解的prefix属性,前缀名必须符合命名规范,即仅能使用纯小写字母、数字、下划线作为合法的字符,大写都不行。此外,@Value注解不支持宽松绑定,大小写不一样就会解析失败。
SpringBoot支持JDK8提供的时间与空间计量单位,@DurationUnit和@DataSizeUnit注解标明所在属性的值的单位。还是上面的例子,加个属性serverTimeOut做调试:
servers:
ipAddress: 192.168.1.1
port: 2345
timeout: -1
serverTimeOut: 6
此时, serverTimeOut值6的单位是不确定的,实体类中使用一个时间类Duration来接收:
OData
@Component
@ConfigurationProperties("servers")
public class ServerInfo {
private String ipAddress:
private int port:
private long timeout;
private Duration serverTimeOut;
}
打印这个Bean,可以看到0.06s,即默认为ms:
...
@DurationUnit(ChronoUnit.MINUTES)
private Duration serverTimeOut;
...
同理,还有空间单位,默认单位Byte,可加@DataSizeUnit注解指定:
OData
@Component
@ConfigurationProperties("servers")
public class ServerInfo {
private String ipAddress:
private int port:
private long timeout;
@DurationUnit(ChronoUnit.MINUTES)
private Duration serverTimeOut;
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize dataSize;
}
开启数据校验有助于系统安全性,J2EE规范中JSR303规范定义了一组有关数据校验相关的API,先导入JSR303的依赖坐标:
<dependency>
<groupId>javax.validationgroupId>
<artifactId>validation-apiartifactId>
dependency>
然后在需要校验的地方开启数据校验:
//springframework下的
@Valiated
然后给属性加具体的校验规则,相关注解有:
此时,运行出现报错:
报错信息为找不到具体的实现,这就和JDBC和mysql驱动是一个意思,有接口,代码能写,但一运行,没有实现,跑不起来。再引入JSR303校验框架的实现,如Hibernate:
<dependency>
<groupId>org.hibernate.validatorgroupId>
<artifactId>hibernate-validatorartifactId>
dependency>
再运行,校验成功:
JSR303的校验,用处很多,比如写接口时,对前端的传参做校验,只需要在Dto类中的属性加相关注解即可,如:
添加校验规则:
在yaml中定义一个属性password,取值0127,使用属性绑定或者@Value取值,打印这个值:
问题的原因就是,在yaml中,书写数字和简单字符串都不用加引号,而0127又特殊在以0开头,后面的字符在0-7之间,被当作了八进制,可通过加引号解决:
datasource:
password: "0127"