天天看點

spring 如何動态加載properties檔案

1. 在xml中配置properties路徑

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list> <!-- 指定資源檔案基名稱 jdbc為檔案名,不包含擴充名 -->
            <value>classpath:resource/jdbc</value>
        </list>
    </property>
</bean>
           

2. 擷取WebApplicationContext(需要入參HttpServerletRequest request)

ServletContext servletContext = request.getSession() .getServletContext();
WebApplicationContext ctx = WebApplicationContextUtils .getRequiredWebApplicationContext(servletContext);
           

3. tongguo WebApplicationContext擷取中鍵值String msg = ctx.getMessage("jdbc.url",null,Locale.CHINA);

Spring的MessageSource有兩個常用的實作ReloadableResourceBundleMesssageSource和ResourceBundleMessageSource.

  • ResourceBundleMessageSource:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="parentMessageSource" ref="bizMessageSource" />
    <property name = "basenames">
        <list>
            <value>resources.cls-web-resources</value>
            <value>resources.cls-web-resources-definitions</value>
            <value>resources.cls-web-resources-menu</value>
        </list>
    </property>
</bean>
           
  • ReloadableResourceBundleMesssageSource
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> 
    <property name="parentMessageSource" ref="bizMessageSource"/> 
    <property name="fallbackToSystemLocale">
        <value>false</value>
    </property> 
    <property name="basenames"> 
        <list>
            <value>classpath:resources/cls-web-resources</value>
            <value>classpath:resources/cls-web-resources-definitions</value> 
            <value>classpath:resources/cls-web-resources-menu</value>  
        </list> 
    </property>
</bean>
           

原因:因為ReloadableResourceBundleMessageSource的内部使用DefaultResourceLoader來裝載ResourceBundle, 而ResourceBundleMessageSource内部是直接使用java.util.ResourceBundle.getBundle(String baseName,Locale locale, ClassLoader loader) 來擷取i18n檔案資訊,而ResourceBundle是使用"."來作為basename分隔符的

另外如果不設定"fallbackToSystemLocale"的話, 那麼當傳入的Locale是null或者ResourceBundle沒有改Locale的配置檔案的話, 那麼會傳回Locale.getDefault()的Locale下的Meseage. 該設定預設為True, 也就是說, 如果找不到相應的ResourceBundle, 系統始終會顯示為中文的Resource, 建議關閉該設定, 否則fallBackLocale就沒有什麼意義了.

另外還有一個有用的設定“useCodeAsDefaultMessage”,預設為false,這樣當Spring在ResourceBundle中找不到messageKey的話,就抛出NoSuchMessageException,把它設定為True,則找不到不會抛出異常,而是使用messageKey作為傳回值。

spring中ResourceBundleMessageSource與ReloadableResourceBundleMessageSource查找資源的差別:

  1. ResourceBundleMessageSource在XML配置中無法指定編碼,而ReloadableResourceBundleMessageSource可以指定編碼
  2. 加載資源檔案的方式不同:
    1. ResourceBundleMessageSource的加載,使用ClassUtils.getDefaultClassLoader()加載器,getDefaultClassLoader的方法代碼如下:
    public static ClassLoader getDefaultClassLoader() { 
        ClassLoader cl = null;
        try { 
            cl = Thread.currentThread().getContextClassLoader();
        }catch (Throwable ex) {
            logger.debug("Cannot access thread context ClassLoader - falling back to system class loader", ex);
        } 
        if (cl == null){
            cl = ClassUtils.class.getClassLoader();
        }
        return cl; 
    }
               
    這種方式也是JVM預設的加載方式,先從目前線程中擷取類加載器,如果沒有,就擷取這個類本身的類加載器
    1. ReloadableResourceBundleMessageSource預設也使用ClassUtils.getDefaultClassLoader()加載器,它加載資源的方式如下:
    public Resource getResource(String location){
        Assert.notNull(location, "Location must not be null");
        if (location.startsWith("classpath:")) {
            return new ClassPathResource(location.s string("classpath:".length()), getClassLoader()); 
        } 
        try{
            URL url = new URL(location);
            return new UrlResource(url);
        }catch (MalformedURLException ex){
            return getResourceByPath(location);
        }
    }
               
    1. 小結:ResourceBundleMessageSource從classloader中加載資源檔案,可以看到:ReloadableResourceBundleMessageSource加載時,預設使用DefaultResourceLoader,他會先判斷資源path是否帶有classpath:字首,如果有,用 ClassPathResource去加載資源檔案,如果沒有試着用檔案協定的url去通路,再沒有就在contextPath即WEB-INF下查找.
  3. 在項目中,MessageSource不會單獨使用,通常我們會把它和自己的業務一起使用,這時候我們可以直接用它本身的方法,我們也可以在其中加入我們自己的邏輯:如,自定義的一個消息類:
public class MessageSourceHelper { 
    private ResourceBundleMessageSource messageSource;
    public String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
        String msg = messageSource.getMessage(code, args, defaultMessage, locale);
        return msg != null ? msg.trim() : msg;
    }
    public void setMessageSource(ResourceBundleMessageSource messageSource) {
        this.messageSource = messageSource;
    }
} 
           

在beans-message.xml中注入:

<bean id="messageSourceHelper" class="com.myspring.message.MessageSourceHelper">
    <property name="messageSource">
        <ref local="messageSource" />
    </property>
</bean> 
           
  1. 我們可以在MessageSourceHelper中加入自己的業務,注入依賴後,就可以在其他類中調用MessageSourceHelper中的方法。
  2. 理論簡要:ApplicationContext接口擴充了MessageSource 接口,因而提供了消息處理的功能(i18n或者國際化)。與HierarchicalMessageSource一起使用,它還能夠處理嵌套的消息,這些是Spring提供的處理消息的基本接口。讓我們快速浏覽一下它所定義的方法:
    • String getMessage(String code, Object[] args, String default, Locale loc):用來從MessageSource擷取消息的基本方法。如果在指定的locale中沒有找到消息,則使用預設的消息。args中的參數将使用标準類庫中的MessageFormat來作消息中替換值。
    • String getMessage(String code, Object[] args, Locale loc):本質上和上一個方法相同,其差別在:沒有指定預設值,如果沒找到消息,會抛出一個NoSuhMessageException異常。
    • String getMessage(MessageSourceResolvable resolvable, Locale locale):上面方法中所使用的屬性都封裝到一個MessageSourceResolvable實作中,而本方法可以指定 MessageSourceResolvable實作。

當一個ApplicationContext被加載時,它會自動在context中查找已定義為MessageSource類型的bean。此bean的名稱須為messageSource。如果找到,那麼所有對上述方法的調用将被委托給該 bean。否則ApplicationContext會在其父類中查找是否含有同名的bean。如果有,就把它作為MessageSource。如果它最終沒有找到任何的消息源,一個空的StaticMessageSource将會被執行個體化,使它能夠接受上述方法的調用。

Spring目前提供了兩個MessageSource的實作:ResourceBundleMessageSource和StaticMessageSource。它們都繼承 NestingMessageSource以便能夠處理嵌套的消息。StaticMessageSource很少被使用,但能以程式設計的方式向消息源添加消息。ResourceBundleMessageSource會用得更多一些

繼續閱讀