`
singleant
  • 浏览: 375119 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

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

 

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

 

 

分享到:
评论
5 楼 singleant 2012-05-07  
laiweiweihi 写道
楼主,可以用绝对路径代替 ClassPath 么?资源文件非要放在 ClassPath 下么?

这个用绝对路径不是太好吧?classpath自包含比较好。
绝对路径可以做,不过就意味着和环境绑定在一起了,这个绝对路径就需要一个可配置的入口。
4 楼 san_yun 2012-05-06  
javaeye 太恶心了,这也要通知我。。。。。
3 楼 laiweiweihi 2012-05-06  
楼主,可以用绝对路径代替 ClassPath 么?资源文件非要放在 ClassPath 下么?
2 楼 singleant 2012-04-24  
san_yun 写道
上次你提到可增量的倒排序索引来解决标签问题,什么时候分享下这方面的心得吧?

我只是觉得技术上可行呀 具体场景上我也没用过
1 楼 san_yun 2012-04-21  
上次你提到可增量的倒排序索引来解决标签问题,什么时候分享下这方面的心得吧?

相关推荐

Global site tag (gtag.js) - Google Analytics