`
singleant
  • 浏览: 378608 次
  • 性别: 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  
上次你提到可增量的倒排序索引来解决标签问题,什么时候分享下这方面的心得吧?

相关推荐

    读取properties、xml格式的配置文件的实例

    这是一个简单实现读取properties、xml格式的配置文件的小案例。虽然实际项目中可能不是这样实现的。作为了解也是不错的。 一、读取properties类型文件 方法一:java.util.ResourceBundle读取properties类型文件; ...

    ResourceBundleEditor_v0.7.5 eclipes国际化插件

    1. **资源文件的创建与编辑**:用户可以直接在Eclipse环境中创建新的`ResourceBundle`文件,无需手动编写XML或Properties文件。编辑界面支持实时预览,可以方便地添加、删除或修改键值对,同时提供错误检查,确保...

    关于struts-menu的简单使用方法(静态的取XML的方式-转载)

    一个简单的XML示例如下: ```xml 首页" action="home.action"/&gt; 产品" action="product_list.action"&gt; 新产品" action="new_product.action"/&gt; 热销产品" action="hot_product.action"/&gt; 联系我们" action=...

    09 Spring IoC容器ApplicationContext如何实现国际化慕课专栏1

    ResourceBundle是Java中用于实现国际化的基本工具类,它允许开发者为不同的语言环境创建不同的资源文件。例如,对于英文环境,我们可以创建一个名为`messages_en.properties`的文件,对于中文环境,则创建`messages_...

    struts2国际化demo

    这通常通过在Action类或者配置文件中设置`ResourceBundle`来实现。在Action类中,我们可以声明一个成员变量来持有资源包,并在构造函数或初始化方法中加载: ```java private ResourceBundle resourceBundle; ...

    (三)Struts2国际化(i18n) :简单语言包的实现

    本篇文章将深入探讨如何在Struts2中实现简单语言包的国际化。 首先,我们需要了解什么是i18n。这里的数字"18"代表了从"i"到"n"之间有18个字母,"i18n"是"internationalization"的缩写。国际化是一种设计和实现软件...

    struts1标签和简单程序(实现了国际化)

    4. **简单程序实现**: - 创建Action类,实现业务逻辑,可能需要继承自`org.apache.struts.action.Action`。 - 编写ActionForm,对应用户输入的数据,并进行数据校验。 - 配置struts-config.xml文件,定义...

    自定义标签实现国际化

    这通常在`web.xml`配置文件中完成,或者使用`ResourceBundle`类动态加载。 3. **自定义标签**:创建一个自定义标签类,该类负责从资源文件中获取相应的文本。标签类需要知道如何根据当前用户的语言环境选择正确的...

    spring mvc简单demo可以运行

    9. **国际化**:通过ResourceBundle和MessageSource,Spring MVC可以轻松实现多语言支持。 10. **AOP(面向切面编程)**:Spring MVC可以与Spring的AOP模块集成,实现日志记录、事务管理等功能。 在"spring mvc...

    国际化示例

    本示例将详细介绍如何在Struts2框架下通过struts2.xml配置文件和.properties资源文件实现国际化功能。 首先,我们需要了解什么是国际化。国际化是一种设计方法,使得软件可以适应不同国家和地区的文化差异,包括...

    javaFX高级教程JavaFX2.0的FXML语言 中文文档

    通过这个简单的示例,我们可以看到 FXML 在创建复杂的用户界面时的强大功能和灵活性。此外,由于 FXML 与 JavaFX 的紧密集成,使得开发者可以轻松地实现动态和交互式用户界面。无论是初学者还是有经验的开发者,都...

    SwingQQ详解(不才之作,敬请欣赏)

    SwingQQ是一个基于Java Swing库实现的桌面应用,旨在模仿QQ的基本功能,提供用户一个简易的聊天体验。这个项目展示了如何使用Swing构建GUI界面,实现用户交互,并且包括了添加、删除和修改联系人与群组的功能。下面...

    Spring3.0就这么简单源码

    - **IoC容器**:Spring的核心,管理对象的生命周期和依赖关系,通过XML或注解配置实现bean的声明式装配。 - **AOP**:提供面向切面编程,用于日志记录、事务管理、权限控制等跨切面关注点的处理。 - **数据访问**...

    tomcat源码

    Tomcat提供了一个简单的JNDI(Java Naming and Directory Interface)实现,允许在Web应用程序中查找和绑定资源。 9. **国际化与本地化**: Tomcat支持多语言环境,通过`ResourceBundle`来实现。 10. **日志系统...

    J2EE企业级项目开发-3期(KC007) 8.2 Struts之数据校验与国际化文档.doc

    Java平台为实现国际化提供了基础支持,包括使用Unicode编码和Locale类来处理多语言环境,以及ResourceBundle类来加载和管理不同地区的资源文件。例如,创建一个名为`applicationResource.properties`的主资源文件,...

    Core-Java-2.-Volume-II.rar_Core Java Volume II_core java II_core

    Java提供了多种API来处理XML,如DOM(文档对象模型)、SAX(简单API for XML)和StAX(流式API for XML)。DOM一次性加载整个XML文档,适合小型文件;SAX是事件驱动的,适用于大文件;StAX允许用户以迭代方式读写XML...

    整合Struts_Hibernate_Spring应用开发详解

    - **让Struts拦截用户请求:** 通过配置`struts-config.xml`文件实现。 - **控制器部分:** `ActionServlet`作为核心控制器,负责调度请求。 - **配置Action:** 在`struts-config.xml`文件中定义Action。 - **...

    Maven SpringMVC

    SpringMVC可以通过ResourceBundle和MessageSource实现国际化,通过ThemeResolver实现主题切换,提供不同语言和风格的用户界面。 9. **SpringMVC与Spring Boot的整合** Spring Boot简化了Spring应用的启动和配置,...

    springmvc+jasperreport解决了中文显示和国际化

    为了实现国际化,JasperReport支持使用资源文件(`.properties`)来定义不同语言的文本。在报表模板中,使用`$R{}`引用这些资源文件中的文本,然后在服务器端根据用户选择的语言加载相应的资源文件。 ```xml ...

Global site tag (gtag.js) - Google Analytics