Spring容器,顾名思义是用来容纳东西的,装的就是Bean。Spring容器负责创建、配置、管理Bean。spring容器有两个核心接口:BeanFactory和ApplicationContext接口,后者是前者的子接口。在基于spring的Java EE程序中,所有的组件都被当成Bean来处理,包括数据源对象、hibernate的sessionFactory、事务管理等,程序中的所有Java类都可以被当成spring容器中的bean。
1、spring容器
spring容器的核心接口是BeanFactory,它有一个子接口就是ApplicationContext。ApplicationContext也被称为spring上下文。
调用者只需要使用getBean()方法即可获得指定bean的引用。对于大部分的Java程序而言,使用ApplicationContext作为spring容易更为方便。其常用的实现类有FileSystemXmlApplicationContext、ClassPathXmlApplicationContext和AnnotationConfigXmlApplicationContext。如果Java web中使用spring容器,则通常有XmlWebApplicationContext、AnnotationConfigWebApplicationContext两个容器。
创建spring容器的实例时,必须提供spring容器管理的bean的配置文件,也就是我们常说的spring.xml配置文件。因此在创建beanFactory时配置文件作为参数传入。xml配置文件一般以resource对象传入。resource是spring提供的资源访问接口,通过该接口spring更简单、透明的访问磁盘,网络系统和类路径上的相关资源。
对于独立的Java EE应用程序,可以通过如下方法来实例化BeanFactory。
//在当前项目类路径下搜索配置文件
ApplicationContext appContext = new ClassPathXmlApplicationContext("beans_7_3_3.xml");
//在文件系统搜索配置文件
appContext = new FileSystemXmlApplicationContext("D:\\spring-tool-workspace\\myspring\\src\\beans_7_3_3.xml");
//获取chinese的Bean,并且返回的类型为Chinese
Person chinese = appContext.getBean("chinese", Chinese.class);
chinese.useAxe();
2、使用ApplicationContext
大部分时间,都不会使用beanFactory实例作为spring容器,而是使用ApplicationContext作为spring容器,因此spring容器也被称为spring上下文。ApplicationContext增强了beanFactory的功能,提供了很多有用、方便开发的功能。
在web中可以利用如contextLoader的支持类,在web应用启动的时候自动创建ApplicationContext。
除了提供beanFactory所支持的全部功能外,application还额外的提供如下功能:
① ApplicationContext会默认初始化所有的singleton bean(单例bean),也可以通过配置取消。
② ApplicationContext继承了messageSource接口,因此提供国际化支持。
③ 资源访问,比如URL和文件。
④ 事件机制。
⑤ 同时加载多个配置文件。
⑥ 以声明式方式启动并创建spring容器。
ApplicationContext包括beanFactory的所有功能,并提供了一些额外的功能,优先使用ApplicationContext。对于在内存消耗的才使用beanFactory。
当系统创建ApplicationContext容器时,会默认初始化singleton bean,包括调用构造器创建该bean的实例,通过元素驱动spring调用setting方法注入所依赖的对象。这就意味着,系统前期创建ApplicationContext会有很大的开销,但是一旦初始化完成后面获取bean实例就会拥有较好的性能。为了阻止在使用ApplicationContext作为spring容器初始化singleton bean可以在元素添加lazy-init="true"属性。
3、ApplicationContext的国际化支持
ApplicationContext接口继承了MessageSource接口,因此具备国际化功能。
//MessageSource接口提供的国际化的两个方法
String getMessage(String code, Object [] args, Locale loc){
}
String getMessage(String code, Object[]args, String default, Locale loc){
}
spring国际化的支持,其实是建立在Java国际化的基础上的。其核心思路将程序中需要国际化的消息写入资源文件,而代码中仅仅使用国际化信息响应的key。
4、ApplicationContext的事件机制
ApplicationContext的事件机制是观察者设计模式的实现。通过ApplicationEvent和ApplicationListener接口实现,前者是被观察者,后者是观察者。
spring事件框架有两个核心的接口:
ApplicationEvent(事件):必须由ApplicationContext来发布。
ApplicationListener(监听器):实现了此接口就可以担任容器中的监听器bean。
实际上,spring的事件机制是由事件(实现ApplicationEvent接口的类)、事件源(也就是spring容器,并且有Java代码显示的触发)、监听器(ApplicationListener接口实现类)。这就像我们在页面点击一个button。button是事件源,单机的这个动作就是事件,处理函数就是监听器。
以下代码演示spring事件机制:
import org.springframework.context.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;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
http://www.senta7.net/content/?729.html
public String getText(www.yacuangyl.com) {
return text;
}
public void setText(String text) {
this.text = text;
}
}
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EmailNotifier implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
//处理email事件
if(event instanceof www.xcdeyiju.com EmailEvent){
EmailEvent email = (EmailEvent) event;
System.out.println(email.getAddress()+" "+email.getText());
}else {
//输出spring容器的内置事件
System.out.println("其它事件:"+event);
}
}
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans_7_4_4.xml");
EmailEvent emailEvent = applicationContext.getBean("emailEvent",EmailEvent.class);
applicationContext.publishEvent(emailEvent);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.jujinyule.com .org/schema/beans"
xmlns:xsi="http://www.yuchengyulegw.com 2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.hnawesm.com org/schema/beans/spring-beans.xsd">
<bean class="EmailNotifier"></bean>
<bean id="emailEvent" class="EmailEvent">
<constructor-arg value="test"></constructor-arg>
<constructor-arg value="123@qq.com"></constructor-arg>
<constructor-arg value="this is a test">www.seocelve.com</constructor-arg>
</bean>
</beans>
从上面的代码可以看出,事件监听器不仅监听到了我们程序显示触发的事件,还监听了spring容器内置的事件。如果实际开发需要,我们可以在spring容器初始化或销毁时回调自定义方法,就可以通过上面的事件监听机制来完成。
spring提供了如下几个内置对象:
ContextRefreshedEvent、ContextStartedEvent、ContextClosedEvent、ContextStoppedEvent、RequestHandledEvent。
5、让bean获取spring容器
上面都是通过ApplicationContext创建spring容器,再调用spring容器的getBean()方法获取bean。这种情况下,程序总是持有spring容器的引用。但是在web应用中,我们可以用声明式的方法来创建spring容器:在web.xml文件中配置一个监听,让这个监听类帮我们来创建spring容器,前端MVC框架直接调用bean,使用依赖注入功能,无需访问spring容器本身。
在某些特殊情况下,bean需要实现某个功能(比如:bean需要输出国际化信息,或向spring容器发布事件),这些功能都需要借助spring容器来完成。就是说我们需要将spring容器作为一个bean来注入到其它bean中,只不过spring容器bean是一个容器级别的bean。
为了让bean获取它所在容器的引用,可以让bean实现beanFactoryAware接口。该接口只有一个方法setBeanFactory(BeanFactory beanFactory)方法,方法的beanFactory参数指向spring容器,会由spring容器注入。我们bean中定义一个setter方法后,通常都是由在配置文件中配置元素来驱动spring容器来注入依赖bean的,但是这里我们并没有这样做,这是因为一个bean如果实现了beanFactory接口,spring在创建该bean时,会自动注入spring容器本身。与beanFactoryAware接口类似的还有BeanNameAware、ResourceLoaderAware接口,这些接口都会提供类似的setter方法,这些方法会由spring容器来注入。
下面我们来演示一个示例,这个示例中的Person类中的sayHi()方法将输出国际化消息,这就需要Person获取Spring容器,借助Spring容器来完成国际化。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messagSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages/message</value>
</list>
</property>
</bean>
<bean id="person" class="com.container.Person">
</bean>
</beans>
package com.container;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Locale;
public class Person implements ApplicationContextAware{
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void sayHello(String name){
Locale locale = Locale.getDefault(Locale.Category.FORMAT);
String myName = applicationContext.getMessage("name",new String[]{name},Locale.US);
System.out.println(myName);
}
public static void main(String[] args) {
System.out.println(Person.class.getClassLoader().getResource(".").getPath());
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans_7_4_5.xml");
Person p = applicationContext.getBean("person",Person.class);
p.sayHello("江疏影");
}
}
message_zh_CN.properties国际化资源文件
name=CH \u4F60\u597D,{0}
message_en_US.properties国际化资源文件