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的使用功能,在不同的条件下使其进行加载;

posted @ 2022-06-05 18:51  雩娄的木子  阅读(670)  评论(0编辑  收藏  举报