本文摘自:李刚 著 《轻量级 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 的容器有两个接口:BeanFactory和ApplicationContext ,这两个接口的实例也被称为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:
//以指定路径下bean.xml配置文件为参数,创建文件输入流
InputStream is = new FileInputStream("beans.xml");
//以指定的文件输入流is ,创建Resource 对象
InputStreamResource isr = new InputStreamResource(is);
//以Resource 对象作为参数,创建BeanFactory 的实例
XmlBeanFactory factory = new XmlBeanFactory(isr);
或者采用如下方法:
//搜索CLASSPATH路径,以CLASSPATH路径下的beans.xml 文件创建Resource对象
ClassPathResource res = new ClassPathResource("beans.xml");
// 以Resource对象为参数,创建BeanFactory实例
XmlBeanFactory factory = new XmlBeanFactory(res);
如果应用里有多个属性配置文件,则应该采用Beanfactory的子接口ApplicationContext来创建BeanFactory的实例, ApplicationContext通常使用如下两个实现类。
(1)FileSystemXmlApplicationContext:以指定路径的XML 配置文件创建ApplicationContext 。
(2)ClassPathXmlApplicationContext:以CLASSPATH 路径下的XML 配置文件创建ApplicationContext 。
如果需要同时加载多个XML 配置文件,可以采用如下方式:
//搜索CLASSPATH 路径,以CLASSPATH 路径下的applicationContext.xml
//service.xml 文件创建ApplicationContext
ClassPathXmlApplicationContext appContext =
new ClassPathXmlApplicationContext(
new String [] {" applicationContext. xml" , "service.xml"});
//事实上, ApplicationContext 是BeanFactory 的子接口,支持强制类型转换
BeanFactory factory = (BeanFactory)appContext;
当然也可支持从指定路径来搜索特定文件加载:
//指定路径下的applicationContext.xml, service.xml文件创建ApplicationContext
FileSystemXmlApplicationContext appContext =
new FileSystemXmlApplicationContext(
new String[] {"applicationContext.xml" , "service.xml"});
//事实上, ApplicationContext是BeanFactory的子接口,支持强制类型转换
BeanFactory factory = (BeanFactory)appContext;
下面是Spring 最简单的配置文件:
<!--XML文件的文件头部分,指定了Xml文件的编码值-->
<?xml version = "1.0" encoding = "gb2312"?>
<!--指定Spring的xml配置文件的dtd-->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<!--beans元素是Spring配置文件的根元素,
所有的Spring的配置文件都应该按以下结构书写-->
<beans>
</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 定义的简单配置文件:
<!--XML文件的文件头部分,指定了Xml文件的编码值-->
<?xml version = "1.0" encoding = "gb2312"?>
<!--指定Spring的xml配置文件的dtd-->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<!--beans元素是Spring配置文件的根元素,
所有的Spring的配置文件都应该按以下结构书写-->
<beans>
<!--定义第一个java实例bean1,该java实例对应的实现类是ppp.Test1-->
<bean id="bean1" class = "ppp.Test1"/>
<!--定义第二个java实例bean2,该java实例对应的实现类是ppp.Test2-->
<bean id="bean2" class = "ppp.Test2"/>
</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:
<!--XML文件的文件头部分,指定了Xml文件的编码值-->
<?xml version = "1.0" encoding = "gb2312"?>
<!--指定Spring的xml配置文件的dtd-->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<!--beans元素是Spring配置文件的根元素,
所有的Spring的配置文件都应该按以下结构书写-->
<beans>
<!--定义第一个java实例p1,该java实例对应的实现类是ppp.Person-->
<bean id="p1" class = "ppp.Person"/>
<!--定义第二个java实例p2,该java实例对应的实现类是ppp.Person
该类为non-singleton-->
<bean id="p1" class = "ppp.Person" singleton = "false"/>
主程序通过如下代码来测试两个bean 的区别:
//搜索ClASSPATH路径,以ClASSPATH路径下的beans.xml文件创建Resource对象
ClassPathResource res = new ClassPathResource("beans.xml");
//以Resource对象为参数,创建Beanfactory实例
XmlBeanFactory beanFactory = new XmlBeanFactory(res);
//判断两次请求Singleton的行为的bean实例是否相等
System.out.println(beanFactory.getBean("p1")==beanFactory.getBean("p1"));
//判断两次请求non-singleton行为的bean实例是否相等
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实例是数据源,提供数据库连接:
<!--下面是标准xml文件头-->
<?xml version = "1.0" encoding = "gb2312"?>
<!--下面是一行定义Spring的xml配置文件的dtd-->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<!--以上三行对于所有的spring配置文件都是相同的-->
<!--spring配置文件的根元素-->
<!--beans元素是Spring配置文件的根元素,所有的Spring配置文件都应该以如下的结构书写-->
<beans>
<!--配置id为datasource的bean,该bean是一个数据源实例-->
<bean id = "datasource" class = "org.commons.dbcp.BasicDataSource"
destroy-method = "close">
<!--确定数据源驱动-->
<property name = "driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<!--确定连接数据源的URL-->
<property name = "url">
<!--j2ee是需要连接的数据库名-->
<value>jdbc:mysql://localhost:3306/j2ee</value>
</property>
<!--确定连接数据库的用户名-->
<property name = "username">
<!--root是连接数据库的用户名-->
<value>root</value>
</property>
<!--确定连接数据库的密码-->
<property name = "password">
<!--pass是连接数据库的密码-->
<value>pass</value>
</property>
</bean>
</beans>
主程序部分由BeanFactory来获取该bean 的实例,获取实例时使用bean的唯一标识符: id 属性。该属性是bean 实例在容器中的访问点。下面是主程序部分:
//实例化Spring容器。Spring容器负责实例化bean
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
//通过bean id获取bean实例,并强制类型转化为DataSource
DataSource ds = (DataSource)ctx.getBean("datasource");
//通过datasource来获取数据库连接
Connection conn = ds.getConnection();
//通过数据库连接获取statement
java.sql.Statement stmt = conn.createStatement();
//使用statement执行sql语句
stmt.execute("insert into mytable values('wdda2')");
//清理资源,回收数据库连接资源
if(stmt != null) stmt.close();
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 管理其生命周期行为。
PS:限于篇幅,关于spring创建实例将单独作为一篇文章发布。