在Spring中从属性文件中读取值

时间:2020-01-09 10:44:29  来源:igfitidea点击:

在本文中,我们将介绍如何在Spring中将配置外部化为属性文件,以及如何在XML中使用XML配置和@PropertySource注释从属性文件中读取值。

最佳实践是将应用程序特定的设置放入单独的属性文件中,而不是在配置中对其进行硬编码。例如,可以将与数据库相关的配置属性(例如DB Url,Driver类,用户,密码)存储在属性文件中,然后从该文件中读取或者在用于发送电子邮件的应用程序中读取SMTP设置(例如主机,用户,密码)可以存储在属性文件中。

Spring中使用XML配置的属性文件

我们可以使用XML中的<context:property-placeholder>配置属性占位符。将要替换的值指定为$ {property-name}形式的占位符。在运行时,将PropertySourcesPlaceholderConfigurer应用于元数据,它检查属性文件中的占位符,并替换与属性文件中的键匹配的占位符值。

请注意,在使用从Spring 5.2开始弃用的org.springframework.beans.factory.config.PropertyPlaceholderConfigurer类之前,要从Spring 5.2版开始使用org.springframework.context.support.PropertySourcesPlaceholderConfigurer。

使用<context:property-placeholder>元素时,将自动注册PropertySourcesPlaceholderConfigurer。

例如,有一个app.properties文件保存在/ src / main / resources /位置,因此它位于类路径上。

db.driverClassName=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/theitroad
db.username=user
db.password=password

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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
        
  <!--  For properties files --> 
  <context:property-placeholder location="classpath:app.properties" />
    
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  
    <property name="dataSource" ref="dataSource"></property>  
  </bean>  
  <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
    <property name="driverClassName" value = "${db.driverClassName}" />
    <property name="url" value = "${db.url}" />
    <property name="username" value = "${db.username}" />
    <property name="password" value = "${db.password}" />
  </bean>
</beans>

如我们所见,通过指定属性文件的位置,使用<context:property-placeholder>元素注册了属性文件。

使用XML指定多个属性文件

我们可以将多个属性文件作为逗号分隔的值传递到location属性中

<context:property-placeholder location="classpath:db.properties, classpath:mail.properties" />

使用@PropertySource注释的Spring中的属性文件

还有一个@PropertySource注释,它为将PropertySource添加到Spring的环境中提供了一种方便的声明性机制。

我们可以将@PropertySource注释与@Value注释一起使用,以注入从属性文件读取的值,但是更好的方法是使用Spring的环境。

@PropertySource和@Value注入值

@Configuration
@ComponentScan(basePackages = "com.theitroad.springproject")
@PropertySource("classpath:app.properties")
public class AppConfig {
  @Value("${db.driverClassName}")
  private String driverClassName;
  @Value("${db.url}")
  private String url;
  @Value("${db.username}")
  private String userName;
  @Value("${db.password}")
  private String pwd;
	
  @Bean
  public DataSource dataSource() {
    DriverManagerDataSource ds = new DriverManagerDataSource();
    ds.setDriverClassName(driverClassName);
    ds.setUrl(url);
    ds.setUsername(userName);
    ds.setPassword(pwd);
    return ds;
  }
}

请注意,PropertySourcesPlaceholderConfigurer类用于根据当前的Spring Environment解析@Value注释中的$ {…}占位符。

使用以下类运行示例

public class App {
  public static void main( String[] args ){
    //EntityManager
    AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    DriverManagerDataSource dataSource =  context.getBean("dataSource", DriverManagerDataSource.class);
    System.out.println("Driver class name- " + dataSource.getUsername());
    context.close();
  }
}

输出:

Driver class name- user

@PropertySource与环境一起读取属性值

@PropertySource注释为Spring的环境添加了一个PropertySource,因此使用Environment的getProperty()方法是一种读取属性的便捷方法,而不是使用@Value注释。

@Configuration
@ComponentScan(basePackages = "com.theitroad.springproject")
@PropertySource("classpath:app.properties")
public class AppConfig {
  @Autowired
  private Environment env;
	
  @Bean
  public DataSource dataSource() {
    DriverManagerDataSource ds = new DriverManagerDataSource();
    ds.setDriverClassName(env.getProperty("db.driverClassName"));
    ds.setUrl(env.getProperty("db.url"));
    ds.setUsername(env.getProperty("db.username"));
    ds.setPassword(env.getProperty("db.password"));
    return ds;
  }
}

注意如何注入org.springframework.core.env.Environment,并且该对象用于获取属性。

Spring @PropertySource功能

  • @PropertySource资源位置中存在的任何$ {...}占位符都是根据已经针对环境注册的一组属性源来解析的。例如
@Configuration
@PropertySource("classpath:${app.path:default/path}/app.properties")
public class AppConfig {
    ....
    ....
}

如果app.path存在于已注册的属性源之一(例如,系统属性或者环境变量)中,则占位符将解析为相应的值。如果不是,则将default / path用作默认值。如果未指定默认值且无法解析属性,则抛出IllegalArgumentException。设置为系统属性-System.setProperty(" app.path"," config");

  • 根据Java 8约定,@PropertySource注释是可重复的。所有此类@PropertySource注释都需要在同一级别声明。
@Configuration
@ComponentScan(basePackages = "com.theitroad.springproject")
@PropertySource("classpath:app.properties")
@PropertySource("classpath:global.properties")
public class AppConfig {
	..
	..
}

我们还可以使用@PropertySources容器注释来聚合几个PropertySource注释。

@Configuration
@PropertySources({
	@PropertySource("classpath:properties/db.properties"),
	@PropertySource("classpath:properties/mail.properties")
})
public class Configurations {
  ...
  ...

}

如果找不到值,则忽略异常

如果找不到属性文件或者属性文件中不存在传递的密钥,则默认情况下,Spring框架会引发异常。如果我们拥有一个可能存在或者可能不存在的属性文件,并且不希望引发不存在的异常,则可以将ignoreResourceNotFound属性指定为true。

带有@PropertySource注释

@Configuration
@PropertySource(value="classpath:properties/db.properties", ignoreResourceNotFound=true)
public class DBConfiguration {  

}

在XML配置中

<context:property-placeholder location="classpath:config/db.properties" ignore-resource-not-found="false" />

使用XML配置时,如果无法解析占位符,我们还可以指定ignore-unresolvable属性来忽略异常。

<context:property-placeholder location="classpath:config/db.properties" ignore-resource-not-found="false" ignore-unresolvable="false" />

用@PropertySource覆盖属性

如果给定的属性键存在于多个.properties文件中,则最后处理的@PropertySource注释将覆盖此类重复键的值。

例如,给定两个属性文件a.properties和b.properties,请考虑以下两个配置类,它们使用@PropertySource注释来引用它们:

@Configuration
 @PropertySource("classpath:/com/myco/a.properties")
 public class ConfigA { }

 @Configuration
 @PropertySource("classpath:/com/myco/b.properties")
 public class ConfigB { }

覆盖顺序取决于在应用程序上下文中注册这些类的顺序。

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
 ctx.register(ConfigA.class);
 ctx.register(ConfigB.class);
 ctx.refresh();

在上述情况下,b.properties中的属性将覆盖a.properties中存在的所有重复项,因为ConfigB是最后注册的。