zoukankan      html  css  js  c++  java
  • spring注解注入properties配置文件

    早期,如果需要通过spring读取properties文件中的配置信息,都需要在XML文件中配置文件读取方式。

    基于XML的读取方式:

    1 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    2     <property name="locations">
    3        <list>  
    4      <value>classpath:properties/thread-pool.properties</value>
    5   </list>
    6     </property>
    7 </bean>

    当然,这种方式可以统一管理properties配置文件,也能实现代码的松耦合。但为了方便开发,提高开发效率,spring官方后来提供了基于注解的配置读取方式。两种方式各有优势,可以基于对项目的考虑选择最合适的方式。接下来就介绍如何通过注解注入properties的配置信息。

    首先,准备配置文件:

    1 core.pool.size=2
    2 max.pool.size=3
    3 keep.alive.time=1
    4 task.queue.size=3
    5 await.termination.time=5

    定义配置类:

      1 package org.cellphone.config;
      2 
      3 import com.google.gson.Gson;
      4 import org.springframework.beans.factory.annotation.Value;
      5 import org.springframework.context.annotation.Bean;
      6 import org.springframework.context.annotation.PropertySource;
      7 import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
      8 import org.springframework.stereotype.Component;
      9 
     10 /**
     11  * 
     12  */
     13 @Component
     14 @PropertySource("classpath:properties/thread-pool.properties")
     15 public class ThreadPoolConfig {
     16     /**
     17      * 核心线程个数
     18      */
     19     @Value("${core.pool.size}")
     20     private int corePoolSize;
     21     /**
     22      * 最大线程个数
     23      */
     24     @Value("${max.pool.size}")
     25     private int maxPoolSize;
     26     /**
     27      * 保持心跳时间
     28      */
     29     @Value("${keep.alive.time}")
     30     private int keeAliveTime;
     31     /**
     32      * 任务队列长度
     33      */
     34     @Value("${task.queue.size}")
     35     private int taskQueueSize;
     36     /**
     37      * 等待任务结束的时间
     38      */
     39     @Value("${await.termination.time}")
     40     private int awaitTerminationTime;
     41 
     42     /**
     43      * 使用@value注解注入properties中的属性
     44      * 1. 在类名上面使用  @PropertySource("classpath:*") 注解,*代表属性文件路径,可以指向多个配置文件路径
     45      *      如果是多个配置文件,则是 @PropertySource({"classpath:*","classpath:*"....})
     46      * 2. 在字段上直接使用@value注解
     47      * 3. 注解内使用${core.pool.size}  core.pool.size 代表属性文件里面的key
     48      * 5. 需要新增 PropertySourcesPlaceholderConfigurer 的 bean
     49      * 6. 在 PropertySourcesPlaceholderConfigurer 增加@bean注解,申明返回的是一个bean,否则会注入失败
     50      *
     51      */
     52 
     53 
     54 
     55     @Bean
     56     public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
     57         return new PropertySourcesPlaceholderConfigurer();
     58     }
     59 
     60     public int getCorePoolSize() {
     61         return corePoolSize;
     62     }
     63 
     64     public void setCorePoolSize(int corePoolSize) {
     65         this.corePoolSize = corePoolSize;
     66     }
     67 
     68     public int getMaxPoolSize() {
     69         return maxPoolSize;
     70     }
     71 
     72     public void setMaxPoolSize(int maxPoolSize) {
     73         this.maxPoolSize = maxPoolSize;
     74     }
     75 
     76     public int getKeeAliveTime() {
     77         return keeAliveTime;
     78     }
     79 
     80     public void setKeeAliveTime(int keeAliveTime) {
     81         this.keeAliveTime = keeAliveTime;
     82     }
     83 
     84     public int getTaskQueueSize() {
     85         return taskQueueSize;
     86     }
     87 
     88     public void setTaskQueueSize(int taskQueueSize) {
     89         this.taskQueueSize = taskQueueSize;
     90     }
     91 
     92     public int getAwaitTerminationTime() {
     93         return awaitTerminationTime;
     94     }
     95 
     96     public void setAwaitTerminationTime(int awaitTerminationTime) {
     97         this.awaitTerminationTime = awaitTerminationTime;
     98     }
     99 
    100     @Override
    101     public String toString() {
    102         return new Gson().toJson(this);
    103     }
    104 }

    这里注入了一个 PropertySourcesPlaceholderConfigurer  bean,spring是通过 PropertySourcesPlaceholderConfigurer 的 locations 来查找属性文件,然后再根据注解将匹配的属性set进去,下面通过源码来了解注解可以进行一些什么操作。

     1 public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
     2 
     3     /**
     4      * {@value} is the name given to the {@link PropertySource} for the set of
     5      * {@linkplain #mergeProperties() merged properties} supplied to this configurer.
     6      */
     7     public static final String LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME = "localProperties";
     8 
     9     /**
    10      * {@value} is the name given to the {@link PropertySource} that wraps the
    11      * {@linkplain #setEnvironment environment} supplied to this configurer.
    12      */
    13     public static final String ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME = "environmentProperties";
    14 
    15 
    16     @Nullable
    17     private MutablePropertySources propertySources;
    18 
    19     @Nullable
    20     private PropertySources appliedPropertySources;
    21 
    22     @Nullable
    23     private Environment environment;

    下面代码省略。。。

    上面源码并没能说明为什么一定要返回这个bean,接下来看父类 PlaceholderConfigurerSupport 的源码:

     1 /**
     2  * Abstract base class for property resource configurers that resolve placeholders
     3  * in bean definition property values. Implementations <em>pull</em> values from a
     4  * properties file or other {@linkplain org.springframework.core.env.PropertySource
     5  * property source} into bean definitions.
     6  *
     7  * <p>The default placeholder syntax follows the Ant / Log4J / JSP EL style:
     8  *
     9  * <pre class="code">${...}</pre>
    10  *
    11  * Example XML bean definition:
    12  *
    13  * <pre class="code">
    14  * <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"/>
    15  *   <property name="driverClassName" value="${driver}"/>
    16  *   <property name="url" value="jdbc:${dbname}"/>
    17  * </bean>
    18  * </pre>
    19  *
    20  * Example properties file:
    21  *
    22  * <pre class="code">driver=com.mysql.jdbc.Driver
    23  * dbname=mysql:mydb</pre>
    24  *
    25  * Annotated bean definitions may take advantage of property replacement using
    26  * the {@link org.springframework.beans.factory.annotation.Value @Value} annotation:
    27  *
    28  * <pre class="code">@Value("${person.age}")</pre>
    29  *
    30  * Implementations check simple property values, lists, maps, props, and bean names
    31  * in bean references. Furthermore, placeholder values can also cross-reference
    32  * other placeholders, like:
    33  *
    34  * <pre class="code">rootPath=myrootdir
    35  * subPath=${rootPath}/subdir</pre>
    36  *
    37  * In contrast to {@link PropertyOverrideConfigurer}, subclasses of this type allow
    38  * filling in of explicit placeholders in bean definitions.
    39  *
    40  * <p>If a configurer cannot resolve a placeholder, a {@link BeanDefinitionStoreException}
    41  * will be thrown. If you want to check against multiple properties files, specify multiple
    42  * resources via the {@link #setLocations locations} property. You can also define multiple
    43  * configurers, each with its <em>own</em> placeholder syntax. Use {@link
    44  * #ignoreUnresolvablePlaceholders} to intentionally suppress throwing an exception if a
    45  * placeholder cannot be resolved.
    46  *
    47  * <p>Default property values can be defined globally for each configurer instance
    48  * via the {@link #setProperties properties} property, or on a property-by-property basis
    49  * using the default value separator which is {@code ":"} by default and
    50  * customizable via {@link #setValueSeparator(String)}.
    51  *
    52  * <p>Example XML property with default value:
    53  *
    54  * <pre class="code">
    55  *   <property name="url" value="jdbc:${dbname:defaultdb}"/>
    56  * </pre>
    57  *
    58  * @author Chris Beams
    59  * @author Juergen Hoeller
    60  * @since 3.1
    61  * @see PropertyPlaceholderConfigurer
    62  * @see org.springframework.context.support.PropertySourcesPlaceholderConfigurer
    63  */
    64 public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer
    65         implements BeanNameAware, BeanFactoryAware {
    66 
    67     /** Default placeholder prefix: {@value} */
    68     public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
    69 
    70     /** Default placeholder suffix: {@value} */
    71     public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
    72 
    73     /** Default value separator: {@value} */
    74     public static final String DEFAULT_VALUE_SEPARATOR = ":";
    75 
    76 
    77     /** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX} */
    78     protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
    79 
    80     /** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX} */
    81     protected String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
    82 
    83     /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR} */
    84     @Nullable
    85     protected String valueSeparator = DEFAULT_VALUE_SEPARATOR;
    86 
    87     protected boolean trimValues = false;
    88 
    89     @Nullable
    90     protected String nullValue;
    91 
    92     protected boolean ignoreUnresolvablePlaceholders = false;
    93 
    94     @Nullable
    95     private String beanName;
    96 
    97     @Nullable
    98     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>

    属性注释:

     1     /** Default placeholder prefix: {@value} */
     2     public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
     3 
     4     /** Default placeholder suffix: {@value} */
     5     public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
     6 
     7     /** Default value separator: {@value} */
     8     public static final String DEFAULT_VALUE_SEPARATOR = ":";
     9 
    10 
    11     /** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX} */
    12     protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
    13 
    14     /** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX} */
    15     protected String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
    16 
    17     /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR} */
    18     @Nullable
    19     protected String valueSeparator = DEFAULT_VALUE_SEPARATOR;
    20 
    21     protected boolean trimValues = false;
    22 
    23     @Nullable
    24     protected String nullValue;
    25 
    26     protected boolean ignoreUnresolvablePlaceholders = false;

    从上面注解可以发现,使用的 默认前缀是:'${',而后缀是:'}',默认的分隔符是 ':',但是set方法可以替换掉默认的分隔符,而  ignoreUnresolvablePlaceholders  默认为 false,表示会开启配置文件不存在,抛出异常的错误。

    从上面就能看出这个bean所起的作用,就是将 @propertySource 注解的bean注入属性的作用,如果没有该bean,则不能解析${}符号。

    接下来执行单元测试。

    配置类代码:

     1 package org.cellphone.web;
     2 
     3 import org.springframework.context.annotation.ComponentScan;
     4 import org.springframework.context.annotation.Configuration;
     5 
     6 /**
     7  * 配置类——用来替换xml配置文件
     8  */
     9 @Configuration
    10 @ComponentScan("org.cellphone.config")
    11 public class SpringConfig {
    12 }
     1 package org.cellphone.web;
     2 
     3 import org.cellphone.config.ThreadPoolConfig;
     4 import org.junit.Test;
     5 import org.junit.runner.RunWith;
     6 import org.slf4j.Logger;
     7 import org.slf4j.LoggerFactory;
     8 import org.springframework.beans.factory.annotation.Autowired;
     9 import org.springframework.test.context.ContextConfiguration;
    10 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    11 
    12 /**
    13  * 纯注解方式整合Junit单元测试框架测试类
    14  */
    15 @RunWith(SpringJUnit4ClassRunner.class)
    16 @ContextConfiguration(classes = { SpringConfig.class }) // 需要注意此处,将加载配置文件的注解换成加载配置类的注解
    17 public class ThreadPoolConfigTest {
    18 
    19     private final Logger logger = LoggerFactory.getLogger(getClass());
    20 
    21     @Autowired
    22     private ThreadPoolConfig threadPoolConfig;
    23 
    24     @Test
    25     public void testThreadPoolConfig() {
    26         logger.info(threadPoolConfig.toString());
    27     }
    28 }

    使用  @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")
          }
    )
  • 相关阅读:
    每日博客
    每日博客之十一月读书笔记1
    每日博客
    今日收获
    今日收获
    今日收获
    动手动脑10.21
    今日收获
    今日收获
    8月21日
  • 原文地址:https://www.cnblogs.com/warehouse/p/8681187.html
Copyright © 2011-2022 走看看