首页 > 代码库 > Spring-----4、使用Spring容器

Spring-----4、使用Spring容器

               Spring有两个核心接口:BeanFactoryApplicationContext(BeanFactory的子接口);他们都可代表Spring容器,Spring容器是生成Bean实例的工厂,并管理容器中的Bean;Bean是Spring管理的基本单位,在基于Spring的JavaEE应用中,所有的组件都被当成Bean处理,包括数据源、Hibernate的SesisonFactoy、事务管理器等

 

               应用中的所有组件,都处于Spring的管理下,都被Spring以Bean的方式管理,Spring负责创建Bean实例,并管理其生命周期

 

               Spring容器负责创建Bean实例,所以需要知道每个Bean的实现类,Java程序面向接口编程,无须关心Bean实例的实现类;但Spring容器必须能精确知道每个Bean实例的实现类,因此Spring配置文件必须精确配置Bean实例的实现类

 

一、Spring容器

               Spring容器最基本的接口就是BeanFactory:负责配置、创建、管理Bean;负责管理Bean与Bean之间的依赖关系;其实现类有XmlBeanFactory;其子类ApplicationContext的实现类有ClassPathXmlApplicationContextFileSystemXmlApplicationContextAnnotationConfigApplicationContext;在Web应用中使用Spring容器,通常有XmlWebApplicationContext、AnnotationConfigApplicationContext

      

               创建Spring容器实例时,必须提供Spring容器管理的Bean详细配置信息。创建BeanFactory实例时,应提供XML配置文件作为参数。XML配置文件通常使用Resource对象传入

      

               BeanFactory接口常用方法:   

               boolean containsBean(String beanId):判断Spring容器是否包含id为beanId的Bean实例

               <T> T getBean(Class<T> requiredType):获取Spring容器中属于requiredType类型的、唯一的Bean实例

               Obejct getBean(String beanId):返回容器id为beanId的Bean实例

               <T> T getBean(String beanId,Class<T> requiredType):返回容器id为beanId,并类型为requiredType的Bean

               Class<?> getType(String beanId):返回容器中指定Bean实例的类型

      

对于独立的应用程序,可通过如下方法实例化BeanFactory:
// 搜索当前文件路径下的beans.xml文件创建Resource对象
InputStreamResource isr = new FileSystemResource(“bean.xml”);
// 以Resource对象作为参数,创建BeanFactory实例
XmlBeanFactory factory = new XmlBeanFactory(isr);
或
// 搜索类加载路径,以类加载路径下的beans.xml文件创建Resource对象
ClassPathResource res = new ClassPathResource(“beans.xml”);
// 以Resource对象为参数,创建BeanFactory实例
XmlBeanFactory factory = new XmlBeanFactory(res);

应用中有多个属性配置文件,应采用BeanFcatory的子接口ApplicationContext来创建BeanFactory的实例
ApplicationContext常用实现类:
FileSystemXmlApplicationContext:以基于文件系统的XML配置文件创建ApplicationContext实例
ClassPathXmlApplicationContext:以类加载路径下的XML配置文件创建ApplicationContext实例
需同时加载多个XML配置文件,可采用如下方式:
// 搜索CLASSPATH路径,以CLASSPATH路径下的bean.xml、service.xml文件创建ApplicationContext
ApplicationContext appContext = new ClassPathXmlApplicationContext(new String[]{“bean.xml”,”service.xml”});
或
// 以指定路径下的bean.xml、service.xml文件创建ApplicationContext
ApplicationContext appContext = new FileSystemXmlApplicationContext(new String[]{“bean.xml”,“service.xml”});

Spring配置文件可用XML Schema来定义配置文件的语义约束;也可用DTD(不能用Spring2.X、Spring3.0所新增的配置标签)
<?xml version="1.0" encoding="UTF-8"?>
<!-- 使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   ………………
</beans>
 
<?xml version="1.0" encoding="UTF-8"?>
<!-- 指定Spring配置文件的DTD信息 -->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
   ...
</beans>


二、使用ApplicationContext

               Application允许以声明式方式操作容器,无须手动创建它;可利用如ContextLoader的支持类,在Web应用启动时自动创建ApplicaionContext;也可用编程方式创建ApplicationContext

      

               除提供BeanFactory所支持的全部功能外,ApplicationContext还有如下额外功能:

               继承MessageSource接口,因此提供国际化支持

               资源访问,比如URL和文件

               事件机制

               载入多个配置文件

               以声明式启动、并创建Spring容器

      

               当系统创建ApplicationContext容器时,默认预初始化所有singleton Bean;即当ApplicationContext容器初始化完成后,容器中所有singleton Bean也实例化完成

      

三、ApplicationContext的国际化支持

               MessageSource接口中用于国际化的方法:

               String getMessage(Stringcode,Object[] args,Locale loc)

               String getMessage(Stringcode,Object[] args,Stringdefault,Locale loc)

               String getMessage(MessageSourceResolvableresolvable,Locale locale)

 

               ApplicationContext正是通过这三个方法来完成国际化的,当程序创建ApplicationContext容器时,Spring自动查找在配置文件中名为messageSource的Bean实例,一旦找到这个Bean实例,上述3个方法的调用被委托给该messageSource Bean;若没有该Bean,ApplicationContext会查找其父定义中的messageSourceBean;若找到,它被作为messageSource使用;

               若无法找到messageSource Bean,系统将创建一个空的StaticMessageSourceBean,该Bean能接受上述三个方法的调用;

               在Spring中配置messageSource Bean时通常使用ResourceBundleMessageSource类

      

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
          <property name="basenames">
              <list>
                  <value>message</value>
                  <!-- 若有多个资源文件,全部列在此处 -->
              </list>
          </property>
</bean>

美式英语资源文件(message_en_US.properties)
hello=welcome,{0}
now=now is:{0}

简体中文资源文件(message.properties)
hellow=欢迎你,{0}
now=现在时间是:{0}
 
因为该资源文件包含了非西欧文字,应使用native2ascii工具(JDK中bin目录下有)将这份资源文件国际化:
将该文件放到jdk的bin目录下
然后输入命令native2ascii message.properties message_zh_CN.properties


public class SpringTest {
      public static void main(String[] args) {
           ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
           // 创建数组
           String[] a = { "读者" };
           // 使用getMessage方法获取本地化信息;Locale的getDefault方法
           String hello = ctx.getMessage("hello", a, Locale.getDefault());
           Object[] b = { new Date() };
           String now = ctx.getMessage("now", b, Locale.getDefault());
           // 打印出两条本地化消息
           System.out.println(hello);
           System.out.println(now);
      }
}



四、ApplicationContext的事件机制
 

               ApplicationContext的事物机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可实现ApplicationContext的事物处理;若容器中有一个ApplictionListenerBean,每当ApplicaitonContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发

               Spring的事件框架有如下两个重要成员:

               ApplicationEvent:容器事件,必须由ApplicationContext发布

               ApplicationListener:监听器,可由容器中的任何监听器Bean担任

      

               实际上,Spring的事件机制与所有事件机制都基本相似,它们都需要事件源、事件和事件监听器组成;只是此处的事件源是ApplicationContext,且事件必须由Java程序显示触发

               

      

/**
 * 定义一个ApplicationEvent类,其对象就是一个Spring容器事件
 *
 */
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;
      }
 
      public String getText() {
           return text;
      }
 
      public void setText(String text) {
           this.text = text;
      }
}

/**
 * 容器监听器类
 */
public class EmailNotifier implements ApplicationListener {
 
      /**
       * 每当容器内发生任何事件时,此方法都被触发
       */
      @Override
      public void onApplicationEvent(ApplicationEvent evt) {
           if (evt instanceof EmailEvent) {
                 // 只处理EmailEvent,发送email通知。。。
                 EmailEvent emailEvent = (EmailEvent) evt;
                 System.out.println("需要发送邮件的接受地址" + emailEvent.getAddress());
                 System.out.println("需要发送邮件的邮件正文" + emailEvent.getText());
           } else {
                 // 容器内置事件不作任何处理
                 System.out.println("容器本身的事件: " + evt);
           }
      }
}

<beans>
      <!-- 配置监听器 -->
      <bean class="org.app.main.EmailNotifier"/>
</beans>
 
<!-- 当在Spring中配置一个实现了ApplicationListener的Bean,Spring容器就会把这个Bean当成容器事件的监听器 -->
<!-- 当系统创建Spring容器、加载Spring容器时会自动触发容器事件,容器事件监听器可监听到这些事件 -->
<!-- 此外,程序也可调用ApplicationContect的pulishEvent()方法来主动触发容器事件-->

/**
 * 程序调用ApplicationContext的publishEvent来触发事件
 *
 */
public class SpringTest {
 
      public static void main(String[] args) {
           ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
           // 创建一个ApplicationEvent对象
           EmailEvent ele = new EmailEvent("hello", "spring_text@163.com", "this is a test");
           // 主动触发容器事件
           ctx.publishEvent(ele);
      }
}

               运行结果:

               容器本身的事件: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.support.ClassPathXmlApplicationContext@2d776d65: startup date [Fri Nov 21 14:48:01 GMT+08:00 2014]; root of context hierarchy]

               需要发送邮件的接受地址spring_text@163.com

               需要发送邮件的邮件正文this is a test

 

               从执行结果可看出,监听器不仅监听到程序所触发的事件,也监听到容器内置的事件;实际上,若开发者需要在Spring容器初始化、销毁时回调自定义方法,就可通过上面事件监听器来实现

               若Bean想发布事件,则Bean必须获得其容器的引用;若程序中没有直接获取容器的引用,则应该让Bean实现ApplicationContextAware或BeanFactoryAware接口,从而可以获得容器的引用



               Spring提供的几个内置事件: 

               ContextRefreshedEvent:ApplicationContext容器初始化或刷新触发该事件;此处的初始化是指所有Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean被预实例化,ApplicationContext容器已就绪可用

               ContextStartedEvent:当使用ConfigurableApplicationContext(ApplicationContext的子接口)接口的start()方法启动ApplicationContext容器时触发该事件;容器管理生命周期的Bean实例将获得一个指定的启动信号,这在经常需要停止后重新启动的场合比较常见

               ContextClosedEvent:当使用ConfigurableApplicationContext(ApplicationContext的子接口)接口的close()方法关闭ApplicationContext容器时触发该事件

               ContextStoppedEvent:当使用ConfigurableApplicationContext(ApplicationContext的子接口)接口的stop()方法停止ApplicationContext容器时触发该事件;此处的“停止”意味着容器管理生命周期的Bean实例将获得一个指定的停止信号,被停止的Spring容器可再次调用start()方法重新启动

               RequestHandledEvent:Web相关的事件,只能应用与使用DispatcherServlet的Web应用;在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件

 

五、让Bean获取Spring容器

               前面介绍的几个例子都是程序先创建Spring容器,再调用Spring容器的getBean()方法来获取Spring容器中的Bean;在这种访问模式下,程序总是持有Spring容器的引用

               但在实际应用中,尤其是Web应用中,Spring容器通常采用声明式方式配置产生:在web.xml文件中配置一个Listener,该Listener将会负责初始化Spring容器;前端MVC框架可直接调用Spring容器(无须访问Spring容器本身);在这种情况下,容器中Bean处于容器管理下,无须主动访问容器,只需接受容器的注入管理即可;Bean实例的依赖关系通常由容器动态注入,无须Bean实例主动请求

               在这种情况下,Spring容器中Bean通常不会需要访问容器中其他的Bean-----采用依赖注入,让Spring把依赖的Bean注入到依赖Bean中即可;但在某些特殊情况下,容器中的Bean可能需要主动访问Spring容器本身,Spring也为这种需求做好了准备

               实现BeanFactoryAware接口的Bean,拥有访问BeanFactory容器的能力,实现BeanFactoryAware(/ApplicationContextAware)接口的Bean实例被容器创建后,他会有一个引用,该引用指向创建它的BeanFactory;BeanFactoryAware接口只有setBeanFactory(BeanFactorybeanFactory)(/setApplicationContext(ApplicationContextappicationContext))方法:该方法中的beanFactory指向创建它的BeanFactory;该方法由Spring调用;当Spring容器调用该方法时,它会把自身作为参数传入该方法

                    

          

/**
 * 定义Bean类,实现ApplicationContextAware接口,该类将拥有访问容器的能力
 *
 */
public class Chinese implements ApplicationContextAware {
      // 将BeanFactory容器以成员变量保存
      private ApplicationContext ctx;
 
      /**
       * 实现ApplicationContextAware接口必须实现的方法
       */
      @Override
      public void setApplicationContext(ApplicationContext ctx) throws BeansException {
           this.ctx = ctx;
      }
 
      // 获得ApplicaitonContext的测试方法
      public ApplicationContext getContext() {
           return ctx;
      }
}


               实现ApplicationContextAware接口让Bean拥有了访问容器的能力,但污染了代码,导致代码与Spring接口耦合在一起;因此,若不是特别必要,不要直接访问容器








Spring-----4、使用Spring容器