(一)、SpringBoot 读取配置文件(精讲)

本文详细介绍了SpringBoot中配置文件的多种读取方式,包括@ConfigurationProperties、@Value、@PropertySource等注解的使用。还提到了松散绑定、参数校验、配置文件占位符、属性转换等功能,以及自定义解析YAML文件的方法。同时强调了在自定义配置文件中避免属性名与主配置文件冲突的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

💡 **注意:**在需要读取配置文件时,SpringBoot 推荐使用注解处理器。该处理器主要功能是:在配置文件里写属性的时候会自动匹配相关的实体类的属性加以提示。
加上后写配置文件的时候会有提 示。添加方式为:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId>
	<optional>true</optional>
</dependency>

读取配置文件的几种方式

@ConfigurationProperties 读取主配置文件

该注解可以写在类、方法上。有一点要注意:只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties 功能。

  • 示例一:直接写在类上,读取主配置文件里的属性
    Student

    @Component
    @Data
    @ConfigurationProperties(prefix = "student")
    public class Student {
        private long id;
        private String name;
        private int age;
        private boolean gender;
        private Map<String, String> scoreMap;
        private List<String> hobbies;
    }
    

    主配置文件 application.yml。其实也可以使用 application.properties ,效果是一样的。

    student:
      id: 9527
      name: ikangjia
      age: 17
      hobbies:
        - pingpang
        - basketball
        - lol
        - cf
      scoreMap: {math: 149, English: 139}
      gender: true
    

    注意: 其中的 @Component 是有意义的,如果删掉会报错,如下图
    请添加图片描述

    由上图可以看出:@ConfigurationProperties 注解不能单独使用。正如提示中显示,它至少需要:

    • 通过 @EnableConfigrationProperties 注册:暂时不知何用,故略;
    • 标记为 Spring 组件:简单粗暴上注解(@Component 或其他类似效果的均可);
    • 通过 @ConfigurationPropertiesScan 扫描:可以放在主类上,需要设置参数指定类或包名。
      三者任一种方式来使其生效。
  • 示例二:将其写在公共方法上。当想要将属性绑定到第三方组件时,这种做法很有用
    还是上文的 application.yml 文件
    配置类

    @Configuration(proxyBeanMethods = false)
    public class StudentConfig {
    
        @Bean
        @ConfigurationProperties(prefix = "student")
        public Student getStudent() {
            return new Student();
        }
    }
    

@Value 读取主配置文件

@Value 一般用于注入单个或几个属性的时候使用,它支持 SPEL 表达式,很强大。它默认只能读取主配置文件里的自定义属性。
@Value 注解一般写在类属性上,一个属性对应一个 @Value 注解,所以一般在单个或很少几个属性值注入的时候才会使用该注解。

  • 使用示例:
    主配置文件 application.yml

    student:
      id: 9527
      name: ikangjia
    
    @Data
    @Component
    public class Student2 {
        @Value("${student.id}")
        private long id;
    
        @Value("${student.name}")
        private String name;
    }
    

@PropertySource 指定资源文件位置

@PropertySource 注解用于指定资源文件读取的位置,它不仅能读取 properties 文件,也能读取 xml文件,并且通过 YAML 解析器,配合自定义 PropertySourceFactory 实现解析 YAML 文件。

要注意:使用自定义配置文件时,在自定义配置文件里的属性名不要与主配置里的属性名重复,否则会被主属性里的值给覆盖。即在同名的情况下:主属性里的属性值优先级高。

@ProperSource + Environment 读取任意位置的自定义配置文件

@ProperSource 与 @Configuration 注解联合使用,可以很方便的为程序注入属性

@Configuration
@PropertySource("classpath:config/student.properties")
public class AppConfig {

    @Autowired
    Environment env;
    
    @Bean
    public Student student() {
        Student student = new Student();
        student.setName(env.getProperty("student.name"));
        return student;
    }
}

@Data
public class Student {
    private long id;
    private String name;
}
// config/student.yml
student.id=95281
student.name=ikangjia

@PropertySource + @ConfigurationProperties 读取自定义配置文件

属性文件 resource/config/student.properties

student.id=9528
student.name=ikangjia
student.age=17
student.gender=true

Student 类

@Component
@PropertySource(value = {"classpath:config/student.properties"}, encoding = "utf-8")
@Data
@ConfigurationProperties(prefix = "student")
public class Student3 {
    private long id;
    private String name;
    private int age;
    private boolean gender;
}

@PropertySource + @Value 读取自定义配置文件

属性文件 resource/config/student.properties

student.id=9528
student.name=ikangjia

Student 类

@Component
@PropertySource(value = {"classpath:config/student.properties"}, encoding = "utf-8")
@Data
public class Student3 {
    @Value("${student.id}")
    private long id;

    @Value("${student.name}")
    private String name;
}

要注意:使用自定义配置文件时,在自定义配置文件里的属性名不要与主配置里的属性名重复,否则会被主属性里的值给覆盖。即在同名的情况下:主属性里的属性值优先级高。

@ImportSource 读取 Spring 配置文件

Spring Boot 里面没有 Spring 的配置文件,我们自己编写的配置文件,也不能自动识别;@ImportSource 注解的作用就是导入 Spring 的配置文件,让 Spring 的配置文件生效,加载进来。

  • (不推荐)使用示例:@ImportResource 标注在一个配置类上

    // 导入Spring的配置文件让其生效
    @ImportResource(locations = {"classpath:beans.xml"})
    public class XxxConfig {
    
    }
    

    编写 Spring 的配置文件 beans.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="student" class="cn.ikangjia.entity.Student"></bean>
    </beans>
    
  • (推荐)使用示例:全注解的方式为容器添加组件
    注意:在 SpringBoot 中,其实完全没必要按照上述示例给容器添加组件。SpringBoot 中推荐使用全注解的方式,即:

    1. 配置类 @Configuration:表明这是个 Spring 配置文件;
    2. 方法上添加注解 @Bean :给容器添加组件。
    /**
     * @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件
     * 在配置文件中用<bean><bean/>标签添加组件
     */
    @Configuration
    public class MyAppConfig {
    
        //将方法的返回值添加到容器中;容器中这个组件默认的 id 就是方法名
        @Bean
        public HelloService helloService02(){
            System.out.println("配置类 @Bean 给容器中添加组件了...");
            return new HelloService();
        }
    }
    

一些使用技巧

松散绑定

Spring Boot 使用一些放松的规则将属性绑定到 bean,因此不需要在属性名和 bean 属性名之间进行精确匹配。
Spring Boot 使用一些放松的规则将 Environment 属性绑定到 @ConfigurationProperties bean,因此不需要在 Environment 属性名和 bean 属性名之间完全匹配。有用的常见例子包括破折号分隔的环境属性(例如,上下文路径绑定到 contextPath)和大写的环境属性(例如,PORT 绑定到端口)。
作为一个例子,考虑以下 @ConfigurationProperties 类:

@ConfigurationProperties(prefix = "my.main-project.person")
@Data
public class MyPersonProperties {
    private String firstName;
}

在前面的代码中,可以使用以下属性名:

示例注意
My.main-project. person.first-name烤串的情况下,这是推荐使用的 .properties 和 .yml 文件。
My.main-project. person.firstName标准驼峰式语法。
My.main-project. person.first _ name下划线表示法,这是在 .properties 和 .yml 文件中使用的一种替代格式。
My.mainproject person firstname大写格式,在使用系统环境变量时推荐使用这种格式。

SpringBoot 建议使用烤肉串的形式,即 My.main-project. person.first-name

参数校验

@ConfigurationProperties 标注的类是支持参数校验的。@Value 不支持。
当使用 Spring 的 @Validated 注释对 @ConfigurationProperties 类进行注释时,Spring Boot 都会尝试对它们进行验证。您可以直接在配置类上使用 JSR-303 javax.validationconstraint 注释。为此,请确保您的类路径上有兼容的 jsr-303 实现,然后向字段添加约束注释,如下:

@ConfigurationProperties("my.service")
@Validated
public class MyProperties {

    @NotNull
    private InetAddress remoteAddress;

    @Valid
    private final Security security = new Security();

    // getters/setters...
    public static class Security {

        @NotEmpty
        private String username;
        // getters/setters...
    }
}

配置文件占位符

# 随机数
# 可以生成整数、长、 uuids 或字符串
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number-less-than-ten=${random.int(10)}
my.number-in-range=${random.int[1024,65536]}

# 占位符获取之前配置的值,如果没有可以是用:指定默认值
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}

属性转换

Spring Boot 试图在外部应用程序属性绑定到 @ConfigurationProperties bean 时将其强制转换为正确的类型。如果需要自定义类型转换,可以提供 ConversionService bean (使用名为 ConversionService 的 bean)或自定义属性编辑器(通过 customerconfigurebean )或自定义转换器(使用注释为 @ConfigurationProperties Binding 的 bean 定义)。

转换持续时间

Spring Boot 专门支持持续时间的表达。如果您暴露了一个 java.time。属性,则应用程序属性中的下列格式可用:

  • 常规的长表示(除非指定了@DurationUnit,否则使用毫秒作为默认单位)
  • Java.time.Duration 使用的标准 iso-8601格式
  • 一种更易读的格式,其中的值和单位是耦合的(10秒表示10秒)

考虑下面的例子:

@ConfigurationProperties("my")
public class MyProperties {

    @DurationUnit(ChronoUnit.SECONDS)
    private Duration sessionTimeout = Duration.ofSeconds(30);

    private Duration readTimeout = Duration.ofMillis(1000);

    // getters / setters...
}

要指定30秒的会话超时,30秒、 PT30S 和30秒都是等效的。读取超时时间为500毫秒,可以以下列任何形式指定: 500、 PT0.5 s 和500毫秒。
你也可以使用任何受支援的单位,包括:

  • 毫微秒
  • 只需要几微秒
  • 毫秒代表毫秒
  • 再来一次
  • 分钟
  • 好几个小时
  • 连续几天

默认单位是毫秒,可以使用 @DurationUnit 重写,如上面的示例所示。
如果您喜欢使用构造函数绑定,可以公开相同的属性,如下面的例子所示:

@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {

    // fields...

    public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout, @DefaultValue("1000ms") Duration readTimeout) {
        this.sessionTimeout = sessionTimeout;
        this.readTimeout = readTimeout;
    }
    // getters...
}

转换周期

除了持续时间,Spring Boot 还可以与 java.time 一起工作。期间类型。以下格式可用于应用程序属性:

  • 常规的 int 表示形式(使用天作为默认单位,除非指定了 @PeriodUnit)
  • Java.time 使用的标准 iso-8601格式
  • 一种更简单的格式,其中值和单位对是耦合的(1y3d 表示1年和3天)

下列单元以简单格式支持:

  • 很多年了
  • 几个月
  • 已经好几周了
  • 连续几天

转换数据大小

Spring framework 具有一个 DataSize 值类型,该类型以字节表示大小。如果公开 DataSize 属性,应用程序属性中的下列格式可用:

  • 常规长表示形式(除非指定了 @DataSizeUnit,否则使用字节作为默认单元)
  • 一种更易读的格式,其中的值和单位是耦合的(10mb 表示10mb)
    考虑下面的例子:
@ConfigurationProperties("my")
public class MyProperties {

    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize bufferSize = DataSize.ofMegabytes(2);

    private DataSize sizeThreshold = DataSize.ofBytes(512);

    // getters/setters...
}

若要指定 10mb 的缓冲区大小,10 和 10mb 是等效的。可以将256字节的大小阈值指定为256或256b。
你也可以使用任何受支援的单位,包括:

  • B 表示字节
  • KB 表示千字节
  • MB 表示兆字节
  • GB 的 g 字节
  • TB 为 TB
    默认单元是字节,可以使用@DataSizeUnit 重写,如上面的示例所示。
    如果您喜欢使用构造函数绑定,可以公开相同的属性,如下面的例子所示:
@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {
    // fields...

    public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize, @DefaultValue("512B") DataSize sizeThreshold) {
        this.bufferSize = bufferSize;
        this.sizeThreshold = sizeThreshold;
    }
    // getters...
}

@PropertySource 高级用法

源码阅读

//只能作用在类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {

	/**
	 * 指定资源名称,如果为空,就根据基础资源的描述生成。
	 */
	String name() default "";

	/**
	 * 指定资源路径。
	 * 可以是 classpath:/xxx/xxxx
	 * 也可以是 file:/xxx/xxx/xx
	 */
	String[] value();

	/**
	 * 是否忽略资源不存在的情况,如果不忽略,当资源不存在时就报错。默认不忽略。
	 * 此属性时spring4.0以后出现的。
	 */
	boolean ignoreResourceNotFound() default false;

	/**
	 * 指定资源文件的编码格式。如果不指定就使用文件默认的。
	 * 此注解是spring4.3以后出现的。
	 */
	String encoding() default "";

	/**
	 * 指定资源工厂,如果不指定,就使用默认的资源工厂。
	 */
	Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}

自定义解析 YAML 文件

PropertySourceFactory 的默认实现 DefaultPropertySourceFactory 是解析不了 yaml 文件的,如果要解析,就要自定义实现。
我们就不自己解析 Yaml,直接引用第三方 jar 包进行解析。

 <dependency>
     <groupId>org.yaml</groupId>
     <artifactId>snakeyaml</artifactId>
     <version>1.23</version>
</dependency>

代码

/**
 * 自定义Yaml解析工厂
 */
public class YAMLPropertySourceFactory implements PropertySourceFactory {
    @Override
    public org.springframework.core.env.PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
        //创建一个YAML解析工厂。
        YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
        //设置资源。
        factory.setResources(encodedResource.getResource());

        //获取解析后的Properties对象
        Properties properties = factory.getObject();
        //返回。此时不能像默认工厂那样返回ResourcePropertySource对象 ,要返回他的父类PropertiesPropertySource对象。
        return name != null ? new PropertiesPropertySource(name, properties) :
                new PropertiesPropertySource(encodedResource.getResource().getFilename(),properties);
    }
}

//配置类:
@Configuration
@ComponentScan(basePackages = "propertysourcedemo")
//使用自定义工厂。
@PropertySource(value = "classpath:dbconfig/datasource-config.yaml",factory = YAMLPropertySourceFactory.class)
public class SpringConfig {

    //通过SPEL表达式注入属性
    @Value("${druid.driverClassName}")
    private String driverClassName;

    @Value("${druid.url}")
    private String url;

    @Value("${druid.username}")
    private String username;

    @Value("${druid.password}")
    private String password;

    //注册Druid数据源连接池
    @Bean
    public DruidDataSource druidDataSource(){
        System.out.println("driverClassName====> " + driverClassName);
        System.out.println("url====> " + url);
        System.out.println("username====> " + username);
        System.out.println("password====> " + password);
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
}

参考文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值