spring中MessageSource的配置使用方法

来源:互联网 发布:淘宝宝贝评分查询 编辑:程序博客网 时间:2024/06/05 08:20

转  http://blog.csdn.net/qyf_5445/article/details/8124306

Spring定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类。首先来了解一下该接口的几个重要方法:
  String getMessage(String code, Object[] args, String defaultMessage, Locale locale) 

code表示国际化资源中的属性名;

args用于传递格式化串占位符所用的运行期参数;当在资源找不到对应属性名时,返回defaultMessage参数所指定的默认信息;

locale表示本地化对象;

  String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException
与上面的方法类似,只不过在找不到资源中对应的属性名时,直接抛出NoSuchMessageException异常;
  String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException

MessageSourceResolvable 将属性名、参数数组以及默认信息封装起来,它的功能和第一个接口方法相同。

MessageSource的类结构
MessageSource分别被HierarchicalMessageSource和ApplicationContext接口扩展,这里我们主要看一下HierarchicalMessageSource接口的几个实现类,如图5-7所示。

                                             图5-7  MessageSource类图结构
HierarchicalMessageSource接口添加了两个方法,建立父子层级的MessageSource结构,类似于前面我们所介绍的HierarchicalBeanFactory。

该接口的setParentMessageSource (MessageSource parent)方法用于设置父MessageSource,而getParentMessageSource()方法用于返回父MessageSource。
HierarchicalMessageSource接口最重要的两个实现类是ResourceBundleMessageSourceReloadableResourceBundleMessageSource。它们基于Java的ResourceBundle基础类实现,允许仅通过资源名加载国际化资源。

ReloadableResourceBundleMessageSource提供了定时刷新功能,允许在不重启系统的情况下,更新资源的信息。

StaticMessageSource主要用于程序测试,它允许通过编程的方式提供国际化信息。

DelegatingMessageSource是为方便操作父MessageSource而提供的代理类。
ResourceBundleMessageSource该实现类允许用户通过beanName指定一个资源名(包括类路径的全限定资源名),或通过beanNames指定一组资源名。

在代码清单5-15中,我们通过JDK的基础类完成了本地化的操作,下面我们使用ResourceBundleMessageSource来完成相同的任务。读者可以比较两者的使用差别,并体会Spring所提供的国际化处理功能所带给我们的好处:

启动Spring容器,并通过MessageSource访问配置的国际化资源,如代码清单 5 17所示:

比较代码清单5-15中的代码,我们发现最主要的区别在于我们无须再分别加载不同语言、不同国家/地区的本地化资源文件,仅仅通过资源名就可以加载整套的国际化资源文件。此外,我们无须显式使用MessageFormat操作国际化信息,仅通过MessageSource# getMessage()方法就可以完成操作了。这段代码的运行结果与代码清单5 15的运行结果完全一样。
ReloadableResourceBundleMessageSource
前面,我们提到该实现类比之于ResourceBundleMessageSource的唯一区别在于它可以定时刷新资源文件,以便在应用程序不重启的情况下感知资源文件的变化。很多生产系统都需要长时间持续运行,系统重启会给运行带来很大的负面影响。这时,通过该实现类就可以解决国际化信息更新的问题。请看下面的配置:

在上面的配置中,我们通过cacheSeconds属性让ReloadableResourceBundleMessageSource每5秒钟刷新一次资源文件(在真实的应用中,刷新周期不能太短,否则频繁的刷新将带来性能上的负面影响,一般不建议小于30分钟)。cacheSeconds默认值为-1表示永不刷新,此时,该实现类的功能就蜕化为ResourceBundleMessageSource的功能。
我们编写一个测试类对上面配置的ReloadableResourceBundleMessageSource进行测试:
在①处,我们让程序睡眠20秒钟,在这期间,我们将fmt_resource_zh_CN.properties资源文件的greeting.common键值调整为:
---How are you!{0},today is {1}---
我们将看到两次输出的格式化信息分别对应更改前后的内容,也即本地化资源文件的调整被自动生效了:
How are you!John,today is 1/9/07 4:55 PM
---How are you!John,today is 1/9/07 4:55 PM---


转  http://blog.csdn.net/liuya1985liuya/article/details/1399817


分析ApplicationContext
    Spring的bean包支持通过编码方式管理和操作bean的基本功能,ApplicationContext
则以Framework的方式提供BeanFactory的所有功能。使用ApplicationContext,你可以让
系统加载你的bean(例如,在Servlet容器初始化ContextLoaderServlet时,通过
ContextLoader类加载Spring Framework),而不是使用编码方式来加载。
    ApplicationContext接口是context包的基础,位于
org.springframework.context包里,提供了BeanFactory的所有功能。除此之外, 
ApplicationContext为了支持Framework的工作方式,提供了以下的功能:
    l.MessageSource,提供了语言信息的国际化支持
    2.提供资源(如URL和文件系统)的访问支持
    3.为实现了ApplicationListener接口的bean提供了事件传播支持
    4.为不同的应用环境提供不同的context,例如支持web应用的XmlWebApplicationContext类

    下面的源代码分析主要集中在ApplicationContext接口特有的功能上,如国际化支持,
资源访问和bean的事件传播。

我的问题
    现在我的问题是,ApplicationContext是如何实现上面提到的功能的?下面的分析
将作出回答。

准备测试用例
    1. 首先在类路径根目录编写测试国际化支持的testmsg.xml,并将它加入Spring IDE
的管理范围:
    <beans>
        <bean id="messageSource" 
      class="org.springframework.context.support.ResourceBundleMessageSource">
          <property name="basenames">
              <list>
                  <value>message</value>
              </list>
          </property>
        </bean>
    </beans>

    2. 编写测试用例,测试国际化支持和资源访问的功能。
      public class MsgTest extends TestCase {
          ApplicationContext ctx = null;
          public MsgTest(String arg0) {
              super(arg0);
          }
          protected void setUp() throws Exception {
              super.setUp();
              ctx = new FileSystemXmlApplicationContext("testmsg.xml");
          }
          public void testMessageResource() {
              Object[] args = {"我", "你"};
              String msg = ctx.getMessage("hello", args, Locale.CHINA);
              //System.out.println("msg=" + msg);
              assertEquals("我和你", msg);
              Resource rs = ctx.getResource("classpath:log4j.properties");
              assertTrue(rs.exists());
          }
      }

    3. 在类路径根目录创建属性文件message.properties,内容为hello={0}和{1}。

    此时运行TestCase,果然不出所料,Junit视图的测试状态是红条。将打印msg变量的
语句打开,重新测试,发现"和"字是乱码。
    在message.properties文件中将"和"字改为ASCII码/u548c,重新运行TestCase,
绿条,测试通过。
    将testmsg.xml中bean的id改为messageSource1,重新运行测试,出现红条,
测试失败,说明bean的名称必须是messageSource,这点值得注意。至于其中的原因稍后
说明。

    ApplicationContext接口通过继承BeanFactory,MessageSource和ResourceLoader
三个接口,分别支持管理和操作bean的功能,语言信息的国际化支持以及对资源访问的支持。
    AbstractApplicationContect是ApplicationContext的抽象实现类,它的继承层次较
为紊乱,我觉得这里应该进行代码重构。AbstractXmlApplicationContext是
AbstractApplicationContext的子类,提供了对XML配置文件的支持,它有三个子类,分别
用于不同的应用环境。
    对于MessageSource,Spring提供了两个bean实现,ResourceBundleMessageSource和
ReloadableResourceBundleMessageSource。前者提供了访问Properties文件的支持,后者
添加了无需重启JVM,重新加载Properties文件的支持。
ApplicationContext的国际化和资源访问支持
    1. 如类层次图所示,在我们的例子中,FileSystemXmlApplicationContext使用
DefaultListableBeanFactory装载和解释testmsg.xml配置文件(参见代码分析的
BeanFactory部分)。

    2. FileSystemXmlApplicationContext根据配置文件的BeanDefinition创建
ResourceBundleMessageSource,加载<list>元素定义的Properties文件,并保存在
AbstractApplicationContext的属性中。当客户程序调用getMessage方法时,
AbstractApplicationContext调用ResourceBundleMessageSource的getMessage方法返回
Message信息。

    3. 至于上节提到的MessageSource的id只能是messageSource,是因为
AbstractApplicationContext的initMessageSource()方法中,有这样一段代码:
            this.messageSource = (MessageSource) 
            getBean(MESSAGE_SOURCE_BEAN_NAME);
      其中MESSAGE_SOURCE_BEAN_NAME的定义为:
            static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
      原因找到了,其实只要稍做代码重构,即可消除这个缺陷。
    4. 如类层次图所示,AbstractApplicationContext继承了DefaultResourceLoader,
当客户程序调用getResource方法时,使用父类中实现的方法来处理。

ApplicationContext的事件传播
准备测试用例
    1. 首先编写测试用例。
      public class SenderBeanTest extends TestCase {
          ApplicationContext ctx = null;
          protected void setUp() throws Exception {
            super.setUp();
            ctx = new FileSystemXmlApplicationContext("testlistener.xml");
          }
          public void testSendEmail() {
            SenderBean sender = (SenderBean)ctx.getBean("sender");
            String msg = sender.sendMessage("test message");
            assertEquals("test message", msg);
          }
      }
    2. 接着编写testlistener.xml配置文件。
      <beans>
          <bean id="sender" class="unittest.SenderBean"/>
          <bean id="listener" class="unittest.MessageListener"/>
      </beans>
    3. 最后编写SenderBean,MessageListener和MessageEvent类。
      public class SenderBean implements ApplicationContextAware {
            private ApplicationContext applicationContext;
            public void setApplicationContext(ApplicationContext applicationContext)
                  throws BeansException {
                this.applicationContext = applicationContext;
            }
            public String sendMessage(String msg) {
                MessageEvent event = new MessageEvent(msg);
                this.applicationContext.publishEvent(event);
                return msg;
            }
        }

        public class MessageListener implements ApplicationListener {
            public void onApplicationEvent(ApplicationEvent event) {
                if (event instanceof MessageEvent) {
                    System.out.println("I got the message:" + event.getSource());
                }
            }
        }

        public class MessageEvent extends ApplicationEvent {
            public MessageEvent(Object source) {
                super(source);
                System.out.println(this.getTimestamp() + ":" + source);
            }
        }
      运行测试案例SenderBeanTest,绿条,测试通过。Console窗口出现以下DEBUG
信息:
      ……
      796 DEBUG support.DefaultListableBeanFactory - Returning 
cached instance of singleton bean 'sender'
      1085553911796:test message
      796 DEBUG support.FileSystemXmlApplicationContext - Publishing event 
in context [org.springframework.context.support.FileSystemXmlApplicationContext;hashCode=13549765]: 
unittest.MessageEvent[source=test message]
      I got the message:test message


事件传播的实现
    1. FileSystemXmlApplicationContext的构造器调用AbstractApplicationContext的
refresh方法。如图所示,refresh方法调用refreshListeners方法。
    2. AbstractApplicationContext的refreshListeners方法使用BeanFactory的
getBeanOfType方法得到所有ApplicationListener类(本例中是MessageListener),并使用
addListener方法把它们都放入ApplicationEventMulticasterImpl的Set容器中
(eventListeners变量)。
    3. 如图所示,SenderBean实现ApplicationContextAware接口,并通过
setApplicationContext方法注入ApplicationContext对象实例。
    4. 当调用SenderBean类sendMessage方法时,AbstractApplicationContext调用
publishEvent方法。
    5. AbstractApplicationContext类的publishEvent方法调用
ApplicationEventMulticasterImpl类的onApplicationEvent方法。
    6. ApplicationEventMulticasterImpl通知Set容器中所有的ApplicationListener
对象,调用它们的onApplicationEvent方法。

    从以上的过程可以看出,ApplicationContext将事件通知所有的
ApplicationListener。如果ApplicationListener的子类(如MessageListener)只想接受
指定的事件类型,需要自己编写过滤代码,如例子中的if (event instanceof MessageEvent)。



转  http://meiyx.iteye.com/blog/1028589


.理论简要:ApplicationContext接口扩展了MessageSource 接口,因而提供了消息处理的功能(i18n或者国际化)。与HierarchicalMessageSource一起使用,它还能够处理嵌套的消息,这些是Spring提供的处理消息的基本接口。让我们快速浏览一下它所定义的方法: 

     · String getMessage(String code, Object[] args, String default, Locale loc):用来从MessageSource获取消息的基本方法。如果在指定的locale中没有找到消息,则使用默认的消息。args中的参数将使用标准类库中的MessageFormat来作消息中替换值。 

     · String getMessage(String code, Object[] args, Locale loc):本质上和上一个方法相同,其区别在:没有指定默认值,如果没找到消息,会抛出一个NoS hMessageException异常。 

     · String getMessage(MessageSourceResolvable resolvable, Locale locale):上面方法中所使用的属性都封装到一个MessageSourceResolvable实现中,而本方法可以指定 MessageSourceResolvable实现。 

当一个ApplicationContext被加载时,它会自动在context中查找已定义为MessageSource类型的bean。此bean的名称须为messageSource。如果找到,那么所有对上述方法的调用将被委托给该 bean。否则ApplicationContext会在其父类中查找是否含有同名的bean。如果有,就把它作为MessageSource。如果它最终没有找到任何的消息源,一个空的StaticMessageSource将会被实例化,使它能够接受上述方法的调用。 

Spring目前提供了两个MessageSource的实现:ResourceBundleMessageSource和StaticMessageSource。它们都继承 NestingMessageSource以便能够处理嵌套的消息。StaticMessageSource很少被使用,但能以编程的方式向消息源添加消息。ResourceBundleMessageSource会用得更多一些, 

6. 更多的资料参考spring官方开发手册,很详尽的! 

0 0
原创粉丝点击