Spring容器和被管理的Bean

来源:互联网 发布:浮云淘宝小号 编辑:程序博客网 时间:2024/06/06 00:52

本文摘自:李刚 著 《轻量级 Java EE企业应用实战 Struts2+Spring+hibernate整合开发》

 

 

        bean 是Spring 管理的基本单位,在Spring 的J2EE应用中,所有的组件都是bean,bean 包括数据源Hibernate 的SessionFactory 及事务管理器等。Spring 里的bean 是非常广义的概念,任何的Java 对象,Java 组件都可被当成bean 处理。甚至这些组件并不是标准的JavaBean。

        整个应用中各层的对象都处于Spring 的管理下,这些对象以bean 的方式存在。Spring负责创建bean 实例,并管理其生命周期。bean 在Spring 容器中运行时,无须感受Spring容器的存在,一样可以接受Spring 的依赖注入,包括bean 属性的注入,合作者的注入及依赖关系的注入等。

        Spring 的容器有两个接口:BeanFactoryApplicationContext ,这两个接口的实例也被称为Spring 上下文,它们都是产生bean 的工厂, bean 是Spring 工厂产生的实例。在Spring 产生bean 实例时,需要知道每个bean 的实现类,而bean 实例的使用者面向接口,无须关心bean 实例的实现类。因为Spring 工厂负责维护bean 实例的实例化,所以使用者无须关心实例化。

        bean 定义通常使用XML 配置文件。正确定义的bean由Spring 提供实例化,以及依赖关系的注入。bean实例通过BeanFactory 访问。对于大部分J2EE应用,bean 通过AppliactionContext 提供访问,因为AppliactionContext 是BeanFactory 的子接口,提供比BeanFactory 更多的功能。

 

 

一. Spring容器

 

        Spring 的容器最基本的接口就是:BeanFactory。BeanFactory 负责配置、创建及管理bean,它有个子接口: ApplictionContext ,因此也被称为Spring 上下文。另外,Spring容器还负责管理bean与bean之间的依赖关系。

        BeanFactory 接口包含如下的基本方法。

       (1)public boolean containsBean(String name):判断Spring 容器是否包含id 为name 的bean 定义。

       (2)public Object getBean(String name):返回容器id 为name 的bean 。

       (3)public Object getBean(String name, Class requiredType):返回容器中id 为name ,并且类型为required可pe 的bean 。

       (4)public Class getType(String name) :返回容器中id 为name的bean的类型。

       调用者只需使用getBean 方法即可获得指定bean的引用,无须关心bean 的实例化过程。即bean 实例的创建过程完全透明。

        BeanFactory有很多实现类,通常使用org.springframework.beans.factory.xml.XmlBeanFactory 类。但对大部分J2EE 应用而言,推荐使用ApplicationContext,因为其是BeanFactory 的子接口,其常用的实现类是org.springframework.context.support.FileSystemXmlApplicationContext

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

        Resource 接口:用于访问配置文件资源。

        对于大部分J2EE 应用而言,可在启动Web 应用时自动加载ApplicationContext实例,接受Spring管理的bean 无须知道ApplicationContext的存在,也一样可以利用ApplicationContext的管理。对于独立的应用程序,也可通过如下方法来实例化BeanFactory:

[java] view plaincopy
  1. //以指定路径下bean.xml配置文件为参数,创建文件输入流  
  2.         InputStream is = new FileInputStream("beans.xml");  
  3.         //以指定的文件输入流is ,创建Resource 对象  
  4.         InputStreamResource isr = new InputStreamResource(is);  
  5.         //以Resource 对象作为参数,创建BeanFactory 的实例  
  6.         XmlBeanFactory factory = new XmlBeanFactory(isr);  

          或者采用如下方法:

[java] view plaincopy
  1. //搜索CLASSPATH路径,以CLASSPATH路径下的beans.xml 文件创建Resource对象  
  2.         ClassPathResource res = new ClassPathResource("beans.xml");  
  3.         // 以Resource对象为参数,创建BeanFactory实例  
  4.         XmlBeanFactory factory = new XmlBeanFactory(res);  

         如果应用里有多个属性配置文件,则应该采用Beanfactory的子接口ApplicationContext来创建BeanFactory的实例, ApplicationContext通常使用如下两个实现类。

        (1)FileSystemXmlApplicationContext:以指定路径的XML 配置文件创建ApplicationContext 。

        (2)ClassPathXmlApplicationContext:以CLASSPATH 路径下的XML 配置文件创建ApplicationContext 。

        如果需要同时加载多个XML 配置文件,可以采用如下方式:

[java] view plaincopy
  1. //搜索CLASSPATH 路径,以CLASSPATH 路径下的applicationContext.xml  
  2.         //service.xml 文件创建ApplicationContext  
  3.         ClassPathXmlApplicationContext appContext =  
  4.             new ClassPathXmlApplicationContext(  
  5.             new String [] {" applicationContext. xml" , "service.xml"});  
  6.         //事实上, ApplicationContext 是BeanFactory 的子接口,支持强制类型转换  
  7.         BeanFactory factory = (BeanFactory)appContext;  

        当然也可支持从指定路径来搜索特定文件加载:

[java] view plaincopy
  1. //指定路径下的applicationContext.xml, service.xml文件创建ApplicationContext  
  2.         FileSystemXmlApplicationContext appContext =  
  3.         new FileSystemXmlApplicationContext(  
  4.         new String[] {"applicationContext.xml" , "service.xml"});  
  5.         //事实上, ApplicationContext是BeanFactory的子接口,支持强制类型转换  
  6.         BeanFactory factory = (BeanFactory)appContext;  

        下面是Spring 最简单的配置文件:

[java] view plaincopy
  1. <!--XML文件的文件头部分,指定了Xml文件的编码值-->  
  2. <?xml version = "1.0" encoding = "gb2312"?>  
  3. <!--指定Spring的xml配置文件的dtd-->  
  4. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  
  5.     "http://www.springframework.org/dtd/spring-beans.dtd">  
  6.     <!--beans元素是Spring配置文件的根元素,  
  7.     所有的Spring的配置文件都应该按以下结构书写-->  
  8. <beans>  
  9. </beans>  


 

 

二. bean的基本定义

 

        <beans/> 元素是Spring 配置文件的根元素, <bean> 元素是<beans/> 元素的子元素,<beans/> 元素可以包含多个<bean/>元素, <bean/>子元素定义一个bean ,每个bean 是接受Spring容器里的Java 实例。

        在定义bean时,通常必须指定以下两个属性:

        (1)id: id 属性是确定该bean 的唯一标识符,容器对bean 管理、访问及该bean 的依赖关系,都通过该属性完成。bean 的id 属性在Spring 容器中是唯一的。

        (2)class:class 属性指定该bean 的具体实现类,这里不能是接口。通常情况下,Spring会直接使用new 关键字创建该bean 的实例,因此,这里必须提供bean 实现类的类名。

        下面给出了包含两个bean 定义的简单配置文件:

[html] view plaincopy
  1. <!--XML文件的文件头部分,指定了Xml文件的编码值-->  
  2. <?xml version = "1.0" encoding = "gb2312"?>  
  3. <!--指定Spring的xml配置文件的dtd-->  
  4. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  
  5.     "http://www.springframework.org/dtd/spring-beans.dtd">  
  6.     <!--beans元素是Spring配置文件的根元素,  
  7.     所有的Spring的配置文件都应该按以下结构书写-->  
  8. <beans>  
  9. <!--定义第一个java实例bean1,该java实例对应的实现类是ppp.Test1-->  
  10. <bean id="bean1" class = "ppp.Test1"/>  
  11. <!--定义第二个java实例bean2,该java实例对应的实现类是ppp.Test2-->  
  12. <bean id="bean2" class = "ppp.Test2"/>  
  13. </beans>  

        在Spring 容器集中管理bean的实例化时, bean实例可以通过BeanFactory 的getBean(String beanid)方法得到。此时,BeanFactory 将变成简单工厂模式里的工厂,程序只需要获取BeanFactory引用,即可获得Spring 容器管理全部实例的引用,从而使程序不需要与具体实例的实现过程藕合。在大部分J2EE 应用中,当应用启动时,会自动创建Spring 容器实例,组件之间直接以依赖注入的方式藕合,甚至无须访问Spring 容器。

 

 

三. 定义Spring中的Bean 的行为方式

 

        在Spring 1. 2 版本中, bean在Spring 的容器中有两种基本行为。singleton:单态non-singleton 或prototype:原型。

        (1)如果一个bean 被设置成non-singleton 行为,当程序每次请求该id的bean 时,Spring都会新建一个bean 实例,然后返回给程序。在这种情况下,Spring 容器仅仅使用new 关键字创建bean 实例,一旦创建成功,容器不再跟踪实例,也不会维护bean 实例的状态

        通常要求将Web 应用的控制器bean 配置成non-singleton 行为。因为,每次HttpServletRequest 都需要系统启动一个新Action 来处理用户请求。

        (2)如果一个bean 被设置成singleton时,整个Spring 容器里只有一个共享实例存在,程序每次请求该id 的bean时, Spring都会返回该bean 的共享实例。该容器负责跟踪单态bean 实例的状态,维护bean 实例的生命周期。

         如果不指定bean 的基本行为,Spring 默认使用singleton行为。在创建Java实例时,需要进行内存申请:销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,non-singleton 行为的bean创建、销毁时代价比较大。而singleton行为的bean实例成功后,可以重复使用。

         关于自定义bean 的生命周期行为,请参看本人的相关博客。设置bean 的基本行为,是通过singleton 属性来指定, singleton 属性只接受true 或false 值。例如在下面配置文件中配置singleton 和non-singleton:

[html] view plaincopy
  1. <!--XML文件的文件头部分,指定了Xml文件的编码值-->  
  2. <?xml version = "1.0" encoding = "gb2312"?>  
  3. <!--指定Spring的xml配置文件的dtd-->  
  4. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  
  5.     "http://www.springframework.org/dtd/spring-beans.dtd">  
  6.     <!--beans元素是Spring配置文件的根元素,  
  7.     所有的Spring的配置文件都应该按以下结构书写-->  
  8. <beans>  
  9. <!--定义第一个java实例p1,该java实例对应的实现类是ppp.Person-->  
  10. <bean id="p1" class = "ppp.Person"/>  
  11. <!--定义第二个java实例p2,该java实例对应的实现类是ppp.Person  
  12.                     该类为non-singleton-->  
  13. <bean id="p1" class = "ppp.Person" singleton = "false"/>  

        主程序通过如下代码来测试两个bean 的区别:

[java] view plaincopy
  1. //搜索ClASSPATH路径,以ClASSPATH路径下的beans.xml文件创建Resource对象  
  2.         ClassPathResource res = new ClassPathResource("beans.xml");  
  3.         //以Resource对象为参数,创建Beanfactory实例  
  4.         XmlBeanFactory beanFactory = new XmlBeanFactory(res);  
  5.         //判断两次请求Singleton的行为的bean实例是否相等  
  6.         System.out.println(beanFactory.getBean("p1")==beanFactory.getBean("p1"));  
  7.         //判断两次请求non-singleton行为的bean实例是否相等  
  8.         System.out.println(beanFactory.getBean("p2")== beanFactory.getBean("p2"));  

        程序执行结果为:true 和 false。

        对于singleton行为的bean,每次请求该id 的bean时,都将返回同一个共事实例,因而两次获取的bean 实例完全相同:但对non-singleton 行为的bean,每次请求该id 的bean时都将产生新的实例,因此两次请求获得bean实例不相同。

 

四. 深入理解bean

   

       Spring 容器对bean没有特殊要求,甚至不要求该bean 像标准的JavaBean一一必须为每个属性提供对应的getter和setter 方法

        Spring中的bean 比JavaBean 的功能要强大,用法也更复杂。当然,传统JavaBean也可作为普通的Spring bean ,可接受Spring管理。下面的代码演示了Spring的bean实例,该bean实例是数据源,提供数据库连接:

[html] view plaincopy
  1. <!--下面是标准xml文件头-->  
  2. <?xml version = "1.0" encoding = "gb2312"?>  
  3. <!--下面是一行定义Spring的xml配置文件的dtd-->  
  4. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  
  5.     "http://www.springframework.org/dtd/spring-beans.dtd">  
  6. <!--以上三行对于所有的spring配置文件都是相同的-->  
  7. <!--spring配置文件的根元素-->  
  8. <!--beans元素是Spring配置文件的根元素,所有的Spring配置文件都应该以如下的结构书写-->  
  9. <beans>  
  10. <!--配置id为datasource的bean,该bean是一个数据源实例-->  
  11.    <bean id = "datasource" class = "org.commons.dbcp.BasicDataSource"  
  12.          destroy-method = "close">  
  13.                  <!--确定数据源驱动-->  
  14.          <property name = "driverClassName">  
  15.               <value>com.mysql.jdbc.Driver</value>  
  16.          </property>  
  17.                  <!--确定连接数据源的URL-->  
  18.          <property name = "url">  
  19.                           <!--j2ee是需要连接的数据库名-->  
  20.               <value>jdbc:mysql://localhost:3306/j2ee</value>  
  21.          </property>  
  22.   
  23.                  <!--确定连接数据库的用户名-->  
  24.          <property name = "username">  
  25.                           <!--root是连接数据库的用户名-->  
  26.               <value>root</value>  
  27.          </property>  
  28.                  <!--确定连接数据库的密码-->  
  29.          <property name = "password">  
  30.                           <!--pass是连接数据库的密码-->  
  31.               <value>pass</value>  
  32.          </property>  
  33.     </bean>  
  34. </beans>  

        主程序部分由BeanFactory来获取该bean 的实例,获取实例时使用bean的唯一标识符: id 属性。该属性是bean 实例在容器中的访问点。下面是主程序部分:

[java] view plaincopy
  1. //实例化Spring容器。Spring容器负责实例化bean  
  2.         ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");  
  3.         //通过bean id获取bean实例,并强制类型转化为DataSource  
  4.         DataSource ds = (DataSource)ctx.getBean("datasource");  
  5.         //通过datasource来获取数据库连接  
  6.         Connection conn = ds.getConnection();  
  7.         //通过数据库连接获取statement  
  8.         java.sql.Statement stmt  = conn.createStatement();  
  9.         //使用statement执行sql语句  
  10.         stmt.execute("insert into mytable values('wdda2')");  
  11.         //清理资源,回收数据库连接资源  
  12.         if(stmt != null) stmt.close();  
  13.         if(conn != null) conn.close();  

        从该实例可以看出, Spring 的bean 远远超出值对象的JavaBean范畴,此时bean可以代表应用中的任何组件及任何资源实例。

        虽然Spring 对bean没有特殊要求,但笔者还是建议在Spring 中的bean应满足如下几个原则:

        (1)每个bean实现类都应提供无参数的构造器。

        (2)接受构造注入的bean,则应提供对应的构造函数。

        (3)接受设值注入的bean,则应提供对应的setter方法,并不强制要求提供对应的getter方法。

        传统的JavaBean和SpringBean存在如下的区别。

        (1)用处不同:传统JavaBean 更多地作为值对象传递参数,而Spring 中的bean用处几乎无所不在,任何应用组件都可被称为bean。

        (2)写法不同:传统JavaBean 作为值对象,要求每个属性都提供getter 和setter 方法;但Spring 中的bean 只需为接受设值注入的属性提供setter 方法。

       (3)生命周期不同:传统lavaBean作为值对象传递,不接收任何容器管理其生命周期;Spring 中的bean由Spring 管理其生命周期行为。

0 0
原创粉丝点击