@PropertySource 注解提供了一种方便的声明机制,用于将 PropertySource 添加到 Spring 的环境中,该注解可与 @Configuration 类一起使用。
@PropertySource 注解定义如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Repeatable(PropertySources.class) public @interface PropertySource { String name() default ""; String[] value(); boolean ignoreResourceNotFound() default false; String encoding() default ""; Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class; }
其中:
value 指示要加载的属性文件(properties)的资源位置
name 指明此属性源(PropertySource)的名称
ignoreResourceNotFound 指示是否应忽略未能找到属性资源
encoding 指定解析资源的字符编码,例如:UTF-8
factory 指定自定义 PropertySourceFactory
在项目的 resources 目录下面创建一个 app.properties 文件,文件内容如下:
beanName=TestBean
然后,在 @Configuration 配置类中使用 @PropertySource 注解将 app.properties 文件添加到 Spring 的 Environment 环境中。代码如下:
// 实体 public class TestBean { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } // 添加配置类 @Configuration @PropertySource("classpath:my.properties") public class AppConfig { @Autowired private Environment env; @Bean public TestBean testBean() { TestBean testBean = new TestBean(); testBean.setName(env.getProperty("beanName")); return testBean; } }
注意,Environment 对象是 @Autowired 到配置类中,然后在填充 TestBean 对象时使用。鉴于上述配置,执行 testBean.getName() 的将返回“TestBean”。
这里将使用 @SpringBootTest 注解创建一个简单的测试类,代码如下:
@SpringBootTest class AppConfigTest { @Autowired private TestBean testBean; @Test void contextLoads() { String name = testBean.getName(); System.out.println("name=" + name); } }
执行上面测试类,将输出如下信息:
name=TestBean
为了使用 PropertySource 中的属性去解析 <bean> 定义或 @Value 注释中的 ${...} 占位符,您必须确保在 ApplicationContext 使用的 BeanFactory 中注册了适当的嵌入值解析器。
在 XML 中使用 <context:property-placeholder> 时会自动配置嵌入值解析器。当使用 @Configuration 类时,这可以通过静态 @Bean 方法显式注册 PropertySourcesPlaceholderConfigurer 来实现。但是请注意,通过静态 @Bean 方法显式注册 PropertySourcesPlaceholderConfigurer 通常仅在您需要自定义配置时才需要(例如占位符语法等)。
@PropertySource 资源位置中存在的任何 ${...} 占位符都将使用已经注册到环境中的属性集进行解析(包括环境变量等)。例如:
(1)在用户的主目录下面创建一个 my.properties 文件,文件内容如下:
beanName=TestBean(User Home)
(2)在 @PropertySource 注解中,通过 ${user.home} 引用用户主目录路径,使用 file 方式去解析用户主目录下的 my.properties 属性文件。代码如下:
@Configuration @PropertySource("file:${user.home}/my.properties") public class AppConfig { @Autowired private Environment env; @Bean public TestBean testBean() { TestBean testBean = new TestBean(); testBean.setName(env.getProperty("beanName")); return testBean; } }
注意,我们可以将上面的 ${user.home} 中的 user.home 替换成属性文件中的属性配置。如果指定的属性不存在,则可以指定默认值(default_value),默认值由冒号“:”分隔,默认值是可选的,如果未指定默认值且无法解析属性,则会抛出 IllegalArgumentException 异常。如下:
@PropertySource("file:${user.home:default_value}/my.properties")
如果给定的属性键存在于多个 .properties 属性文件中,则处理的最后一个 @PropertySource 注解将“获胜”并覆盖任何先前具有相同名称的键。
例如:给定两个属性文件 a.properties 和 b.properties,考虑以下两个使用 @PropertySource 注解引用它们的配置类:
// 配置类1 @Configuration @PropertySource("classpath:/com/myco/a.properties") public class ConfigA { } // 配置类2 @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 是最后注册的。
在某些情况下,在使用 @PropertySource 注释时严格控制属性源排序可能是不切实际的。例如,如果上面的 @Configuration 类是通过组件扫描注册的,则很难预测其顺序。在这种情况下,如果重写很重要,建议用户回退到使用编程式 PropertySource API 实现。
注意:根据 Java 8 约定,此注释是可重复的。但是,所有此类 @PropertySource 注释都需要在同一级别声明:直接在配置类上或作为同一自定义注释上的元注释。不建议混合直接注释和元注释,因为直接注释将有效地覆盖元注释。