• spring 配置文件 获取变量(PropertyPlaceholderConfigurer)


    转自:https://hbiao68.iteye.com/blog/2031006

    1.Spring的框架中,org.springframework.beans.factory.config.PropertyPlaceholderConfigurer类可以将.properties(key/value形式)文件中一些动态设定的值(value),在XML中替换为占位该键($key$)的值,.properties文件可以根据客户需求,自定义一些相关的参数,这样的设计可提供程序的灵活性。

    2.在Spring中,使用PropertyPlaceholderConfigurer可以在XML配置文件中加入外部属性文件,当然也可以指定外部文件的编码,如: 

    Xml代码  收藏代码
    1. <bean id="propertyConfigurerForAnalysis" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    2.     <property name="location">  
    3.         <value>classpath:/spring/include/dbQuery.properties</value>  
    4.     </property>  
    5.     <property name="fileEncoding">  
    6.         <value>UTF-8</value>  
    7.     </property>  
    8. </bean>  

    其中classpath是引用src目录下的文件写法,当存在多个Properties文件时,配置就需使用locations了:

    Xml代码  收藏代码
    1. <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    2.     <property name="locations">  
    3.         <list>  
    4.             <value>classpath:/spring/include/jdbc-parms.properties</value>  
    5.             <value>classpath:/spring/include/base-config.properties</value>  
    6.             <value>classpath*:config/jdbc.properties</value>  
    7.         </list>  
    8.     </property>  
    9. </bean>  
    Xml代码  收藏代码
    1. <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    2.     <property name="location">  
    3.         <value>classpath:hb.properties</value>  
    4.         <value>/WEB-INF/config/project/project.properties</value>  
    5.     </property>  
    6. </bean>  

    接下来我们要使用多个PropertyPlaceholderConfigurer来分散配置,达到整合多工程下的多个分散的Properties文件,其配置如下

    Xml代码  收藏代码
    1. <bean id="propertyConfigurerForProject2" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    2.     <property name="order" value="2" />  
    3.     <property name="ignoreUnresolvablePlaceholders" value="true" />  
    4.     <property name="locations">  
    5.       <list>  
    6.         <value>classpath:/spring/include/jdbc-parms.properties</value>  
    7.         <value>classpath:/spring/include/base-config.properties</value>  
    8.       </list>  
    9.     </property>  
    10. </bean>   

     其中order属性代表其加载顺序,而ignoreUnresolvablePlaceholders为是否忽略不可解析的Placeholder,如配置了多个PropertyPlaceholderConfigurer,则需设置为true

    3.譬如,jdbc.properties的内容为:

    Java代码  收藏代码
    1. jdbc.driverClassName=com.mysql.jdbc.Driver  
    2. jdbc.url=jdbc:mysql://localhost/mysqldb?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=round;  
    3. jdbc.username=root  
    4. jdbc.password=123456  

    备注:一定要在properties文件中&写为&amp;因为在xml文件中不识别&,必须是&amp 

    4.那么在spring配置文件中,我们就可以这样写:

    Xml代码  收藏代码
    1. <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    2.     <property name="locations">  
    3.         <list>  
    4.             <value>classpath: conf/sqlmap/jdbc.properties </value>  
    5.         </list>  
    6.     </property>  
    7. </bean>  
    8.    
    9. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
    10.     <property name="driverClassName" value="${jdbc.driverClassName}" />  
    11.     <property name="url" value="${jdbc.url}" />  
    12.     <property name="username" value="${jdbc.username}" />  
    13.     <property name="password" value="${jdbc.password}" />  
    14. </bean>  

    这样,一个简单的数据源就设置完毕了。可以看出:PropertyPlaceholderConfigurer起的作用就是将占位符指向的数据库配置信息放在bean中定义的工具

    查看源代码,可以发现,locations属性定义在PropertyPlaceholderConfigurer的祖父类PropertiesLoaderSupport中,而location只有setter方法。类似于这样的配置,在spring的源程序中很常见的。

    PropertyPlaceholderConfigurer如果在指定的Properties文件中找不到你想使用的属性,它还会在Java的System类属性中查找。
    我们可以通过System.setProperty(key, value)或者java中通过-Dnamevalue来给Spring配置文件传递参数。

    我们还可以使用注解的方式实现,如:<context:property-placeholder location="classpath:jdbc.properties" />,效果跟PropertyPlaceholderConfigurer是一样的。

    原理分析

    PropertyPlaceholderConfigurer类是如何做到xml配置的bean的配置熟悉的运行时替换的呢?这个离不开Spring框架提供的许多扩展点。这个类依赖的扩展点就是BeanFactoryPostProcessors见下面的类继承图:

    BeanFactoryPostProcessors类有一个方法,提供了在Bean创建前对Bean的Definition进行处理的钩子机制。

    1 //在Bean初始化前被调用(例如在InitializingBean的afterPropertiesSet前)
    2 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

    从上面的继承图我们知道PropertyPlaceholderConfigurer继承自类PropertyResourceConfigurer, 而该类实现了接口BeanFactoryPostProcessors

     1 public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport
     2         implements BeanFactoryPostProcessor, PriorityOrdered {
     3 @Override
     4     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
     5         try {
     6             Properties mergedProps = mergeProperties();
     7 
     8             // Convert the merged properties, if necessary.
     9             convertProperties(mergedProps);
    10 
    11             // Let the subclass process the properties.
    12             processProperties(beanFactory, mergedProps);
    13         }
    14         catch (IOException ex) {
    15             throw new BeanInitializationException("Could not load properties", ex);
    16         }
    17     }
    18     protected abstract void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
    19             throws BeansException;
    20 }

    我们的主角PropertyPlaceholderConfigurer实现了父类中的方法processProperties, 在该类中对xml配置文件中的展位符进行替换处理

     1 /**
     2  * Visit each bean definition in the given bean factory and attempt to replace ${...} property
     3  * placeholders with values from the given properties.
     4  */
     5 @Override
     6 protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
     7         throws BeansException {
     8 
     9     StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
    10     doProcessProperties(beanFactoryToProcess, valueResolver);
    11 }
    12 
    13 protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
    14         StringValueResolver valueResolver) {
    15 
    16     BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
    17 
    18     String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
    19     for (String curName : beanNames) {
    20         // Check that we're not parsing our own bean definition,
    21         // to avoid failing on unresolvable placeholders in properties file locations.
    22         if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
    23             BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
    24             try {
    25                 visitor.visitBeanDefinition(bd);
    26             }
    27             catch (Exception ex) {
    28                 throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
    29             }
    30         }
    31     }
    32 
    33     // New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
    34     beanFactoryToProcess.resolveAliases(valueResolver);
    35 
    36     // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
    37     beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
    38 }

    这样就解开了Spring中xml配置文件中占位符的替换魔法功能。

    现在想一下,如果我们的配置文件中对某些属性进行了加密,这时再使用 PropertyPlaceholderConfigurer 读取配置文件我们想要加密前的内容该怎么办?

    答案就是重写 PropertyPlaceholderConfigurer。除了数据库的配置信息我们放在配置文件,然后可以通过 druid 进行加解密。但是配置的邮箱信息呢?

    这时重写它就显得很有必要。PropertyPlaceholderConfigurer起的作用就是将占位符指向的数据库配置信息放在bean中定义的工具。

    下面来看一个通过 PropertyPlaceholderConfigurer读取加解密配置文件的案例:

     1 package com.xttblog.plugin;
     2 import com.zheng.common.util.AESUtil;
     3 import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
     4 //支持加密配置文件插件
     5 public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
     6     private String[] propertyNames = {
     7         "master.jdbc.password", "slave.jdbc.password", "generator.jdbc.password", "master.redis.password"
     8     };
     9     //解密指定propertyName的加密属性值
    10     @Override
    11     protected String convertProperty(String propertyName, String propertyValue) {
    12         for (String p : propertyNames) {
    13             if (p.equalsIgnoreCase(propertyName)) {
    14                 return AESUtil.AESDecode(propertyValue);
    15             }
    16         }
    17         return super.convertProperty(propertyName, propertyValue);
    18     }
    19 }

    我们只需重写 PropertyPlaceholderConfigurer 类的 convertProperty 方法即可,然后在该方法中实现解密工作。

  • 相关阅读:
    Docker Mysql 只从复制
    Mysql 常用sql记录
    ssh 内网穿透
    MyBatis相关记录
    mybatis(plus) 继承子模块的 Mapper文件
    Navicat 连接 Mysql 错误 2059
    angular service 进行组件通信
    angular 中的 ? 和 !
    angular @Input() 和 @Output()
    Centos7 安装 Docker CE
  • 原文地址:https://www.cnblogs.com/fnlingnzb-learner/p/10384742.html
Copyright © 2020-2023  润新知