Spring有两个核心接口:BeanFactory和ApplicationContext。其中ApplicationContext是BeanFactory的子接口。它们可以代表Spring容器,Spring容器是生成Bean实例的工厂,并管理容器中的Bean。在基于Spring的JavaEE应用中,所有组件都被当成Bean处理,包括数据源、Hibernate的SessionFactory、事务管理器等。Spring容器还负责管理Bean与Bean之间的依赖关系。
BeanFactory
BeanFactory是Spring最基本的接口,它负责配置、创建、管理Bean,它有一个子接口ApplicationContext,也被称为上下文。BeanFactory接口包含如下几个基本方法:
调用者只需使用getBean()方法即可获得指定Bean的引用,无须关心Bean的实例化过程。Bean实例的创建、初始化以及依赖关系的注入都由Spring容器完成。
BeanFactory常用实现类是DefaultListableBeanFactory,ApplicationContext是BeanFactory的子接口,大部分JavaEE应用,使用ApplicationContext作为Spring容器,其常用实现类是FileSystemXmlApplicationContext、ClassPathXmlApplicationContext和AnnotationConfigApplicationContext。如果在Web应用中使用Spring容器,则通常有XmlWebApplicationContext、AnnotationConfigWebApplicationContext两个实现类。
创建Spring实例时,必须提供Spring容器管理的Bean的详细配置信息。Spring的配置信息通常采用XML配置文件来配置,因此创建BeanFactory时,用该提供XML配置文件作为参数。XML配置文件通常使用Resource对象传入。
Resource接口是Spring提供的资源访问接口,通过使用该接口,Spring能访问磁盘、类路径和网络上的资源。
大部分JavaEE应用,可以在启动Web应用时自动加载ApplicationContext实例,接受Spring管理的Bean无须知道ApplicationContext的存在,一样可以利用ApplicationContext的管理。
ApplicationContext
大部分时候,不使用BeanFactory实例作为Spring容器,而是使用ApplicationContext实例作为容器,所以也把Spring容器称为上下文。ApplicationContext是BeanFactory接口的子接口,它增强了BeanFactory的功能。
ApplicationContext允许以声明式方式操作容器,无须手动创建它。可利用如ContextLoader的支持类,在Web应用启动时自动创建ApplicationContext。也可采用编程方式创建ApplicationContext。除了拥有BeanFactory所支持的所有功能以外,ApplicationContext还有如下额外功能:
- ApplicationContext默认会初始化所有的Singleton Bean,可通过配置取消初始化。
- ApplicationContext继承MessageSource接口,因此提供国际化支持。
- 同时加载多个配置文件。
- 以声明方式启动并创建Spring容器。
ApplicationContext包括BeanFactory所有功能,一般建议使用ApplicationContext,除非对于某些内存非常关键的应用,才考虑使用BeanFactory。
当系统创建ApplicationContext容器时,默认会预初始化所有的Singleton Bean。即当ApplicationContext容器初始化完成后,容器会自动初始化所有的singleton Bean,包括调用构造器创建该Bean的实例,并根据< property…/>元素执行setter方法。这意味着系统前期创建ApplicationContext时将有较大的系统开销,但一旦ApplicationContext初始化完成,程序后面获取Singleton Bean时将拥有较好的性能。
<bean id="Chinese" class="com.afy.spring.beans.Person">
<property name="test" value="猪八戒"></property>
</bean>
这里配置了一个Chinese Bean,没有特别设置的话,该Bean就是Singleton Bean,ApplicationContext会在容器初始化完成后,自动调用Person类的构造器创建Chinese Bean,并以“猪八戒”作为传入参数去调用Chinese Bean的setTest()方法。
程序中的Person类代码如下:
public class Person{
public Person(){
System.out.println("==正在执行Person无参构造器==");
}
public void setTest(String name){
System.out.println("正在调用setName()方法,传入参数:"+name);
}
}
即使主程序只有一行代码:
//创建Spring容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
上面代码只是使用ApplicationContext创建了Spring容器,ApplicationContext会自动预初始化容器中的Chinese Bean——包括调用它的无参构造器,根据< property…/>元素执行setter方法,执行上面代码后输出:
==正在执行Person无参构造器==
正在调用setName()方法,传入参数:猪八戒
使用BeanFactory做Spring容器则不会预初始化容器中的Bean,如果要阻止ApplicationContext预初始化容器中的singleton Bean,可以为< bean…/>元素指定lazy-init=”true”。
ApplicationContext通常的实现:
FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供它的构造函数。
ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里需要正确设置classpath因为这个容器将在classpath里找bean配置。
WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。
Bean 工厂和 Application contexts 的区别:
Application contexts提供一种方法处理文本消息,一个通常的做法是加载文件资源(比如镜像),它们可以向注册为监听器的bean发布事件。另外,在容器或容器内的对象上执行的那些不得不由bean工厂以程序化方式处理的操作,可以在Application contexts中以声明的方式处理。Application contexts实现了MessageSource接口,该接口的实现以可插拔的方式提供获取本地化消息的方法。
ApplicationContext的国际化支持
ApplicationContext继承了MessageSource接口,因此具有过国际化功能。MessageSource接口中定义的用于国际化的方法有:
当程序创建ApplicationContext容器时,Spring自动查找配置文件中名为 messageSource的Bean实例,一旦找到这个Bean实例,上述两个方法的调用就委托给messageSource Bean.如果没有messageSource Bean,ApplicationContext会找其父容器中的messageSource Bean,如果找到将以此为messageSource使用。如果无法找到messageSource Bean,系统会创建一个空的StaticMessageSource Bean,该Bean能接受上述两个方法的调用。
在Spring中配置 messageSource Bean时通常使用ResourceBundleMessageSource类,看下面的配置文件:
然后给出两个资源文件:
第一份为美式英语的资源文件,文件名为:message_en_US_.properties
hello=welcome,{0}
now=now is:{0}
第二份为简体中文的资源文件,文件名为:message.properties
hello=欢迎你,{0}
now=现在时间是:{0}
由于包含非西欧文字,所以使用native2ascii工具将这份资源文件国际化,命令为:
native2acsii message.properties message_zh_CN.properties
执行上面命令后,将看到系统会多出一个message_zh_CH.properties文件,该文件为简体中文实际的国际化文件。此时,程序拥有了两份资源文件,可以自适应美式英语和简体中文的环境。主程序部分代码如下:
public class SpringTest{
public static void main(String[] args)throws Exception{
// 实例化ApplicationContext
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
// 使用getMessage()方法获取本地化消息。
// Locale的getDefault方法返回计算机环境的默认Locale
String hello = ctx.getMessage("hello" , new String[]{"孙悟空"}, Locale.getDefault(Locale.Category.FORMAT));
String now = ctx.getMessage("now" , new Object[]{new Date(), Locale.getDefault(Locale.Category.FORMAT));
// 打印出两条本地化消息
System.out.println(hello);
System.out.println(now);
}
}
上面程序的执行结果会因环境不同而改变,在简体中文环境下,执行结果如下:
欢迎你,孙悟空
现在时间是14-5-10 下午 3;27
在美国英语环境下,执行结果如下:
welcome:孙悟空
now is :5/10/14 3:27 PM
即使在英文环境下,“孙悟空”这个词也无法变成中文,因为这三个字是写在程序代码中,而不是从资源文件中获得的。
Spring国际化的支持,是建立在Java国际化的基础上的。核心思路是将程序需要国际化的信息写入资源文件,而代码中仅仅使用相应的各信息的Key。
ApplicationContext的事件机制
ApplicationContext的事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext的事件处理。如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布了ApplicationEvent时,ApplicationListener Bean将自动被触发。Spring的事件框架有如下两个主要成员:
1. ApplicationEvent:容器事件,必须由ApplicationContext发布。
2. ApplicationListener:监听器,可由容器中的任何监听器Bean担任。
Spring的事件机制和所有的事件机制基本相似,它们都由事件源、事件和事件监听器组成。只有此处的事件源是ApplicationContext,且事件必须由Java程序显式触发。下图给出了Spring容器的事件机制示意图:
下面将在程序中示例Spring容器的事件机制。程序先定义一个ApplicationEvent类,其对象就是一个Spring容器事件。ApplicationEvent类的代码如下:
public class EmailEvent extends ApplicationEvent{
private String address;
private String text;
public EmailEvent(Object source){
super(source);
}
// 初始化全部成员变量的构造器
public EmailEvent(Object source , String address , String text){
super(source);
this.address = address;
this.text = text;
}
// address的setter和getter方法
public void setAddress(String address){
this.address = address;
}
public String getAddress(){
return this.address;
}
// text的setter和getter方法
public void setText(String text){
this.text = text;
}
public String getText(){
return this.text;
}
}
上面的EmailEvent类继承了ApplicationEvent抽象类,除此之外,它便是一个普通的Java类。当一个普通的Java类继承了ApplicationEvent基类,该对象就可作为Spring容器的容器事件。
容器事件的监听器类必须实现ApplicationListener接口,实现该接口必须实现onApplicationEvent(ApplicationEvent event)方法,每当容器内发生任何事件时此方法都触发。下面是容器监听器类代码:
public class EmailNotifier implements ApplicationListener{
// 该方法会在容器发生事件时自动触发
public void onApplicationEvent(ApplicationEvent evt){
// 只处理EmailEvent,模拟发送email通知...
if (evt instanceof EmailEvent){
EmailEvent emailEvent = (EmailEvent)evt;
System.out.println("需要发送邮件的接收地址 "
+ emailEvent.getAddress());
System.out.println("需要发送邮件的邮件正文 "
+ emailEvent.getText());
}
else{
// 其他事件不作任何处理
System.out.println("其他事件:" + evt);
}
}
}
将监听器配置在容器中:
<bean class="com.afy.ssh.spring.EmailNotifier"/>
为Spring容器注册事件监听器只要进行简单配置,只要在Spring中配置一个实现了ApplicationListener接口的Bean,Spring容器就会把这个Bean当成容器事件的事件监听器。
当系统创建Spring容器、加载Spring容器时会自动触发容器事件,容器事件监听去可以监听到这些事件。此外,程序可以调用ApplicationContext的pulishEvent()方法来主动触发容器事件:
public class SpringTest{
public static void main(String[] args){
ApplicationContext ctx = new
ClassPathXmlApplicationContext("beans.xml");
// 创建一个ApplicationEvent对象
EmailEvent ele = new EmailEvent("test" ,
"spring_test@163.com" , "this is a test");
// 发布容器事件
ctx.publishEvent(ele);
}
}
上面代码创建了ApplicationEvent对象,并通过ApplicationContext主动触发了该事件。运行结果:
[java]其他事件……
[java]需要发送邮件的接受地址 spring_test@163.com
[java]需要发送邮件的邮件正文 this is a test
监听器不仅监听到程序触发的事件,也监听到容器内置的事件。如果开发者需要在Spring容器初始化、销毁时回调自定义方法,就可以通过上面的事件监听器来实现。