springboot基础之配置文件和属性绑定功能
SpringBoot基础功能
条件装配
Spring中的@Conditional注解及其衍生注解的说明
在实际的项目开发中,经常遇到根据不同的环境做出不同的配置的需求,springboot中提供了将不同的配置写在指定的环境配置文件中,服务器启动时回读取指定的配置文件,做出对应的装配。为了应对更加灵活的装配不同的环境或@Bean,springboot还提供了其他的方式,在此介绍三种可选的条件装配方式。
profile
@Bean
@Profile("dev")
public Object init() {
System.out.println("dev环境时装配bean");
return new Object();
}
@Bean
@Profile("prod")
public Object init() {
System.out.println("prod环境时装配bean");
return new Object();
}
Condition接口
public class EnvCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return "true".equalsIgnoreCase(context.getEnvironment().getProperty("***.***"));
}
}
@Component
@Conditional(EnvCondition.class)
public class ***Service {
//...
}
ConditionalOnProperty
@Component
@ConditionalOnProperty(name="***.***", havingValue="true")
public class ***Service {
//...
}
当在properties文件中读取***.***的值等于true时,会实例化***Service对象;反之,不会创建对象。
以上3中方式都可以进行条件装配,可根据具体业务需求选择应用,推荐搭建应用@ConditionalOnProperty的方式。
至于一些注解:
@ConditionalOnBean、@ConditionalOnMissingBean:容器中有该bean或者是没有bean的时候;
@ConditionalOnClass、@ConditionalOnMissingClass:容器中有该类或者是没有该类;
@ConditionalOnProperty:配置文件中有某个值的时候生效;
@ConditionalOnSingleCandidate:容器中只有一个候选的组件;
例子:
依赖属性:比如,如果想要本配置类在满足某个属性条件才加载的话,例如下面
就是说如果spring.main.web-application-type指定值为SERVLET,或者没指定任何值的话就加载。
@ConditionalOnProperty(name="spring.main.web-application-type", havingValue = "SERVLET", matchIfMissing = true)
依赖类:如果想要本配置是否加载取决于是否已经加载了某些类,可以如下 ,当容器里面有加载servlet等三个类的话才加载本类:
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
当然,如果要判断本项目是否开启了web服务的话,再加上下面这个条件更准确
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
通常将不易修改的信息放置在配置文件中,在springboot中是对应的yaml文件。
那么在springboot中,可以将配置文件中的配置信息绑定到javabean中来,无论是简单类型还是复杂类型,都是可以的。
JavaBean绑定配置文件属性
第一种方式
如果想要来配置javabean绑定到配置文件中,那么这个javabean必须得是容器中的组件。
配置文件:
user:
id: 666
name: lig
java类:
@Data
@Component // 成为组件
@ConfigurationProperties(prefix = "user") // 属性配置绑定
public class User {
private Integer id;
private String name;
}
注意:需要提供get/setter方法才能够来设置对应的值。
第二种方式
在配置类上添加@EnableConfigurationProperties注解,表示:1、开启属性自动配置功能;2、成为容器中的组件;
@SpringBootApplication
// 只能使用到配置类上
@EnableConfigurationProperties(value = {User.class})
public class SpringobootBasicOneApplication {}
对应的user类:
@Data
@ConfigurationProperties(prefix = "user")
public class User {
private Integer id;
private String name;
}
这样子也是可以来进行实现的。
区别
第一种方式和第二种方式都是可以将配置文件中的属性和javabean来进行绑定。但是从第一种中可以看到,只能够在我们自己写的类上来进行标注,而无法针对于第三方包中的类来进行标注。所以这里是一个弱点,但是这个弱点对于第二种配置方式来说,是可以来解决的。
可以看到主要归功于注解:@EnableConfigurationProperties,将类添加成容器中的组件并且开启属性绑定功能
配置文件
有yaml、yml以及properties三种写法。但是开发中最常用的是yml语言来进行使用。
语法
- k: v格式(:后有空格)
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab键,只允许使用空格
- 缩进的空格数量不重要,只要相同层级的元素左对齐即可(一般说来,这里要空两格即可)
- ‘#’表示注释
- ''和""表示的是字符串内容。如果是''单引号,表示的是可以会被转义;如果是“”双引号,那么表示的是不需要来进行转义
数据类型
- 字面量。单个的、不可再分的值。date、boolean、string、number、null
k: v
- 对象:键值对的集合。map、hash、set、object
行内写法: k: {k1:v1,k2:v2,k3:v3}
#或
k:
k1: v1
k2: v2
k3: v3
- 数组:一组按次序排列的值。array、list、queue
行内写法: k: [v1,v2,v3]
#或者
k:
# 一个-表示的是一个集合中的元素
- v1
- v2
- v3
以上三种足以解决配置绑定中的问题。来个例子来进行说明:
@Data
public class Person {
private String userName;
private Boolean boss;
private Date birth;
private Integer age;
private Pet pet;
private String[] interests;
private List<String> animal;
private Map<String, Object> score;
private Set<Double> salarys;
private Map<String, List<Pet>> allPets;
}
@Data
public class Pet {
private String name;
private Double weight;
}
对应的yml配置文件中的书写内容:
person:
userName: lig
boss: true
# 默认是/开头
birth: 2022/06/05
age: 11
# 对象
pet:
name: pet
weight: 12.00
interests:
# -表示的是一个元素
- 篮球
- 足球
- 音乐
animal: [狗,猫,鸟]
score:
chinese: 12
math: 12
english: 88
salarys: [12.03,12.52,47.12]
allPets:
sick:
- name: a
weight: 22.0
- name: b
weight: 23.07
health:
- name: c
weight: 24.09
- name: d
weight: 25.02
特殊符号改变语义
使用特殊符号来进行举例:
company:
name: 'tl \n company'
address: "tl \n company"
使用value来进行打印:
@Test
public void testValue(){
System.out.println("value1的值是:"+value1); // value1的值是:tl \ company
System.out.println("value2的值是:"+value2); // value2的值是:tl company
}
value1的值是:tl \n company
value2的值是:tl
company
可以看到使用“”并不会改变原来的语义,而使用''会改变对应的语义。
使用value("#{}")和value("${}")
/**
* Value("#{}"):可以取ioc容器中的bean( 写bean名)
* Value("${}"):作用是取配置文件中的值 注意:这里如果没有读取到,还可以给一个默认的值
*/
@Value(value = "#{user}")
private User user1;
@Value("${user.name}")
private String userName;
@Value("${user.id}")
private Integer id;
@Value("${user.num:123}")
private Integer num;
@Test
public void testGetYml() {
System.out.println("从容器中获取得到的user对象是:"+user1);
System.out.println("从配置文件中获取得到的username是:"+userName);
System.out.println("从配置文件中获取得到的id是:"+id);
System.out.println("从配置文件中读取到的值是:"+num); // 从配置文件中读取到的值是:123
}
注意:上面的user是自己来进行配置的
@SpringBootApplication
@EnableConfigurationProperties(value = {User.class, Person.class})
public class SpringobootBasicOneApplication {
public static void main(String[] args) {
SpringApplication.run(SpringobootBasicOneApplication.class, args);
}
@Bean
public User user(){
return new User();
}
}
对应的user类:
@Data
@ConfigurationProperties(prefix = "user")
public class User {
private Integer id;
private String name;
}
对应的配置文件:
user:
id: 666
name: lig
在打印的时候
从容器中获取得到的user对象是:User(id=666, name=lig)
结论:如果对应的类开启了属性配置,那么即使创建了一个新的对象的时候,也会来进行属性注入
配置文件中开启自动提示功能
在使用javabean来进行配置的时候,需要将配置文件中的属性绑定到javabean的时候发现没有提示。如果想要提示,那么需要有以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
但是应该在打包的时候不应该打包进jar包中来
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<!--直接排除在外-->
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
<mainClass>com.guang.springobootbasicone.SpringobootBasicOneApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
maven在进行打包的时候,将这个依赖排除在外,不然浪费内存空间,因为打包成jar包加载之后,也需要占用内存的。
Profile功能
为了方便多环境适配,springboot简化了profile功能。为了来区别开发、测试和生产环境中配置文件中不同的配置
直接来说对应的结论:
-
默认配置文件 application.yaml;任何时候都会加载【重点】
-
指定环境配置文件 application-{env}.yaml
-
激活指定环境
-
- 配置文件激活
- 命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=haha 【重要】
-
-
- 修改配置文件的任意值,命令行优先
- 使用命令行修改指定配置文件的优先级,命令行优先
-
-
默认配置与环境配置同时生效
-
同名配置项,profile配置文件优先
那么下面来举一个例子:
application.properties
per.name=lig
server.port=8080
# 激活指定的环境
spring.profiles.active=dev
application-dev.yml
per:
name: lim
server:
port: 8081
application-prod.yml
per:
name: lixiang
server:
port: 8082
因为默认加载的是dev环境,也就是说对于:per.name来说,应该是dev环境中的lim,那么写个controller来注入对应的值打印到浏览器端看下对应的值。
那么同样如此的是:利用一个接口,来写两个子类,然后两个子类分别在不同的环境下激活。激活一个环境之后,让其中的一个生效。
public interface Foot {
String getName();
}
@Data
@Profile("dev")
@Component
@ConfigurationProperties(prefix = "foot")
public class Apple implements Foot {
private String name;
}
@Data
@Profile("prod")
@Component
@ConfigurationProperties(prefix = "foot")
public class Balala implements Foot{
private String name;
}
因为在application.properties中配置的是:
spring.profiles.active=dev
那么配置文件中的application-dev.yml对应的值是:
per:
name: lim
server:
port: 8081
foot:
name: 111
所以会使用到这里的。
同理,@Profile不止只有这里可以使用,还可以在配置类类上、配置类方法上使用。例子:
@SpringBootApplication
@EnableConfigurationProperties(value = {User.class, Person.class})
public class SpringobootBasicOneApplication {
public static void main(String[] args) {
SpringApplication.run(SpringobootBasicOneApplication.class, args);
}
@Bean
public User user(){
return new User();
}
@Profile("dev")
@Bean
public Color red(){
Color color = new Color();
color.setColor("red");
return color;
}
@Profile("dev")
@Bean
public Color yellow(){
Color color = new Color();
color.setColor("yellow");
return color;
}
}
在这里来新建了两个Color的类,可以根据对应的环境不同分别来创建对应的对象。
配置分组
也就是说,可以将多个配置文件(yml)分配到一个组中来进行运行,但是这块有点鸡肋,所以不来记录。
总结
1、使用条件装配注解:ConditionalOnProperty,当配置文件中有哪些值的时候,让当前的条件装配生效。
2、属性配置绑定:①javabean和配置文件中的属性进行绑定;②在程序中使用导入配置文件中的值:注意各种数据类型的编写方式;
3、@Profile的使用功能,在不同的条件下使其进行加载;