SpringBoot读取配置文件到实体类和静态变量

SpringBoot读取配置文件到实体类和静态变量
强烈推介IDEA2020.2破解激活,IntelliJ IDEA 注册码,2020.2 IDEA 激活码

目录

1.读取配置文件到实体类
 1.1 配置文件和实体类准备
 1.2 读取配置文件到实体类的方法
  1.2.1 @Value注解
  1.2.2 @ConfigurationProperties注解
  1.2.3 @PropertySource注解加载其他配置文件
  1.2.4 @ImportResource注解应用xml配置文件
2.读取配置文件到静态变量
 2.1 在方法上添加@Value注解
 2.2 使用@PostConstruct注解在初始化Servlet之前初始化静态变量

1.读取配置文件到实体类

在实际开发中,我们知道数据库的配置信息不能在代码中写死,得放到配置文件中,动态的去读取,这样当我们修改数据库密码或者其他参数的时候,就不用重新编译和打包了,直接修改配置文件即可。所以,和数据源类似,有时我们需要从配置文件中读取数据到实体类中。

1.1 配置文件和实体类准备

首先在SpringBoot的application.yml 配置文件中配置示例数据emp:

server:
  port: 80
#emp的配置数据
emp:
  name: korbin
  age: 21
  map:
    key1: value1
    key2: value2
  list:
    - one
    - two
    - three
  forte:
    name: java
    time: 8

示例实体类emp,必须包含set方法才能实现后续将从配置文件中读取的值注入到实体类的对应变量中:

public class Emp{
   
    private String name;
    private int age;
    private Map map;
    private List list;
    private Forte forte;

    @Override
    public String toString() {
   
        return "Emp{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", map=" + map +
                ", list=" + list +
                ", forte=" + forte +
                '}';
    }

    public void setName(String name) {
   this.name = name;}

    public void setAge(int age) {
   this.age = age;}

    public void setMap(Map map) {
   this.map = map;}

    public void setList(List list) {
   this.list = list;}

    public void setForte(Forte forte) {
   this.forte = forte;}
}

1.2 读取配置文件到实体类的方法

SpringBoot提供了多种注解来快速读取配置文件中值的方式,如下:

1.2.1 @Value注解

首先需要在实体类上添加@Component注解表示将该实体类放入到Spring容器中,然后只需要在每个成员变量上添加@Value注解即可,注解中的参数值为配置文件中的参数名,并用${}包起来:

@Component
public class Emp{
   
    @Value("${emp.name}")
    private String name;
    @Value("${emp.age}")
    private int age;
    private Map map;//Map和List和对象不能使用@Value
    private List list;//Map和List和对象不能使用@Value
    private Forte forte;//Map和List和对象不能使用@Value

    public void setName(String name) {
   this.name = name;}

    public void setAge(int age) {
   this.age = age;}

    public void setMap(Map map) {
   this.map = map;}

    public void setList(List list) {
   this.list = list;}

    public void setForte(Forte forte) {
   this.forte = forte;}
}

在测试类中进行测试,下述其他注解以同样方式测试,后文不再赘述。且为了表述简洁,实体类中的toString方法请自行添加:

@RunWith(SpringRunner.class)
@SpringBootTest
public class HellospringbootApplicationTests {
   

    @Autowired
    private Emp emp;
    @Test
    public void contextLoads() {
   
        System.out.println(emp);
    }
}

注意:该注解仅能读取单个值,对于Map、List和Bean对象这类数据不能读取和自动注入,也就是说不能读取一系列的多个数据。 所以上述示例仅对name和age两个成员变量添加了@Value注解。

1.2.2 @ConfigurationProperties注解

对于@Value注解的局限性,@ConfigurationProperties注解对其进行了解决,该注解可以实现对配置文件中的属性值批量读取和注入。

使用方法依旧是首先添加@Component注解将实体类放入到Spring容器中,然后添加@ConfigurationProperties注解,并指定该注解的prefix参数表示对应实体类的前缀:

@Component
@ConfigurationProperties(prefix = "emp")
public class Emp{
   
    private String name;
    private int age;
    private Map map;
    private List list;
    private Forte forte;

    public void setName(String name) {
   this.name = name;}

    public void setAge(int age) {
   this.age = age;}

    public void setMap(Map map) {
   this.map = map;}

    public void setList(List list) {
   this.list = list;}

    public void setForte(Forte forte) {
   this.forte = forte;}
}

该注解可以实现对上述配置文件中的单个值、Map、List集合和其他对象的快速注入到实体类中。所以@Value@ConfigurationProperties 注解的差异在于:需要批量注入配置文件的属性值时用后者,需要指定单个属性值时用前者。

1.2.3 @PropertySource注解加载其他配置文件

上述的@ConfigurationProperties注解和 @ConfigurationProperties 注解默认都是从SpringBoot的全局配置文件application.properties/application.yml中获取的值。所有配置数据写在全局配置文件中,会显得配置文件过于臃肿和杂乱,所以可将和全局配置无关的配置参数(如上述的实体类参数)抽取出来,单独放到另一个局部配置文件中。这就是@PropertySource注解的作用,指定想要读取的配置文件的来源。

首先将上述application.yml配置文件中的emp示例的配置数据全部抽取到新建的一个emp.properties 文件中,放到application的同级目录resources下。

#emp的配置数据
emp.name=korbin
emp.age=21
emp.map.key1=value1
emp.map.key2=value3
emp.list=one,two,three
emp.forte.name=java
emp.forte.time=10

注解使用方法只需在上述注解使用的基础上,添加@PropertySource注解即可,将默认配置文件改为我们自己指定的配置文件,注解参数value属性是数组类型,用于指定配置文件位置。:

@Component
@ConfigurationProperties(prefix = "emp")
@PropertySource(value = {
   "classpath:emp.properties"})
public class Emp{
   
    private String name;
    private int age;
    private Map map;
    private List list;
    private Forte forte;

    public void setName(String name) {
   this.name = name;}

    public void setAge(int age) {
   this.age = age;}

    public void setMap(Map map) {
   this.map = map;}

    public void setList(List list) {
   this.list = list;}

    public void setForte(Forte forte) {
   this.forte = forte;}
}

1.2.4 @ImportResource注解应用xml配置文件

SpringBoot提倡零配置, 即无xml配置,但是在实际开发中,可能有一些特殊要求必须使用 xml 配置;这时我们可以通过 Spring 提供的 @ImportResource 来应用 xml 配置文件。

比如上述我们需要将一个实体类中的数据全部从配置文件中读取,所以我们除了上述SpringBoot本身就提供的便捷的注解之外,还可以直接在Spring的xml配置文件中配置实体类的属性值,直接在配置文件中配置相当于也实现了从配置文件中读取数据。

@ImportResource的作用是加载Spring的xml配置文件的配置内容并应用到容器中使用:
1.创建 resources/spring01.xml 文件,添以下配置内容:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="emp1" class="cn.korb1n.hellospringboot.bean.Emp"></bean>
</beans>

2.使用 @ImportResource注解标注在一个配置类上,实现将Spring的该配置文件加载应用到容器中,下例是主配置类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@ImportResource(locations = {
   "classpath:spring01.xml"})
@SpringBootApplication
public class HellospringbootApplication {
   

	public static void main(String[] args) {
   
     SpringApplication.run(HellospringbootApplication.class, args);
    }

}

单元测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class HellospringbootApplicationTests {
   

    @Autowired
    private ApplicationContext applicationContext;
    @Test
    public void testXml(){
   
        //没有找到就报错
        System.out.println("empService:"+applicationContext.getBean("emp1"));
    }

}

@ImportResource注解实际提供的是一种向容器中注入组件的方式,同理,可以通过其他向容器中注入组件的方式来实现对实体类数据的解耦,如@@Configuration@Bean注解搭配等。

2.读取配置文件到静态变量

在使用阿里云的短信服务时,accessKeyId和accessSecret等参数不能在代码中写死,需要解耦,并且可能OSS对象存储也需要用到这个密钥,所以想到的方案是将这些参数放到一个静态变量中,供全局调用,然后静态变量中的值来源于配置文件。所以出现了需要读取配置文件到静态变量的需求。

2.1 在方法上添加@Value注解

上述描述的读取配置文件到实体类中@Value注解是添加在成员变量上,但对于静态成员变量就不行了,会是一个空值。但可以将其添加到set方法上,实现对静态变量的注入。另外,依旧是得在类上添加@Component注解,要注入就要放入容器嘛~

AliyunParameter.properties配置文件:

#阿里云短信服务的参数数据
aliyun.accessKeyId=xxxxxxxxxxxx
aliyun.accessSecret=yyyyyyyyyyyy
aliyun.smsSignName=科彬个人社区
aliyun.smsRegionId=cn-hangzhou
aliyun.smsTemplateCode.REGISTER_CODE=SMS_1111111111
aliyun.smsTemplateCode.VERIFY_CODE=SMS_222222222

含静态变量的实体类:

import java.util.Map;

@PropertySource(value = {
   "classpath:AliyunParameter.properties"})
@Component
public class AliyunParameter {
   
    public static String accessKeyId;
    public static String accessSecret;
    public static String smsRegionId;
    public static String smsSignName;
    public static Map<String,String> smsTemplateCode;

	@Value("${aliyun.accessKeyId}")
    public void setAccessKeyId(String accessKeyId){
   
        AliyunParameter.accessKeyId = accessKeyId;
    }

}

这种读取配置文件到静态变量的方式依旧存在@Value注解的局限性,只能注入单个变量,对于集合List、Map这类数据不能注入。

2.2 使用@PostConstruct注解在初始化Servlet之前初始化静态变量

@PostConstruct注解修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct修饰的方法在其所在类的构造函数之后执行,Servlet的 init() 方法之前执行。通常我们在Spring框架中使用到@PostConstruct注解的方法在整个Bean初始化中的执行顺序为:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

创建一个配置类AliyunConfig,该配置类通过上述的@ConfigurationProperties注解将配置文件中的值注入到实体类中,然后再通过@PostConstruct注解对上述AliyunParameter类中的静态变量进行初始化:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;

@PropertySource(value = {
   "classpath:AliyunParameter.properties"})
@Component
@ConfigurationProperties(prefix = "aliyun")
public class AliyunConfig {
   
    private String accessKeyId;
    private String accessSecret;
    private String smsRegionId;
    private String smsSignName;
    private Map<String,String> smsTemplateCode;

    @PostConstruct
    public void aliyunParameterInit(){
   
        AliyunParameter.setParameter(this);
    }

    public String getAccessKeyId() {
   
        return accessKeyId;
    }

    public void setAccessKeyId(String accessKeyId) {
   
        this.accessKeyId = accessKeyId;
    }

    public String getAccessSecret() {
   
        return accessSecret;
    }

    public void setAccessSecret(String accessSecret) {
   
        this.accessSecret = accessSecret;
    }

    public String getSmsRegionId() {
   
        return smsRegionId;
    }

    public void setSmsRegionId(String smsRegionId) {
   
        this.smsRegionId = smsRegionId;
    }

    public String getSmsSignName() {
   
        return smsSignName;
    }

    public void setSmsSignName(String smsSignName) {
   
        this.smsSignName = smsSignName;
    }

    public Map<String, String> getSmsTemplateCode() {
   
        return smsTemplateCode;
    }

    public void setSmsTemplateCode(Map<String, String> smsTemplateCode) {
   
        this.smsTemplateCode = smsTemplateCode;
    }
}

调整包含静态变量的 AliyunParameter 类,提供一个set方法对静态变量进行赋值:

import java.util.Map;

public class AliyunParameter {
   
    public static String accessKeyId;
    public static String accessSecret;
    public static String smsRegionId;
    public static String smsSignName;
    public static Map<String,String> smsTemplateCode;

    public static void setParameter(AliyunConfig aliyunConfig) {
   
        AliyunParameter.accessKeyId = aliyunConfig.getAccessKeyId();
        AliyunParameter.accessSecret = aliyunConfig.getAccessSecret();
        AliyunParameter.smsRegionId = aliyunConfig.getSmsRegionId();
        AliyunParameter.smsSignName = aliyunConfig.getSmsSignName();
        AliyunParameter.smsTemplateCode = aliyunConfig.getSmsTemplateCode();
    }

    public static void print() {
   
        System.out.println("SmsParameter{" +
                "regionId='" + smsRegionId + '\'' +
                ", accessKeyId='" + accessKeyId + '\'' +
                ", accessSecret='" + accessSecret + '\'' +
                ", signName='" + smsSignName + '\'' +
                ", templateCode=" + smsTemplateCode +
                '}');
    }
}
本文来源MrKorbin,由架构君转载发布,观点不代表Java架构师必看的立场,转载请标明来源出处:https://javajgs.com/archives/25269

发表评论