📄 reloadableresourcebundlemessagesource.java
字号:
/*
* Copyright 2002-2004 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.support;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;
import org.springframework.util.StringUtils;
/**
* MessageSource that accesses the ResourceBundles with the specified basenames.
* This class uses <code>java.util.Properties</code> instances as its internal
* data structure for messages, loading them via a PropertiesPersister strategy:
* The default strategy can load properties files with a specific encoding.
*
* <p>In contrast to ResourceBundleMessageSource, this class supports reloading
* of properties files through the "cacheSeconds" setting, and also through
* programmatically clearing the properties cache. Since application servers do
* typically cache all files loaded from the classpath, it is necessary to store
* resources somewhere else (for example, in the "WEB-INF" directory of a web app).
* Otherwise changes of files in the classpath are not reflected in the application.
*
* <p>Note that the base names set as the "basename" and "basenames" properties are
* treated in a slightly different fashion than the "basename" property of
* ResourceBundleMessageSource. It follows the basic ResourceBundle rule of not
* specifying file extension or language codes, but can refer to any Spring resource
* location (instead of being restricted to classpath resources). With a "classpath:"
* prefix, resources can still be loaded from the classpath, but "cacheSeconds" values
* other than "-1" (caching forever) will not work in this case.
*
* <p>This MessageSource implementation is usually slightly faster than
* ResourceBundleMessageSource, which builds on <code>java.util.ResourceBundle</code>
* - in the default mode, i.e. when caching forever. With "cacheSeconds" set to 1,
* message lookup takes about twice as long - with the benefit that changes in
* individual properties files are detected with a maximum delay of 1 second.
* Higher "cacheSeconds" values usually <i>don't</i> make a significant difference.
*
* <p>This MessageSource can easily be used outside an ApplicationContext: It uses
* a DefaultResourceLoader as default, getting overridden with the ApplicationContext
* if running in a context. It does not have any other specific dependencies.
*
* <p>Thanks to Thomas Achleitner for providing the initial implementation of
* this message source!
*
* @author Juergen Hoeller
* @see #setCacheSeconds
* @see #setBasenames
* @see #setDefaultEncoding
* @see #setFileEncodings
* @see #setPropertiesPersister
* @see #setResourceLoader
* @see org.springframework.util.DefaultPropertiesPersister
* @see org.springframework.core.io.DefaultResourceLoader
* @see ResourceBundleMessageSource
* @see java.util.ResourceBundle
*/
public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
implements ResourceLoaderAware {
private static final String PROPERTIES_SUFFIX = ".properties";
private String[] basenames;
private String defaultEncoding;
private Properties fileEncodings;
private boolean fallbackToSystemLocale = true;
private long cacheMillis = -1;
/** Cache to hold filename lists per Locale */
private final Map cachedFilenames = new HashMap();
/** Cache to hold already loaded properties per filename */
private final Map cachedProperties = new HashMap();
/** Cache to hold merged loaded properties per basename */
private final Map cachedMergedProperties = new HashMap();
private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
private ResourceLoader resourceLoader = new DefaultResourceLoader();
/**
* Set a single basename, following the basic ResourceBundle convention of
* not specifying file extension or language codes, but in contrast to
* ResourceBundleMessageSource referring to a Spring resource location:
* e.g. "WEB-INF/messages" for "WEB-INF/messages.properties",
* "WEB-INF/messages_en.properties", etc.
* @param basename the single basename
* @see #setBasenames
* @see org.springframework.core.io.ResourceEditor
* @see java.util.ResourceBundle
*/
public void setBasename(String basename) {
setBasenames(new String[]{basename});
}
/**
* Set an array of basenames, each following the above-mentioned special
* convention. The associated resource bundles will be checked sequentially
* when resolving a message code.
* <p>Note that message definitions in a <i>previous</i> resource bundle
* will override ones in a later bundle, due to the sequential lookup.
* @param basenames an array of basenames
* @see #setBasename
* @see java.util.ResourceBundle
*/
public void setBasenames(String[] basenames) {
this.basenames = basenames;
}
/**
* Set the default charset to use for parsing properties files.
* Used if no file-specific charset is specified for a file.
* <p>Default is none, using java.util.Properties' default charset.
* @see #setFileEncodings
* @see org.springframework.util.PropertiesPersister#load
*/
public void setDefaultEncoding(String defaultEncoding) {
this.defaultEncoding = defaultEncoding;
}
/**
* Set per-file charsets to use for parsing properties files.
* @param fileEncodings Properties with filenames as keys and charset
* names as values. Filenames have to match the basename syntax,
* with optional locale-specific appendices: e.g. "WEB-INF/messages"
* or "WEB-INF/messages_en".
* @see #setBasenames
* @see org.springframework.util.PropertiesPersister#load
*/
public void setFileEncodings(Properties fileEncodings) {
this.fileEncodings = fileEncodings;
}
/**
* Set whether to fall back to the system Locale if no files for a specific
* Locale have been found. Default is true; if this is turned off, the only
* fallback will be the default file (e.g. "messages.properties" for
* basename "messages").
* <p>Falling back to the system Locale is the default behavior of
* java.util.ResourceBundle. However, this is often not desirable in an
* application server environment, where the system Locale is not relevant
* to the application at all: Set this flag to "false" in such a scenario.
*/
public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
this.fallbackToSystemLocale = fallbackToSystemLocale;
}
/**
* Set the number of seconds to cache loaded properties files.
* <ul>
* <li>Default is "-1", indicating to cache forever (just like
* java.util.ResourceBundle).
* <li>A positive number will cache loaded properties files for the given
* number of seconds. This is essentially the interval between refresh attempts.
* Note that a refresh attempt will first check the last-modified timestamp
* of the file before actually reloading it; so if files don't change, this
* interval can be set rather low, as refresh attempts will not actually reload.
* <li>A value of "0" will check the last-modified timestamp of the file on
* every message access. <b>Do not use this in a production environment!</b>
* </ul>
*/
public void setCacheSeconds(int cacheSeconds) {
this.cacheMillis = cacheSeconds * 1000;
}
/**
* Set the PropertiesPersister to use for parsing properties files.
* The default is DefaultPropertiesPersister.
* @see org.springframework.util.DefaultPropertiesPersister
*/
public void setPropertiesPersister(PropertiesPersister propertiesPersister) {
this.propertiesPersister = propertiesPersister;
}
/**
* Set the ResourceLoader to use for loading bundle properties files.
* The default is DefaultResourceLoader. Will get overridden by the
* ApplicationContext if running in a context.
* @see org.springframework.core.io.DefaultResourceLoader
*/
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
protected String resolveCodeWithoutArguments(String code, Locale locale) {
if (this.cacheMillis < 0) {
PropertiesHolder propHolder = getMergedProperties(locale);
String result = propHolder.getProperty(code);
if (result != null) {
return result;
}
}
else {
for (int i = 0; i < this.basenames.length; i++) {
List filenames = calculateAllFilenames(this.basenames[i], locale);
for (int j = 0; j < filenames.size(); j++) {
String filename = (String) filenames.get(j);
PropertiesHolder propHolder = getProperties(filename);
String result = propHolder.getProperty(code);
if (result != null) {
return result;
}
}
}
}
return null;
}
protected MessageFormat resolveCode(String code, Locale locale) {
if (this.cacheMillis < 0) {
PropertiesHolder propHolder = getMergedProperties(locale);
MessageFormat result = propHolder.getMessageFormat(code, locale);
if (result != null) {
return result;
}
}
else {
for (int i = 0; i < this.basenames.length; i++) {
List filenames = calculateAllFilenames(this.basenames[i], locale);
for (int j = 0; j < filenames.size(); j++) {
String filename = (String) filenames.get(j);
PropertiesHolder propHolder = getProperties(filename);
MessageFormat result = propHolder.getMessageFormat(code, locale);
if (result != null) {
return result;
}
}
}
}
return null;
}
/**
* Get a PropertiesHolder that contains the actually visible properties
* for a Locale, after merging all specified resource bundles.
* Either fetches the holder from the cache or freshly loads it.
* <p>Only used when caching resource bundle contents forever, i.e.
* with cacheSeconds < 0. Therefore, merged properties are always
* cached forever.
*/
protected PropertiesHolder getMergedProperties(Locale locale) {
synchronized (this.cachedMergedProperties) {
PropertiesHolder mergedHolder = (PropertiesHolder) this.cachedMergedProperties.get(locale);
if (mergedHolder != null) {
return mergedHolder;
}
Properties mergedProps = new Properties();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -