ALL is Well!

          敏捷是一條很長的路,摸索著前進著

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            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中對應的屬性值。

          現(xiàn)在我有一個需求,要求在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屬性,有則替換其值
                  
          // 再去看系統(tǒng)屬性中是否有index屬性,有則替換其值
                  
          // 由于都沒有index屬性,所以取值為 3,也就是要替換 xxx${key3:yyy}
                  
          // 由于key3在.properties文件的屬性中、系統(tǒng)屬性中均沒有此屬性,所以返回默認值 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

                  
          // 系統(tǒng)屬性中加入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
              }

          }

          通過上面的代碼,我們便可以實現(xiàn)自己的placeholder了。再加上Json Schema的校驗類,給自己的參數定義Schema,使用時校驗配置參數的正確性,然后再進行Placeholder,最后將這些參數生成Json對象,供程序使用,非常方便。

          本文為原創(chuàng),歡迎轉載,轉載請注明出處BlogJava

          posted on 2010-09-13 15:44 李 明 閱讀(4917) 評論(2)  編輯  收藏 所屬分類: Java 、Spring

          評論

          # re: 順手牽 Spring中的PropertyPlaceholderConfigurer.java 2010-09-13 21:14 cxh8318
          有必要搞得如此復雜嗎?  回復  更多評論
            

          # re: 順手牽 Spring中的PropertyPlaceholderConfigurer.java 2010-09-13 21:43 Java你我
          復雜肯定不是我要的。
          這只是一種解決方法,當然也可以通過增加特殊意義的配置表來完成。@cxh8318
            回復  更多評論
            

          主站蜘蛛池模板: 互助| 仙桃市| 临高县| 油尖旺区| 舒兰市| 茶陵县| 巴中市| 会宁县| 津南区| 都安| 壤塘县| 礼泉县| 全椒县| 兰考县| 阜南县| 山阳县| 雅安市| 呼和浩特市| 深州市| 三亚市| 黔西| 中江县| 鸡西市| 蒲江县| 深州市| 同江市| 茂名市| 简阳市| 视频| 甘南县| 仙桃市| 阳东县| 平顶山市| 广安市| 雅安市| 炎陵县| 阿城市| 金寨县| 车致| 同心县| 社旗县|