Xml ResourceBundle简单实现
ResourceBundle主要是用于和本地语言环境相关的一些资源绑定。特别是String资源。
从国际化的设计角度看,一般在代码里不编写和语言环境相关的东西。比如在代码里编写和语言环境相关的错误提示或信息。
以下面枚举为例:
public enum WeekdayEnum { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday; }
如果期望在不同的weekday的心情,不同语言环境输出不同的信息,如中文环境希望这样输出:
Monday:星期一郁闷 Tuesday:星期二忐忑 Wednesday:星期三难熬 Thursday:星期四期待 Friday星期五激动 Saturday:星期六高兴 Sunday:星期天担忧
这时我们需要定义资源文件,为不同的语言环境制定不同的资源文件,同时支持占位符,可以通过动态传入字符串替换占位符。
JDK自带的ResourceBundle支持properties文件作为资源文件绑定。而我们有的时候希望更习惯于xml的文件定义。这时可以基于JDK ResourceBundl扩展,实现基于XML的ResourceBundl资源绑定。
我们公司里面的二方库有一套xml resourcebundle的实现,但是该实现需要依赖一些外部的jar包,如xml解析等等。
有时候,这种基础功能基于原生实现更方便,不要去依赖其他的二方库,所以实现了一套完全基于JDK的实现。
1.定义资源文件 WeekDayEnum.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <entry key="Monday">星期一郁闷</entry> <entry key="Tuesday">星期二忐忑</entry> <entry key="Wednesday">星期三难熬</entry> <entry key="Thursday">星期四期待</entry> <entry key="Friday">星期五激动</entry> <entry key="Saturday">星期六高兴</entry> <entry key="Sunday">星期天担忧</entry> </properties>
2.编写代码实现
2.1.XmlResourceFactory
public class XmlResourceBundleFactory { private static XMLResourceBundleControl DEFAULT_CONTROL = new XMLResourceBundleControl(null,true); private final static String POINT_REGEX = "\\."; /** * 通过默认的XMLResourceBundleControl获取资源Bundle * * <pre> * 默认的XMLResourceBundleControl实现,就在classpath的跟目录下查找资源文件。 * </pre> * * @param fileName * @return */ public static ResourceBundle getBundle(String fileName) { return ResourceBundle.getBundle(fileName, DEFAULT_CONTROL); } /** * 通过指定path的XMLResourceBundleControl获取资源Bundle * * <pre> * 指定path的XMLResourceBundleControl实现,会在指定的classpath目录下查找资源文件。 * </pre> * * @param fileName * @param classpath * @return */ public static ResourceBundle getBundle(String classpath, String fileName) { XMLResourceBundleControl xmlResourceBundleControl = new XMLResourceBundleControl(classpath,true); return ResourceBundle.getBundle(fileName, xmlResourceBundleControl); } public static class XMLResourceBundleControl extends ResourceBundle.Control { private String resourcePath; // 資源的classpath路径 private boolean seprateDotPrefix; // 用于标识是否把包含点的baseName前缀过滤掉。 XMLResourceBundleControl(String resourcePath, boolean seprateDotPrefix){ this.resourcePath = resourcePath; this.seprateDotPrefix = seprateDotPrefix; } public List<String> getFormats(String baseName) { return Collections.singletonList("xml"); } public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException { if ((baseName == null) || (locale == null) || (format == null) || (loader == null)) { throw new NullPointerException(); } ResourceBundle bundle = null; if (!format.equals("xml")) { return null; } if (seprateDotPrefix) { if (baseName.indexOf(".") > 0) { String array[] = baseName.split(POINT_REGEX); baseName = array[array.length - 1]; } } String bundleName = toBundleName(baseName, locale); String resourceName = toResourceName(bundleName, format); String resourceFullName = resourceName; if (resourcePath != null && !"".equals(resourcePath.trim())) {// baseName可能包含包命前綴,把包命前綴去除 resourceFullName = resourcePath + "/" + resourceName; } URL url = loader.getResource(resourceFullName); if (url == null) { return null; } URLConnection connection = url.openConnection(); if (connection == null) { return null; } if (reload) { connection.setUseCaches(false); } InputStream stream = connection.getInputStream(); if (stream == null) { return null; } BufferedInputStream bis = new BufferedInputStream(stream); bundle = new XMLResourceBundle(bis); bis.close(); return bundle; } } static class XMLResourceBundle extends ResourceBundle { private Properties props; XMLResourceBundle(InputStream stream) throws IOException{ props = new Properties(); props.loadFromXML(stream); } protected Object handleGetObject(String key) { return props.getProperty(key); } public Enumeration<String> getKeys() { Set<String> handleKeys = props.stringPropertyNames(); return Collections.enumeration(handleKeys); } } }
2.2 CodeMessageHolder
public class CodeMessageHolder { private Class<?> type; private String resourcePath; private ResourceBundle resourceBundle; /** * 从指定路径加载resource * * @param type * @param classpath */ private CodeMessageHolder(Class<?> type, String classpath){ this.type = type; this.resourcePath = classpath; loadBundleBySpecialPath(); } /** * 从默认路径加载resource * * @param type */ private CodeMessageHolder(Class<?> type){ this.type = type; loadBundleByDefault(); } protected static CodeMessageHolder newDefaultMessageHolder(Class<?> type) { return new CodeMessageHolder(type); } protected static CodeMessageHolder newSpecialPathMessageHolder(Class<?> type,String classpath) { return new CodeMessageHolder(type,classpath); } private void loadBundleByDefault() { resourceBundle = XmlResourceBundleFactory.getBundle(type.getName()); } private void loadBundleBySpecialPath() { resourceBundle = XmlResourceBundleFactory.getBundle(resourcePath, type.getName()); } public String getMessage(Enum<?> e) { if (resourceBundle == null) { return null; } return resourceBundle.getString(e.name()); } public String getMessage(String key) { if (resourceBundle == null) { return null; } return resourceBundle.getString(key); } public String getMessage(Enum<?> e, String... param) { String s = getMessage(e); if (s == null) { return null; } return MessageFormat.format(s, (Object[]) param); } public String getMessage(String key, String... param) { String s = getMessage(key); if (s == null) { return null; } return MessageFormat.format(s, (Object[]) param); } }
3.编写枚举测试类,测试绑定
public enum WeekdayEnum { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday; private static CodeMessageHolder messageHolder = CodeMessageHolder.newDefaultMessageHolder(WeekdayEnum.class); public String getMessage() { return messageHolder.getMessage(this); } public String getMessage(String... params) { return messageHolder.getMessage(this, params); } public static void main(String[] args) { for (WeekdayEnum w : WeekdayEnum.values()) { System.out.println(w.getMessage()); } } }
关于resourcebundle内部实现,JDK是基于cache来做的,不需要每次都去装载资源文件。内部实现看源码即可明白,这里不过多解释。
附件附上源码实现,刚好看到这个且有兴趣的,欢迎一起交流或提供建议。
上一篇: 【shell】串行执行批量任务脚本
下一篇: [监控]Btrace监控简单笔记