Chinaunix首页 | 论坛 | 博客
  • 博客访问: 522127
  • 博文数量: 147
  • 博客积分: 10105
  • 博客等级: 上将
  • 技术积分: 1594
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-14 10:18
文章分类

全部博文(147)

文章存档

2011年(4)

2010年(4)

2009年(6)

2008年(5)

2007年(40)

2006年(88)

我的朋友

分类: Java

2006-12-04 10:44:04

Spring IoC 高级特性5
 
ApplicationContext(转)
 
ApplicationContext是BeanFactory的扩展。它提供了根BeanFactory一样的功能,不过减少了和其交互所需要的代码量。它可以替代很多BeanFactory使用的工具类,并可以代替编程人员与这些工具类交流。而且它完全支持以声明方式来配置和管理Spring和Spring所管理的资源。它使用和BeanFactory一样的配置文件。

它还支持BeanFactory不具备的功能:
  • 国际化
  • 事件发布
  • 资源管理和存取
  • 额外的声明周期接口
  • 经过改良的基础性组件自动装配
除非你要找一个真正轻量级的IoC方案,否则无论是应用程序还是Web应用,毫无疑问,就是ApplicationContext。

ApplicationContext的实现
 
ApplicationContext本身也是一个接口。虽然用户也可以自己实现,但是也绝非易事。通常使用如下三个实现。
 
FileSystemXmlApplicationContext:通过这个实现可以家在于文件系统任何地方的配置文件,只要应用程序有访问权限。

ClasspathXmlApplicationContext:载入位于classpath里面的配置,特别是你打算把配置文件和一组类打包成一个JAR文件时,这个类就非常有用。

XmlWebApplicationContext:仅供Web应用环境中使用。借助ContextLoaderListener或者ContextLoaderServlet,可以在你的Web应用中自动加载ApplicationContext配置。
 
使用ApplicationContextAware
 
同BeanFactoryAware一样。这个接口也是让你的bean能够缺的其ApplicaitonContext的引用。此接口只实现了一个方法setApplicationContext()。使用方法同BeanFactoryAware一模一样。读者可自己参考实现。
 
控制Bean的初始化
 
还记得当初为了做那个shutdown hook,我们不得不调用BeanFactory的preInstantiateSingletons()方法吧,只有这样,配置中的bean才能被实例化。现在我们再也不用这样做了。

使用ApplicationContext时,它总会把lazy加载设置为false,意味着,ApplicationContext创建之时,会自动实例化所有配置中的bean。当然如果你要自己设定某个bean是否是懒加载,则可以通过设置bean元素的lazy-init属性。
 
例程代码: P128,代码清单5-42,部分

 class="com.apress.prospring.ch5.interaction.ShutdownHookBean"
 lazy-init="false"/>
 
在lazy-init值为false时,意味着使用该配置的ApplicationContext一创建,就会立即实例化配置中lazy-init值为false的bean。为true时,则使用懒加载,即在此bean在第一次被创建的时候才实例化。
 
利用MessageSource实现国际化
 
ps.现在每学习一个框架就必须要学习一个国际化,有的时候一个框架可能涉及到几个国际化要学习,黑线…………
 
Spring对i18n提供了良好的支持。但是这都是借助于MessageSource来实现。不过不用担心,Applicationcontext扩展了MessageSrouce接口,所以它也可以做国际化的。在进行本节内容之前,最好先看看Locale和ResourceBundle类的JDK文档,反正有了中文版的。不知道地址的访问这里:
 
//////////////////////////////////////////
使用ApplicationContext和MessageSource
//////////////////////////////////////////
 
首先MessageSource也有三种实现。

ResourceBundleMessageSource:使用ResourceBundle载入消息。

ReloadableResourceMessageSource:同前者没有实质不同,它支持定期重新载入。

StaticMessageSource:一般不用在产品级应用中,因为无法从外部对他进行配置。
其实ApplicationContext也是一个MessageSource的实现。
 
上述四种实现的机理:在其实现内部都实现了另一个接口HierarchicalMessageSource,它支持大量MessageSource实例的嵌套。因此,可以通过这一点来使得ApplicationContext和MessageSource共处。
 
使用:为了在ApplicationContext中使用MessageSource,必须在你的bean配置中配置这样一个bean。它的名必须为messageSource,它的实现类必须为MessageSource。
这样ApplicationContext才能使用MessageSource中的资源。
 
例程代码,P130,Listing 5-44,部分
ApplicationContext ctx = new FileSystemXmlApplicationContext(
 "./ch5/src/conf/appContext/messageSource.xml");
Local english = Locale.ENGLISH;
Locale czech = new Locale("cs","CZ");
System.out.println("msg",null,english);
System.out.println("msg",null,czech);
System.out.println("msg",new Object[] {"Rob","Harrop"},english);
 
参看配置,P130,Listing 5-45
 class="org.springframework.context.support.ResourceBundleMessageSource">
 
  
   buttons
   labels
  

 

 
代码解释:

在解释代码之前,先解释以下Java中ResourceBundle是如何处理国际化的问题的。
对于使用不同语言的属性(properties)文件,文件名由两部分组成,一部分称为基础文件名(basename),另一部分称为区域语言文件名,它对应Locale。例如,一个基础文件名是foo,而它所要对应的Locale是en-GB(英国英语),则这个资源绑定所对应的属性(properties)文件的文件名全称应该是,foo_en_GB.properties。
 
接下来先看配置文件,其中使用依赖注入,将一个List注入到basenames属性中去。这个basenames就我们要处理的一组属性文件的基础文件名集合,ResourceBundle会依次根据传入的Locale来查找对应的basename并组合出正确的属性文件名(即那个组合后的文件名所对应的文件中含有要查找的消息),再从这个文件名所对应的文件中找到相应的资源。
回头再看代码。由于ApplicationContext扩展了MessageResource,所以它能够使用后者的getMessage()方法。
 
这个方法有三种重载,

getMessage(String, Object[], Locale)——标准形式

 String对应properties文件中资源的key
 Object[]对应资源中消息的替换文本。例如:My name is {0} {1},中花括号中的0和1就是要被替换的消息,代码中将0和1按照Object[]中的顺序替换为Rob和Harrop。
 Locale对应properties文件的区域和语言设定,就是刚才说过的那个en-GB
 
getMessage(String, Object[], String, Locale)
 其他三个参数都同标准形式,第二个String代表的是万一传入Locale里(的properties文件中)没有传入key对应的消息,那么该参数允许你传入一个缺省值。
 
getMessage(MessageSourceResolvable, Locale)
 这个需要到Spring Web部分才能说明
 
使用时机:
前文说过ApplicationContext并非轻量级,而且它也实现了MessageSource接口。但是对于独立的应用,尽量应该通过依赖注入来使用MessageSource而非ApplicationContext。

因为如果在独立应用(以非Web应用为例)中的Bean应用到ApplicationContext上,就意味着Bean可以使用ApplicationContext所提供的所有服务,而这些对于Bean来说,很多都是无谓的。如果是Web应用,反倒是应该使用ApplicationContext取代MessageSource,但是在独立应用中,这样做无异于让Bean实现了一个ApplicationContextAware接口,在未见可知的好处之前,反而令测试变得复杂。
 
使用应用程序事件
 
作用:ApplicationContext能够将自己作为代理发布和接收事件。
概念解释:
事件——继承自ApplicationEvent的类,ApplicationEvent又继承自java.util.EventObject
监听器——实现了ApplicationListener接口的类,使得任何bean都能监听事件。ApplicationContext还会把实现该接口的bean自动注册为监听者
发布事件——通过ApplicationContext.publishEvent()方法发布事件。
 
实现:Web中,可以通过protected方法访问ApplicationContext,独立应用中,通过实现ApplicationContextAware接口获取ApplicationContext。
 
例程代码:P133,Listing 5-46,部分。
public class MessageEvent extends ApplicationEvent {
 private String msg;
 public MessageEvent(Object source, String msg) {
  super(source);
 this.msg = msg;
 }
 // some operation
}

MessageEvent即是扩展了ApplicationEvent的事件类。
注意,ApplicationEvent只有一个构造函数!
 
P134,Listing 5-47,部分
public class MessageEventListener implements ApplicationListener {
 public void onApplicationEvent(ApplicationEvent event) {
  // some operation
 }
}

MessageEventListener实现了ApplicationListener接口,因此,它会被ApplicationContext自动注册到监听器中。
注意,ApplicationListener只定义了一个方法,就是onApplicationEvent。
 
发布事件,P134,Listing 5-48,部分
// ctx is an instance of ApplicationContext
ctx.publishEvent(new MessageEvent(this, message));
 
最后看配置
 class="com.apress.prospring.ch5.event.Publisher"/>
 class="com.apress.prospring.ch5.event.MessageEventListener"/>

两个类都是自定义类,前者是代码清单5-48所指的类,后者是事件监听器,都没有什么特别的地方。但是ApplicationContext会自动注册监听器。
 
Event使用注意事项
 
重构原文的别扭话就是:从前,有个一个更新组件可以发出更新事件,有个缓存组件可以缓存数据。一天更新组件在代码中出现了“缓存组件.更新()”这样的耦合代码,所以更新组件耦合了缓存组件。为了解耦,我们让更新组件做出“更新组件.发布更新消息()”的动作——>生成了“事件”——>让缓存组件执行“缓存组件.接受事件(事件)”的动作。这样在更新和缓存之间就因为多了个第三者——事件,于是,二者解耦了……
 
末了还有交代说:
一般而言,对于那些迅速执行且不是应用程序主要逻辑组成部分的反作用逻辑,应采用事件。对于长期运行且已经成为主业务逻辑一部分的进程,使用JMS或者消息系统(如MSMQ)。
 
访问资源
 
作用:让你的应用程序可以以一致的方式访问文件资源,而无论这些资源是存储在哪里或者以什么方式储存的。

实现:使用Resource接口进行编程。(Spring处处体现针对接口编程啊)
此接口定义了7个自解释方法:exists(),getDescription(),getFile(),getFileName(),geInputStream(),getURL()isOpen()。另外还有一个createRelative()方法,此方法使用调用它/的/实例的/相对路径来创建Resource实例。(声明,本人非赵丽华赵老湿的追随者)
 
以上是外部实现,内部来说是通过ResourceLoader接口及其缺省实现DefaultResourceLoader来定位和创建Resource资源的。(内部实现就是说我们根本不用它,只有Spring用它)

不过对我们来说ResourceLoader的另外一个实现——ApplicationContext搞定一切!
 
例程代码:P136,Listing 5-50
// this code shows 3 type of protocal
// ctx is an instance of ApplicationContext
Resource res1 = ctx.getResource("");
Resource res2 = ctx.getResource("classpath:lib/commons-logging.jar");
Resource res3 = ctx.getResource("");
// some operation
res2.getURL().getContent();
 
首先声明,关于file:协议,后面到盘符前确实是三个"/",测试通过。
 
注意,其中“classpath:”协议为Spring专有。对于file协议和http协议,Spring返回一个UrlResource实例。而且建议使用getInputStream()来访问资源,因为它几乎是万能的……
阅读(815) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~