ALL is Well!

敏捷是一条很长的路,摸索着前进着

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  30 随笔 :: 23 文章 :: 71 评论 :: 0 Trackbacks

他山之石可以攻玉。
Spring为我们提供了一个PropertyPlaceholderConfigurer,它能够使Bean在配置时引用外部属性文件。
可以将BeanFactory定义中的一些属性值放到另一个单独的标准Java Properties文件中。
我们在部署应用时只需要在属性文件中对一些属性进行修改,而不用对主XML定义文件或容器所用文件进行复杂和危险的修改。
让我们看看下面的例子片段:

    <bean id="propertyConfigurer"
        class
="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        
<property name="locations">
            
<list>      
                
<value>classpath:jdbc.properties</value>
            
</list>      
        
</property>      
    
</bean> 
    
<bean id="proxoolDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        
<property name="driverClassName" value="${driver}" />
        
<property name="url" value="${dburl}" />
        
<property name="username" value="${username}" /> 
        
<property name="password" value="${password}" />
    
</bean>

jdbc.properties:

driver=oracle.jdbc.OracleDriver
dburl=jdbc:oracle:thin:@localhost:1521:root
username=myusername
password=mypassword


相信上面的配置大家都用到过,
如此配置后 xml 文件中的 "${***}"占位符会被替换成jdbc.properties中对应的属性值。

现在我有一个需求,要求在DB中配置一些参数,如数据库的用户名、密码等,我在参数中提供一个模板,
形如 jdbc:oracle:thin:@${host}:${port:1521}:${service_name}。
然后host、port、service_name从参数表中取得,然后进行替换。
于是,我想到了Spring为我们提供的PropertyPlaceholderConfigurer.java,在看了代码之后,将字符串替换的代码摘出来,为我的需求服务。
下面是我摘出来的字符串解析替换的辅助类:

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.util.StringUtils;

public class PlaceholderUtils {

    
/** Default Holder prefix: "${" */
    
public static final String DEF_HOLDER_PREFIX            = "${";

    
public static final int    DEF_HOLDER_PREFIX_LEN        = 2;

    
/** Default Holder suffix: "}" */
    
public static final String DEF_HOLDER_SUFFIX            = "}";

    
public static final int    DEF_HOLDER_SUFFIX_LEN        = 1;
    
/** Never check system properties. */
    
public static final int    SYSTEM_PROPERTIES_MODE_NEVER = 0;

    
/**
     * Check system properties if not resolvable in the specified properties.
     * This is the default.
     
*/

    
public static final int    SYS_PROPS_MODE_FALLBACK      = 1;

    
/**
     * Check system properties first, before trying the specified properties.
     * This allows system properties to override any other property source.
     
*/

    
public static final int    SYS_PROPS_MODE_OVERRIDE      = 2;

    
/**
     * Parse the given String value recursively, to be able to resolve
     * nested Holders (when resolved property values in turn contain
     * Holders again).
     * 
     * 
@param strVal
     *            the String value to parse
     * 
@param props
     *            the Properties to resolve Holders against
     * 
@param visitedHolders
     *            the Holders that have already been visited
     *            during the current resolution attempt (used to detect circular references
     *            between Holders). Only non-null if we're parsing a nested Holder.
     * 
@throws Exception
     * 
@throws AppException
     *             if invalid values are encountered
     * 
@see #resolveHolder(String, java.util.Properties, int)
     
*/

    
public static String parse(String strVal) throws Exception {
        Set
<String> visitedHolders = new HashSet<String>();
        
return parse(strVal, null, visitedHolders, false);
    }


    
public static String parse(String strVal, Map<Object, Object> props) throws Exception {
        Set
<String> visitedHolders = new HashSet<String>();
        
return parse(strVal, props, visitedHolders, false);
    }


    
public static String parse(String strVal, boolean ignoreBadHolders) throws Exception {
        Set
<String> visitedHolders = new HashSet<String>();
        
return parse(strVal, null, visitedHolders, ignoreBadHolders);
    }


    
private static String parse(String strVal, Map<Object, Object> props,
        Set
<String> visitedHolders, boolean ignoreBadHolders) throws Exception {

        StringBuffer buf 
= new StringBuffer(strVal);
        
int startIndex = strVal.indexOf(DEF_HOLDER_PREFIX);
        
while (startIndex != -1{
            
int endIndex = findHolderEndIndex(buf, startIndex);
            
if (endIndex != -1{
                String holder 
= buf.substring(startIndex + DEF_HOLDER_PREFIX_LEN, endIndex);
                String defValue 
= null;
                
int defIndex = org.apache.commons.lang.StringUtils.lastIndexOf(holder, ":");
                
if (defIndex >= 0{
                    defValue 
= StringUtils.trimWhitespace(holder.substring(defIndex + 1));
                    holder 
= StringUtils.trimWhitespace(holder.substring(0, defIndex));
                }


                
if (!visitedHolders.add(holder)) {
                    
throw new Exception("Circular PlaceHolder reference '" + holder
                        
+ "' in property definitions");
                }

                
// Recursive invocation, parsing Holders contained in the Holder key.
                holder = parse(holder, props, visitedHolders, ignoreBadHolders);
                
// Now obtain the value for the fully resolved key
                String propVal = resolveHolder(holder, props, SYS_PROPS_MODE_FALLBACK, defValue);
                
if (propVal != null{
                    
// Recursive invocation, parsing Holders contained in the
                    
// previously resolved Holder value.
                    propVal = parse(propVal, props, visitedHolders, ignoreBadHolders);
                    buf.replace(startIndex, endIndex 
+ DEF_HOLDER_SUFFIX_LEN, propVal);
                    startIndex 
= buf.indexOf(DEF_HOLDER_PREFIX, startIndex + propVal.length());
                }
 else if (ignoreBadHolders) {
                    
// Proceed with unprocessed value.
                    startIndex = buf.indexOf(DEF_HOLDER_PREFIX, endIndex + DEF_HOLDER_SUFFIX_LEN);
                }
 else {
                    
throw new Exception("Could not resolve Placeholder '" + holder + "'");
                }

                visitedHolders.remove(holder);
            }
 else {
                startIndex 
= -1;
            }

        }


        
return buf.toString();
    }


    
private static int findHolderEndIndex(CharSequence buf, int startIndex) {
        
int index = startIndex + DEF_HOLDER_PREFIX_LEN;
        
int withinNestedHolder = 0;
        
while (index < buf.length()) {
            
if (StringUtils.substringMatch(buf, index, DEF_HOLDER_SUFFIX)) {
                
if (withinNestedHolder > 0{
                    withinNestedHolder
--;
                    index 
= index + DEF_HOLDER_SUFFIX_LEN;
                }
 else {
                    
return index;
                }

            }
 else if (StringUtils.substringMatch(buf, index, DEF_HOLDER_PREFIX)) {
                withinNestedHolder
++;
                index 
= index + DEF_HOLDER_PREFIX_LEN;
            }
 else {
                index
++;
            }

        }

        
return -1;
    }


    
/**
     * Resolve the given Holder using the given properties, performing
     * a system properties check according to the given mode.
     * <p>
     * Default implementation delegates to <code>resolveHolder
     * (Holder, props)</code> before/after the system properties check.
     * <p>
     * Subclasses can override this for custom resolution strategies, including customized points
     * for the system properties check.
     * 
     * 
@param holder
     *            the Holder to resolve
     * 
@param props
     *            the merged properties of this configurer
     * 
@param sysPropsMode
     *            the system properties mode,
     *            according to the constants in this class
     * 
@return the resolved value, of null if none
     * 
@see #setSystemPropertiesMode
     * 
@see System#getProperty
     * 
@see #resolveHolder(String, java.util.Properties)
     
*/

    
private static String resolveHolder(String holder, Map<Object, Object> props, int sysPropsMode,
        String defaultValue) 
{
        String propVal 
= null;
        
if (sysPropsMode == SYS_PROPS_MODE_OVERRIDE) {
            propVal 
= resolveSystemProperty(holder);
        }

        
if (propVal == null{
            propVal 
= resolveHolder(holder, props, defaultValue);
        }

        
if (propVal == null && sysPropsMode == SYS_PROPS_MODE_FALLBACK) {
            propVal 
= resolveSystemProperty(holder);
        }

        
return propVal;
    }


    
/**
     * Resolve the given Holder using the given properties.
     * The default implementation simply checks for a corresponding property key.
     * <p>
     * Subclasses can override this for customized Holder-to-key mappings or custom resolution
     * strategies, possibly just using the given properties as fallback.
     * <p>
     * Note that system properties will still be checked before respectively after this method is
     * invoked, according to the system properties mode.
     * 
     * 
@param holder
     *            the Holder to resolve
     * 
@param props
     *            the merged properties of this configurer
     * 
@return the resolved value, of <code>null</code> if none
     * 
@see #setSystemPropertiesMode
     
*/

    
private static String resolveHolder(String holder, Map<Object, Object> props,
        String defaultValue) 
{
        
if (props != null{
            Object value 
= props.get(holder);
            
if (value != null{
                
return "" + value;
            }
 else if (defaultValue != null{
                
return defaultValue;
            }

        }


        
return defaultValue;
    }


    
/**
     * Resolve the given key as JVM system property, and optionally also as
     * system environment variable if no matching system property has been found.
     * 
     * 
@param key
     *            the Holder to resolve as system property key
     * 
@return the system property value, or <code>null</code> if not found
     * 
@see #setSearchSystemEnvironment
     * 
@see java.lang.System#getProperty(String)
     * 
@see java.lang.System#getenv(String)
     
*/

    
private static String resolveSystemProperty(String key) {
        
try {
            String value 
= System.getProperty(key);
            
if (value == null{
                value 
= System.getenv(key);
            }

            
return value;
        }
 catch (Throwable ex) {
            ex.printStackTrace();
            
return null;
        }

    }

}


下面是测试类:

import java.util.Properties;

public class PlaceholderStringTest {
    
public static void main(String[] args) throws Exception {
        Properties props 
= new Properties();
        
// 在.properties文件中放置key1、key2
        props.put("key1""Hello");
        props.put(
"key2""World");
        
        String str 
= null;
        
// 替换key1、key2
        str = PlaceholderUtils.parse("Property:${key1}=${key2}", props);
        System.out.println(str);
// Property:Hello=World

        
// 此处要替换的是 key${index:3},先去看.properties属性中是否有index属性,有则替换其值
        
// 再去看系统属性中是否有index属性,有则替换其值
        
// 由于都没有index属性,所以取值为 3,也就是要替换 xxx${key3:yyy}
        
// 由于key3在.properties文件的属性中、系统属性中均没有此属性,所以返回默认值 yyy
        str = PlaceholderUtils.parse("xxx${key${index:3}:yyy}", props);
        System.out.println(str); 
// xxxyyy

        
// 在.properties文件属性中加入index=2
        props.put("index""2");

        
// 此处的index属性值为2,则替换key2的属性值,默认值yyy被忽略了
        str = PlaceholderUtils.parse("xxx${key${index:3}:yyy}", props);
        System.out.println(str); 
// xxxWorld

        
// 系统属性中加入var1
        System.setProperty("var1""IamSystem");
        str 
= PlaceholderUtils.parse("xxx${var1}");
        System.out.println(str); 
// xxxIamSystem

        System.setProperty(
"var2""System2");
        str 
= PlaceholderUtils.parse("xxx${var1}.${var2}");
        System.out.println(str);
// xxxIamSystem.System2

        str 
= PlaceholderUtils.parse("xxx${var1}.${var3}"true);
        System.out.println(str); 
// xxxIamSystem.${var3} 
        props.clear();
        
        
// 模板
        String dburlTmp = "jdbc:oracle:thin:@${host}:${port:1521}:${service_name}";
        Properties dbProps 
= new Properties();
        dbProps.put(
"host""localhost");
        dbProps.put(
"service_name""root");
        str 
= PlaceholderUtils.parse(dburlTmp, dbProps);
        System.out.println(str); 
// jdbc:oracle:thin:@localhost:1521:root
    }

}

通过上面的代码,我们便可以实现自己的placeholder了。再加上Json Schema的校验类,给自己的参数定义Schema,使用时校验配置参数的正确性,然后再进行Placeholder,最后将这些参数生成Json对象,供程序使用,非常方便。

本文为原创,欢迎转载,转载请注明出处BlogJava

posted on 2010-09-13 15:44 李 明 阅读(4873) 评论(2)  编辑  收藏 所属分类: JavaSpring

评论

# re: 顺手牵 Spring中的PropertyPlaceholderConfigurer.java 2010-09-13 21:14 cxh8318
有必要搞得如此复杂吗?  回复  更多评论
  

# re: 顺手牵 Spring中的PropertyPlaceholderConfigurer.java 2010-09-13 21:43 Java你我
复杂肯定不是我要的。
这只是一种解决方法,当然也可以通过增加特殊意义的配置表来完成。@cxh8318
  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: