1. <nobr id="easjo"><address id="easjo"></address></nobr>

      <track id="easjo"><source id="easjo"></source></track>
      1. 
        

      2. <bdo id="easjo"><optgroup id="easjo"></optgroup></bdo>
      3. <track id="easjo"><source id="easjo"><em id="easjo"></em></source></track><option id="easjo"><span id="easjo"><em id="easjo"></em></span></option>
          貴州做網站公司
          貴州做網站公司~專業!靠譜!
          10年網站模板開發經驗,熟悉國內外開源網站程序,包括DEDECMS,WordPress,ZBlog,Discuz! 等網站程序,可為您提供網站建設,網站克隆,仿站,網頁設計,網站制作,網站推廣優化等服務。我們專注高端營銷型網站,企業官網,集團官網,自適應網站,手機網站,網絡營銷,網站優化,網站服務器環境搭建以及托管運維等。為客戶提供一站式網站解決方案?。?!

          spring boot 使用ReloadableResourceBundleMessageSource的坑

          來源:互聯網轉載 時間:2024-01-29 08:15:46

          根據網上的例子MessageSource 配置如下

            @Bean(name = "messageSource")    public ReloadableResourceBundleMessageSource messageSource() {        ReloadableResourceBundleMessageSource messageBundle = new ReloadableResourceBundleMessageSource();        messageBundle.setBasename("messages/messages");        messageBundle.setDefaultEncoding("UTF-8");        return messageBundle;    }

          接著直接使用:

          圖片.png

          代碼調用:

          @Autowired    @Qualifier("messageSource")    private MessageSource messageSource;//下面在方法種使用messageSource.getMessage("test", new Object[], SIMPLIFIED_CHINESE);
          • 但是在使用過程中我發現出現異常如下:

          No message found under "test" for locale 'zh_CN'

          雖然網上也有很多資料但是找到沒找到問題的關鍵。。

          • 為什么會出現上述問題呢? 下面我們源碼分析一波
          1. 首先定位問題在ReloadableResourceBundleMessageSource 的類
          2. 在ReloadableResourceBundleMessageSource 的配置我們只配置了basename,所以問題接著就定位在basename
          3. 從問題拋出的異常點入手,messageSource.getMessage,messageSource是一個接口,真正起作用的是實現類AbstractMessageSource。

          整個繼承圖如下:

          圖片.png

          所以我們重點關注的AbstractMessageSource的getMessage方法。以其中一個為例分析

          public final String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {        String[] codes = resolvable.getCodes();        if (codes != null) {            String[] var4 = codes;            int var5 = codes.length;            for(int var6 = 0; var6 < var5; ++var6) {                String code = var4[var6];//這里去取資源文件中的數據,我們繼續跟蹤如下                String message = this.getMessageInternal(code, resolvable.getArguments(), locale);                if (message != null) {                    return message;                }            }        }//這里如果沒有從配置文件種找到,會走默認,但是我們沒有提供默認,所以拋出異常        String defaultMessage = this.getDefaultMessage(resolvable, locale);        if (defaultMessage != null) {            return defaultMessage;        } else {//這里就是我們異常的觸發點            throw new NoSuchMessageException(!ObjectUtils.isempty(codes) ? codes[codes.length - 1] : null, locale);        }    }

          getMessageInternal方法:

          protected String getMessageInternal(String code, Object[] args, Locale locale) {       //省略。。。//如果使用模版,使用下面方法            if (!this.isAlwaysUseMessageFormat() && ObjectUtils.isEmpty(args)) {                String message = this.resolveCodeWithoutArguments(code, locale);                if (message != null) {                    return message;                }            } else {//否則如下                argsToUse = this.resolveArguments(args, locale);                MessageFormat messageFormat = this.resolveCode(code, locale);                if (messageFormat != null) {                    synchronized(messageFormat) {                        return messageFormat.format(argsToUse);                    }                }            }            Properties commonMessages = this.getCommonMessages();            if (commonMessages != null) {                String commonMessage = commonMessages.getProperty(code);                if (commonMessage != null) {                    return this.formatMessage(commonMessage, args, locale);                }            }//如果還沒找到,調用父類放入資源查找            return this.getMessageFromParent(code, argsToUse, locale);        }    }

          通過上面的方法很明顯resolveCodeWithoutArguments和resolveCode方法就是核心方法,而這兩個方法最終也歸結為resolveCode:

              protected String resolveCodeWithoutArguments(String code, Locale locale) {        MessageFormat messageFormat = this.resolveCode(code, locale);        if (messageFormat != null) {            synchronized(messageFormat) {                return messageFormat.format(new Object[0]);            }        } else {            return null;        }    }//很明顯這個方法沒有實現,具體的實現方式,為我們最初定義的ReloadableResourceBundleMessageSource去實現的,回到ReloadableResourceBundleMessageSource類中查看    protected abstract MessageFormat resolveCode(String var1, Locale var2);

          回過頭我們開始分析我們注入spring的ReloadableResourceBundleMessageSource類

          public class ReloadableResourceBundleMessageSource extends AbstractResourceBasedMessageSource implements ResourceLoaderAware {//下面兩個屬性標示該類支持xml和properties兩種資源文件    private static final String PROPERTIES_SUFFIX = ".properties";    private static final String XML_SUFFIX = ".xml";//編碼類型    private Properties fileEncodings;//默認自動刷新,這也是我們選擇 ReloadableResourceBundleMessageSource 而不是用ResourceBundleMessageSource的一個原因    private boolean concurrentRefresh = true;    private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();//默認的資源加載器(這里是我們出現問題的關鍵)    private ResourceLoader resourceLoader = new DefaultResourceLoader();//緩存我們的文件名    private final ConcurrentMap<String, Map<Locale, List<String>>> cachedFilenames = new ConcurrentHashMap();//緩存資源PropertiesHolder(為內部類,每一個對象都應對的一個資源文件)    private final ConcurrentMap<String, ReloadableResourceBundleMessageSource.PropertiesHolder> cachedProperties = new ConcurrentHashMap();    private final ConcurrentMap<Locale, ReloadableResourceBundleMessageSource.PropertiesHolder> cachedMergedProperties = new ConcurrentHashMap();    public ReloadableResourceBundleMessageSource() {    }}//其他方法暫略。。。
          • 接著分析它實現了AbstractMessageSource抽象類中的resolveCode方法如下:
          protected MessageFormat resolveCode(String code, Locale locale) {//刷新        if (this.getCacheMillis() < 0L) {            ReloadableResourceBundleMessageSource.PropertiesHolder propHolder = this.getMergedProperties(locale);            MessageFormat result = propHolder.getMessageFormat(code, locale);            if (result != null) {                return result;            }        } else {            Iterator var10 = this.getBasenameSet().iterator();//下面兩個循環是通過key查找資源。從配置的的多個basename中的多個文件中查找文件            while(var10.hasNext()) {                String basename = (String)var10.next();                List<String> filenames = this.calculateAllFilenames(basename, locale);                Iterator var6 = filenames.iterator();//第二層循環為路徑下的資源文件,還記得前面說PropertiesHolder 其實對應每個國際化的資源文件                while(var6.hasNext()) {                    String filename = (String)var6.next();//this.getProperties(filename);這個方法獲取propHolder ,我們繼續跟蹤這個方法                    ReloadableResourceBundleMessageSource.PropertiesHolder propHolder = this.getProperties(filename);                    MessageFormat result = propHolder.getMessageFormat(code, locale);                    if (result != null) {                        return result;                    }                }            }        }
          • getProperties方法
           protected ReloadableResourceBundleMessageSource.PropertiesHolder getProperties(String filename) {//這一步先從之前緩存中取,第一次沒有緩存,所以直接跳過看else中的代碼        ReloadableResourceBundleMessageSource.PropertiesHolder propHolder = (ReloadableResourceBundleMessageSource.PropertiesHolder)this.cachedProperties.get(filename);        long originalTimestamp = -2L;        ReloadableResourceBundleMessageSource.PropertiesHolder existingHolder;        if (propHolder != null) {            originalTimestamp = propHolder.getRefreshTimestamp();            if (originalTimestamp == -1L || originalTimestamp > System.currentTimeMillis() - this.getCacheMillis()) {                return propHolder;            }//新創建PropertiesHolder接著放到緩存        } else {            propHolder = new ReloadableResourceBundleMessageSource.PropertiesHolder();            existingHolder = (ReloadableResourceBundleMessageSource.PropertiesHolder)this.cachedProperties.putIfAbsent(filename, propHolder);            if (existingHolder != null) {                propHolder = existingHolder;            }        }        if (this.concurrentRefresh && propHolder.getRefreshTimestamp() >= 0L) {            if (!propHolder.refreshLock.tryLock()) {                return propHolder;            }        } else {            propHolder.refreshLock.lock();        }        ReloadableResourceBundleMessageSource.PropertiesHolder var6;        try {//直接從緩存中取PropertiesHolder,并查看是否過期,過期則重新加載            existingHolder = (ReloadableResourceBundleMessageSource.PropertiesHolder)this.cachedProperties.get(filename);//默認沒有定義兩者均為-2 所以直接執行刷新操作refreshProperties            if (existingHolder != null && existingHolder.getRefreshTimestamp() > originalTimestamp) {                var6 = existingHolder;                return var6;            }//刷新資源,該方法會將資源文件加載到propHolder,繼續看它是如何加載的            var6 = this.refreshProperties(filename, propHolder);        } finally {            propHolder.refreshLock.unlock();        }        return var6;    }
          • refreshProperties加載資源文件
          protected ReloadableResourceBundleMessageSource.PropertiesHolder refreshProperties(String filename, ReloadableResourceBundleMessageSource.PropertiesHolder propHolder) {        long refreshTimestamp = this.getCacheMillis() < 0L ? -1L : System.currentTimeMillis();//可以看到properties和xml文件均能加載,this.resourceLoader.getResource加載核心類,沒有配置使用的為spring默認的DefaultResourceLoader        Resource resource = this.resourceLoader.getResource(filename + ".properties");        if (!resource.exists()) {            resource = this.resourceLoader.getResource(filename + ".xml");        }//如果資源文件存在,添加時間戳,        if (resource.exists()) {            long fileTimestamp = -1L;            if (this.getCacheMillis() >= 0L) {                try {                    fileTimestamp = resource.lastModified();                    if (propHolder != null && propHolder.getFileTimestamp() == fileTimestamp) {                        if (this.logger.isDebugEnabled()) {                            this.logger.debug("Re-caching properties for filename [" + filename + "] - file hasn't been modified");                        }                        propHolder.setRefreshTimestamp(refreshTimestamp);                        return propHolder;                    }                } catch (IOException var10) {                    if (this.logger.isDebugEnabled()) {                        this.logger.debug(resource + " could not be resolved in the file system - assuming that it hasn't changed", var10);                    }                    fileTimestamp = -1L;                }            }            try {//根據resource, filename生成Properties屬性 創建PropertiesHolder對象(Properties就是java  中常用的配置方式,存有我們的國際化數據)                Properties props = this.loadProperties(resource, filename);                propHolder = new ReloadableResourceBundleMessageSource.PropertiesHolder(props, fileTimestamp);            } catch (IOException var9) {                if (this.logger.isWarnEnabled()) {                    this.logger.warn("Could not parse properties file [" + resource.getFilename() + "]", var9);                }                propHolder = new ReloadableResourceBundleMessageSource.PropertiesHolder();            }        } else {            if (this.logger.isDebugEnabled()) {                this.logger.debug("No properties file found for [" + filename + "] - neither plain properties nor XML");            }            propHolder = new ReloadableResourceBundleMessageSource.PropertiesHolder();        }        propHolder.setRefreshTimestamp(refreshTimestamp);        this.cachedProperties.put(filename, propHolder);        return propHolder;    }

          上面方法重點在于兩個方法,其一是是否成功生成resource資源,其二為loadProperties屬性是否正確。這兩種方法如果均為加載我們的資源文件,也都會生成propHolder,但是會取不到數據,也就是前面的錯誤:No message found under "test" for locale 'zh_CN'

          • 所以分析這兩個方法: 1) this.resourceLoader.getResource(filename + ".properties");我們沒有配置資源加載器,所以這里其作用的為spring的默認資源加載器DefaultResourceLoader
          public Resource getResource(String location) {        Assert.notNull(location, "Location must not be null");        Iterator var2 = this.protocolResolvers.iterator();        Resource resource;        do {//如果/開頭使用路徑加載            if (!var2.hasNext()) {                if (location.startsWith("/")) {                    return this.getResourceByPath(location);                }//classpath開頭使用類路徑加載器                if (location.startsWith("classpath:")) {                    return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());                }//最后使用url加載(這里是出現之前的問題的關鍵)                try {                    URL url = new URL(location);                    return new UrlResource(url);                } catch (MalformedURLException var5) {                    return this.getResourceByPath(location);                }            }            ProtocolResolver protocolResolver = (ProtocolResolver)var2.next();            resource = protocolResolver.resolve(location, this);        } while(resource == null);        return resource;    }

          很顯然出現之前的問題為basename的路徑配置錯誤,資源文件在resource路徑下編譯后就是類的住目錄,所以這里應該使用classpath:為開頭,其他兩種分別為url和路徑加載的方式

          正確配置

          @Configurationpublic class I18nConfig {    @Bean(name = "messageSource")    public ReloadableResourceBundleMessageSource messageSource() {        ReloadableResourceBundleMessageSource messageBundle = new ReloadableResourceBundleMessageSource();        messageBundle.setBasename("classpath:messages/messages");        messageBundle.setDefaultEncoding("UTF-8");        return messageBundle;    }}
          • 注意這里messageBundle.setBasename("classpath:messages/messages");的classpath和使用setBasename("messages/messages")是有區別的
          標簽:refreshlock-

          網絡推廣與網站優化公司(網絡優化與推廣專家)作為數字營銷領域的核心服務提供方,其價值在于通過技術手段與策略規劃幫助企業提升線上曝光度、用戶轉化率及品牌影響力。這...

          在當今數字化時代,公司網站已成為企業展示形象、傳遞信息和開展業務的重要平臺。然而,對于許多公司來說,網站建設的價格是一個關鍵考量因素。本文將圍繞“公司網站建設價...

          在當今的數字化時代,企業網站已成為企業展示形象、吸引客戶和開展業務的重要平臺。然而,對于許多中小企業來說,高昂的網站建設費用可能會成為其發展的瓶頸。幸運的是,隨...

          如何將BT5安裝到U盤中?開始安裝:1。首先,將U盤格式化為FAT32格式2。解壓縮下載的unetbootin并運行unetbootin窗口-最新.exe接口如下:3。點擊界面右下角的“…”,選擇您下載的BT5 ISO鏡像。下面的輸入框是要設置的已保存配置文件的大小。根據個人需要和U盤大小填寫。比如我的U盤是8GB,填寫3200m,3.06gb的鏡像,3200m配置文件加載滿后大約7GB,U盤中還...

          myeclipse 2013如何破解注冊?這樣,注冊碼和激活碼都會生成用戶名和注冊碼,以便在MyEclipse中注冊,然后激活它。選擇使用激活碼激活,將激活碼粘貼到注冊機中,并確認MyEclipse已激活。進入注冊的方式是:window---preferences---MyEclipse---subscription。我希望它能幫助你...

          itunes怎么下載軟件?當前版本的iTunxp系統如何下載itunes軟件?在蘋果官網下載相應的win版本即可。怎樣查看iTunes正在下載軟件?下載軟件后,可以在iTun怎么下載itunes安裝到電腦?1.點擊開始菜單或者點擊win10系統的win圖標打開,找到app storitunes怎么安裝到電腦?1.點擊開始菜單或者點擊win10系統的win圖標打開,找到app storitunes里...

          TOP
          国产初高中生视频在线观看|亚洲一区中文|久久亚洲欧美国产精品|黄色网站入口免费进人
          1. <nobr id="easjo"><address id="easjo"></address></nobr>

              <track id="easjo"><source id="easjo"></source></track>
              1. 
                

              2. <bdo id="easjo"><optgroup id="easjo"></optgroup></bdo>
              3. <track id="easjo"><source id="easjo"><em id="easjo"></em></source></track><option id="easjo"><span id="easjo"><em id="easjo"></em></span></option>