欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Xml ResourceBundle简单实现

程序员文章站 2022-06-08 20:31:23
...

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来做的,不需要每次都去装载资源文件。内部实现看源码即可明白,这里不过多解释。

 

附件附上源码实现,刚好看到这个且有兴趣的,欢迎一起交流或提供建议。