早期, 如果需要通过 spring 读取 properties 文件中的配置信息, 都需要在 XML 文件中配置文件读取方式
基于 XML 的读取方式:
- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="locations">
- <list>
- <value>classpath:properties/thread-pool.properties</value>
- </list>
- </property>
- </bean>
当然, 这种方式可以统一管理 properties 配置文件, 也能实现代码的松耦合但为了方便开发, 提高开发效率, spring 官方后来提供了基于注解的配置读取方式两种方式各有优势, 可以基于对项目的考虑选择最合适的方式接下来就介绍如何通过注解注入 properties 的配置信息
首先, 准备配置文件:
- core.pool.size=2
- max.pool.size=3
- keep.alive.time=1
- task.queue.size=3
- await.termination.time=5
定义配置类:
- package org.cellphone.config;
- import com.google.gson.Gson;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.PropertySource;
- import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
- import org.springframework.stereotype.Component;
- /**
- *
- */
- @Component
- @PropertySource("classpath:properties/thread-pool.properties")
- public class ThreadPoolConfig {
- /**
- * 核心线程个数
- */
- @Value("${core.pool.size}")
- private int corePoolSize;
- /**
- * 最大线程个数
- */
- @Value("${max.pool.size}")
- private int maxPoolSize;
- /**
- * 保持心跳时间
- */
- @Value("${keep.alive.time}")
- private int keeAliveTime;
- /**
- * 任务队列长度
- */
- @Value("${task.queue.size}")
- private int taskQueueSize;
- /**
- * 等待任务结束的时间
- */
- @Value("${await.termination.time}")
- private int awaitTerminationTime;
- /**
- * 使用 @value 注解注入 properties 中的属性
- * 1. 在类名上面使用 @PropertySource("classpath:*") 注解,* 代表属性文件路径, 可以指向多个配置文件路径
- * 如果是多个配置文件, 则是 @PropertySource({"classpath:*","classpath:*"....})
- * 2. 在字段上直接使用 @value 注解
- * 3. 注解内使用 ${core.pool.size} core.pool.size 代表属性文件里面的 key
- * 5. 需要新增 PropertySourcesPlaceholderConfigurer 的 bean
- * 6. 在 PropertySourcesPlaceholderConfigurer 增加 @bean 注解, 申明返回的是一个 bean, 否则会注入失败
- *
- */
- @Bean
- public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
- return new PropertySourcesPlaceholderConfigurer();
- }
- public int getCorePoolSize() {
- return corePoolSize;
- }
- public void setCorePoolSize(int corePoolSize) {
- this.corePoolSize = corePoolSize;
- }
- public int getMaxPoolSize() {
- return maxPoolSize;
- }
- public void setMaxPoolSize(int maxPoolSize) {
- this.maxPoolSize = maxPoolSize;
- }
- public int getKeeAliveTime() {
- return keeAliveTime;
- }
- public void setKeeAliveTime(int keeAliveTime) {
- this.keeAliveTime = keeAliveTime;
- }
- public int getTaskQueueSize() {
- return taskQueueSize;
- }
- public void setTaskQueueSize(int taskQueueSize) {
- this.taskQueueSize = taskQueueSize;
- }
- public int getAwaitTerminationTime() {
- return awaitTerminationTime;
- }
- public void setAwaitTerminationTime(int awaitTerminationTime) {
- this.awaitTerminationTime = awaitTerminationTime;
- }
- @Override
- public String toString() {
- return new Gson().toJson(this);
- }
- }
这里注入了一个 PropertySourcesPlaceholderConfigurer bean,spring 是通过 PropertySourcesPlaceholderConfigurer 的 locations 来查找属性文件, 然后再根据注解将匹配的属性 set 进去, 下面通过源码来了解注解可以进行一些什么操作
- public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
- /**
- * {@value} is the name given to the {@link PropertySource} for the set of
- * {@linkplain #mergeProperties() merged properties} supplied to this configurer.
- */
- public static final String LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME = "localProperties";
- /**
- * {@value} is the name given to the {@link PropertySource} that wraps the
- * {@linkplain #setEnvironment environment} supplied to this configurer.
- */
- public static final String ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME = "environmentProperties";
- @Nullable
- private MutablePropertySources propertySources;
- @Nullable
- private PropertySources appliedPropertySources;
- @Nullable
- private Environment environment;
下面代码省略
上面源码并没能说明为什么一定要返回这个 bean, 接下来看父类 PlaceholderConfigurerSupport 的源码:
- /**
- * Abstract base class for property resource configurers that resolve placeholders
- * in bean definition property values. Implementations <em>pull</em> values from a
- * properties file or other {@linkplain org.springframework.core.env.PropertySource
- * property source} into bean definitions.
- *
- * <p>The default placeholder syntax follows the Ant / Log4J / JSP EL style:
- *
- * <pre class="code">${...}</pre>
- *
- * Example XML bean definition:
- *
- * <pre class="code">
- * <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"/>
- * <property name="driverClassName" value="${driver}"/>
- * <property name="url" value="jdbc:${dbname}"/>
- * </bean>
- * </pre>
- *
- * Example properties file:
- *
- * <pre class="code">driver=com.mysql.jdbc.Driver
- * dbname=mysql:mydb</pre>
- *
- * Annotated bean definitions may take advantage of property replacement using
- * the {@link org.springframework.beans.factory.annotation.Value @Value} annotation:
- *
- * <pre class="code">@Value("${person.age}")</pre>
- *
- * Implementations check simple property values, lists, maps, props, and bean names
- * in bean references. Furthermore, placeholder values can also cross-reference
- * other placeholders, like:
- *
- * <pre class="code">rootPath=myrootdir
- * subPath=${rootPath}/subdir</pre>
- *
- * In contrast to {@link PropertyOverrideConfigurer}, subclasses of this type allow
- * filling in of explicit placeholders in bean definitions.
- *
- * <p>If a configurer cannot resolve a placeholder, a {@link BeanDefinitionStoreException}
- * will be thrown. If you want to check against multiple properties files, specify multiple
- * resources via the {@link #setLocations locations} property. You can also define multiple
- * configurers, each with its <em>own</em> placeholder syntax. Use {@link
- * #ignoreUnresolvablePlaceholders} to intentionally suppress throwing an exception if a
- * placeholder cannot be resolved.
- *
- * <p>Default property values can be defined globally for each configurer instance
- * via the {@link #setProperties properties} property, or on a property-by-property basis
- * using the default value separator which is {@code ":"} by default and
- * customizable via {@link #setValueSeparator(String)}.
- *
- * <p>Example XML property with default value:
- *
- * <pre class="code">
- * <property name="url" value="jdbc:${dbname:defaultdb}"/>
- * </pre>
- *
- * @author Chris Beams
- * @author Juergen Hoeller
- * @since 3.1
- * @see PropertyPlaceholderConfigurer
- * @see org.springframework.context.support.PropertySourcesPlaceholderConfigurer
- */
- public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer
- implements BeanNameAware, BeanFactoryAware {
- /** Default placeholder prefix: {@value} */
- public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
- /** Default placeholder suffix: {@value} */
- public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
- /** Default value separator: {@value} */
- public static final String DEFAULT_VALUE_SEPARATOR = ":";
- /** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX} */
- protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
- /** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX} */
- protected String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
- /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR} */
- @Nullable
- protected String valueSeparator = DEFAULT_VALUE_SEPARATOR;
- protected boolean trimValues = false;
- @Nullable
- protected String nullValue;
- protected boolean ignoreUnresolvablePlaceholders = false;
- @Nullable
- private String beanName;
- @Nullable
- private BeanFactory beanFactory;
- 下面代码省略
- 类注释说明了 PlaceholderConfigurerSupport 类所起的作用, 以及替换了 XML 的哪些操作, 其中就描述了注入的 bean 可以利用 @Value 注解进行属性替换:
- * Annotated bean definitions may take advantage of property replacement using
- * the {@link org.springframework.beans.factory.annotation.Value @Value} annotation:
- *
- * <pre class="code">@Value("${person.age}")</pre>
- 属性注释:
- /** Default placeholder prefix: {@value} */
- public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
- /** Default placeholder suffix: {@value} */
- public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
- /** Default value separator: {@value} */
- public static final String DEFAULT_VALUE_SEPARATOR = ":";
- /** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX} */
- protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
- /** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX} */
- protected String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
- /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR} */
- @Nullable
- protected String valueSeparator = DEFAULT_VALUE_SEPARATOR;
- protected boolean trimValues = false;
- @Nullable
- protected String nullValue;
- protected boolean ignoreUnresolvablePlaceholders = false;
- 从上面注解可以发现, 使用的 默认前缀是:${, 而后缀是:}, 默认的分隔符是 :, 但是 set 方法可以替换掉默认的分隔符, 而 ignoreUnresolvablePlaceholders 默认为 false, 表示会开启配置文件不存在, 抛出异常的错误
- 从上面就能看出这个 bean 所起的作用, 就是将 @propertySource 注解的 bean 注入属性的作用, 如果没有该 bean, 则不能解析 ${} 符号
- 接下来执行单元测试
- 配置类代码:
- package org.cellphone.web;
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.Configuration;
- /**
- * 配置类用来替换 xml 配置文件
- */
- @Configuration
- @ComponentScan("org.cellphone.config")
- public class SpringConfig {
- }
- package org.cellphone.web;
- import org.cellphone.config.ThreadPoolConfig;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
- /**
- * 纯注解方式整合 Junit 单元测试框架测试类
- */
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(classes = { SpringConfig.class }) // 需要注意此处, 将加载配置文件的注解换成加载配置类的注解
- public class ThreadPoolConfigTest {
- private final Logger logger = LoggerFactory.getLogger(getClass());
- @Autowired
- private ThreadPoolConfig threadPoolConfig;
- @Test
- public void testThreadPoolConfig() {
- logger.info(threadPoolConfig.toString());
- }
- }
使用 @PropertySource 注解需要注意以下几个地方:
1. 使用注解需要将类申明为一个 bean, 可以使用 @Component 注解;
2. @PropertySource(value = "classpath:properties/config_userbean.properties", ignoreResourceNotFound = true) 表示注入配置文件, 并且忽略配置文件不存在的异常;
3. 必须返回一个 PropertySourcesPlaceholderConfigurer 的 bean, 否则会不能识别 @Value("${core.pool.size}") 注解中的 ${core.pool.size} 指向的 value, 而会注入 ${core.pool.size} 的字符串, 返回 PropertySourcesPlaceholderConfigurer 的方法, 使用 @Bean 注解, 表示返回的是个 bean
在 spring 4.0 以后, spring 增加了 @PropertySources 注解, 可以使用多个 @PropertySource 注解, 如下:
- @PropertySources(
- {
- @PropertySource("classpath:properties/thread-pool.properties"),
- @PropertySource("classpath:properties/mysql.properties")
- }
- )
来源: http://www.bubuko.com/infodetail-2544865.html