1、Spring 教程Email:版权所有,如果转载和篡改,请注明出处1Spring 教程作者:钱安川(Moxie)注:后面的内容我将不再完善,但网上的朋友可以帮助完善,只需注明住处即可。Spring 教程Email:版权所有,如果转载和篡改,请注明出处2Spring 教程 1Spring 框架概述 3Spring 是什么? 3Spring 的历史 4Spring 的使命(Mission Statement) 4Spring 受到的批判 4Spring 包含的模块 5总结 6Spring 的 IoC 容器 6用户注册的例子 7面向接口编程 8(用户持久化类)重构第一步 面向接口编程 8重构第二步
2、工厂(Factory)模式 .9重构第三步 工厂(Factory)模式的改进 .10重构第四步IoC 容器 .11控制反转(IoC)/依赖注入( DI) 11什么是控制反转/依赖注入? 11依赖注入的三种实现形式 12BeanFactory .14BeanFactory 管理 Bean(组件)的生命周期 .15Bean 的定义 16Bean 的之前初始化 19Bean 的准备就绪(Ready )状态 21Bean 的销毁 21ApplicationContext 21Spring 的 AOP 框架 21Spring 的数据层访问 22Spring 的声明式事务 22Spring 对其它企业应用
3、支持 22名词解释容器:框架:组件:服务:框架容器Spring 教程Email:版权所有,如果转载和篡改,请注明出处3Spring 框架概述 主要内容:介绍 Spring 的历史, Spring 的概论和它的体系结构,重点阐述它在 J2EE中扮演的角色。 目的:让学员全面的了解 Spring 框架,知道 Spring 框架所提供的功能,并能将 Spring框架和其它框架(WebWork/Struts 、hibernate )区分开来。Spring 是什么?Spring 是一个开源框架,它由 Rod Johnson 创建。它是为了解决企业应用开发的复杂性而创建的。Spring 使用基本的 Jav
4、aBean 来完成以前只可能由 EJB 完成的事情。然而,Spring 的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java 应用都可以从 Spring 中受益。 目的:解决企业应用开发的复杂性 功能:使用基本的 JavaBean 代替 EJB,并提供了更多的企业应用功能 范围:任何 Java 应用简单来说,Spring 是一个轻量级的控制反转 (IoC)和面向切面(AOP)的容器框架。 轻量从大小与开销两方面而言 Spring 都是轻量的。完整的 Spring 框架可以在一个大小只有 1MB 多的 JAR 文件里发布。并且 Spring 所需的处理开销也是微不足道
5、的。此外,Spring 是非侵入式的:典型地, Spring 应用中的对象不依赖于 Spring 的特定类。 控制反转Spring 通过一种称作控制反转( IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为 IoC 与 JNDI 相反不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。 面向切面Spring 提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务()管理)进行内聚性的开发。应用对象只实现它们应该做的完成业务逻
6、辑仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。 容器Spring 包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个 bean 如何被创建基于一个可配置原型( prototype) ,你的bean 可以创建一个单独的实例或者每次需要时都生成一个新的实例 以及它们是如何相互关联的。然而,Spring 不应该被混同于传统的重量级的 EJB 容器,它们经常是庞大与笨重的,难以使用。 框架Spring 可以将简单的组件配置、组合成为复杂的应用。在 Spring 中,应用对象被声明式地组合,典型地是在一个 XML 文件里。Spring 也提供
7、了很多基础功能(事务管理、持久化框架集成等等) ,将应用逻辑的开发留给了你。 Spring 教程Email:版权所有,如果转载和篡改,请注明出处4所有 Spring 的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为 Spring 中的各种模块提供了基础支持。Spring 的历史Spring 的基础架构起源于 2000 年早期,它是 Rod Johnson 在一些成功的商业项目中构建的基础设施。在 2002 后期,Rod Johnson 发布了Expert One-on-One J2EE Design and Development一书,并随书提供了一个初步的开发框架实现i
8、nterface21 开发包,interface21 就是书中阐述的思想的具体实现。后来,Rod Johnson 在 interface21 开发包的基础之上,进行了进一步的改造和扩充,使其发展为一个更加开放、清晰、全面、高效的开发框架Spring。2003 年 2 月 Spring 框架正式成为一个开源项目,并发布于 SourceForge 中。Spring 的使命(Mission Statement) J2EE 应该更加容易使用。 面向对象的设计比任何实现技术(比如 J2EE)都重要。 面向接口编程,而不是针对类编程。Spring 将使用接口的复杂度降低到零。 (面向接口编程有哪些复杂度?
9、) 代码应该易于测试。Spring 框架会帮助你,使代码的测试更加简单。 JavaBean 提供了应用程序配置的最好方法。 在 Java 中,已检查异常(Checked exception)被过度使用。框架不应该迫使你捕获不能恢复的异常。Spring 受到的批判 Spring 不是一个“标准” 。Spring 不是 J2EE 规范的一部分,没有通过 JCP(Java Community Process)的审核认可。批判来源于 EJB 的支持者,他们认为 EJB 是一个标准,是 J2EE 规范的一部分。当然,标准最主要的目的是希望在应用服务器之间是可移植的,可是 EJB 的移植却并不轻松,不同应
10、用服务器的 ejb 部署描述文件总是有着差异。而且 EJB 开发的类完全依赖于 EJB 容器。而 Spring 对其管理的 Bean 没有任何形式的侵入,这样的 Bean 是普通 Java 对象(POJO),那么它就遵循 Java 标准,可以到处移植。 Spring 是“超重量级”的。Spring 涉及的内容确实很多(例如:提供了对 jdbc、ORM、远程访问等等的支持) ,但其本质还是 Java 技术的庞大。Spring 只是为了这些技术提供更好的使用方案而已。同时,你可以只选取你需要使用的部分。Spring 教程Email:版权所有,如果转载和篡改,请注明出处5Spring 包含的模块Sp
11、ring 框架由七个定义明确的模块组成(图 1.1) 。(Spring 框架概览图)如果作为一个整体,这些模块为你提供了开发企业应用所需的一切。但你不必将应用完全基于 Spring 框架。你可以自由地挑选适合你的应用的模块而忽略其余的模块。 就像你所看到的,所有的 Spring 模块都是在核心容器之上构建的。容器定义了 Bean是如何创建、配置和管理的更多的 Spring 细节。当你配置你的应用时,你会潜在地使用这些类。但是作为一名开发者,你最可能对影响容器所提供的服务的其它模块感兴趣。这些模块将会为你提供用于构建应用服务的框架,例如 AOP 和持久性。核心容器这是 Spring 框架最基础的
12、部分,它提供了依赖注入( Dependency Injection)特征来实现容器对 Bean 的管理。这里最基本的概念是 BeanFactory,它是任何 Spring 应用的核心。BeanFactory 是工厂模式的一个实现,它使用 IoC 将应用配置和依赖说明从实际的应用代码中分离出来。应用上下文(Context )模块核心模块的 BeanFactory 使 Spring 成为一个容器,而上下文模块使它成为一个框架。这个模块扩展了 BeanFactory 的概念,增加了对国际化( I18N)消息、事件传播以及验证的支持。 另外,这个模块提供了许多企业服务,例如电子邮件、JNDI 访问、E
13、JB 集成、远程以及时序调度(scheduling)服务。也包括了对模版框架例如 Velocity 和 FreeMarker 集成的支持。Spring 的 AOP 模块Spring 在它的 AOP 模块中提供了对面向切面编程的丰富支持。这个模块是在 Spring应用中实现切面编程的基础。为了确保 Spring 与其它 AOP 框架的互用性, Spring 的 AOP支持基于 AOP 联盟定义的 API。AOP 联盟是一个开源项目,它的目标是通过定义一组共同的接口和组件来促进 AOP 的使用以及不同的 AOP 实现之间的互用性。通过访问他们的站点 http:/aopalliance. ,你可以找
14、到关于 AOP 联盟的更多内容。 Spring 教程Email:版权所有,如果转载和篡改,请注明出处6Spring 的 AOP 模块也将元数据编程引入了 Spring。使用 Spring 的元数据支持,你可以为你的源代码增加注释,指示 Spring 在何处以及如何应用切面函数。JDBC 抽象和 DAO 模块 使用 JDBC 经常导致大量的重复代码,取得连接、创建语句、处理结果集,然后关闭连接。Spring 的 JDBC 和 DAO 模块抽取了这些重复代码,因此你可以保持你的数据库访问代码干净简洁,并且可以防止因关闭数据库资源失败而引起的问题。这个模块还在几种数据库服务器给出的错误消息之上建立了
15、一个有意义的异常层。使你不用再试图破译神秘的私有的 SQL 错误消息! 另外,这个模块还使用了 Spring 的 AOP 模块为 Spring 应用中的对象提供了事务管理服务。对象/关系映射集成模块对那些更喜欢使用对象/关系映射工具而不是直接使用 JDBC 的人,Spring 提供了ORM 模块。Spring 并不试图实现它自己的 ORM 解决方案,而是为几种流行的 ORM 框架提供了集成方案,包括 Hibernate、JDO 和 iBATIS SQL 映射。Spring 的事务管理支持这些ORM 框架中的每一个也包括 JDBC。Spring 的 Web 模块Web 上下文模块建立于应用上下文
16、模块之上,提供了一个适合于 Web 应用的上下文。另外,这个模块还提供了一些面向服务支持。例如:实现文件上传的 multipart 请求,它也提供了 Spring 和其它 Web 框架的集成,比如 Struts、WebWork。Spring 的 MVC 框架 Spring 为构建 Web 应用提供了一个功能全面的 MVC 框架。虽然 Spring 可以很容易地与其它 MVC 框架集成,例如 Struts,但 Spring 的 MVC 框架使用 IoC 对控制逻辑和业务对象提供了完全的分离。它也允许你声明性地将请求参数绑定到你的业务对象中,此外,Spring 的 MVC 框架还可以利用 Spri
17、ng 的任何其它服务,例如国际化信息与验证。总结Spring 带来了复杂的 J2EE 开发的春天。它的核心是轻量级的 IoC 容器,它的目标是为J2EE 应用提供了全方位的整合框架,在 Spring 框架下实现多个子框架的组合,这些子框架之间可以彼此独立,也可以使用其它的框架方案加以代替,Spring 希望为企业应用提供一站式(one-stop shop)的解决方案。Spring 的 IoC 容器 主要内容:从最基本的面向接口编程逐步引入 IoC 设计模式(以银行卡:Card 为例,接口单例工厂方法IoC) ;详细介绍 IoC 的三种实现,并对其优、缺点进行比较;之后开始引入 Spring 的
18、 IoC 容器,详细介绍如何使用 Spring 的 IoC 容器组织业务组件。 目的:使学员真正理解 IoC 的概念、优点,并掌握 Spring IoC 容器的使用。Spring 教程Email:版权所有,如果转载和篡改,请注明出处7用户注册的例子我们先看看更进一步的需求:实现一个用户注册信息持久化的类。功能:1、 保存用户注册的信息;2、 根据用户的名称获得该注册用户。虽然功能简单,但它对持久化方式的要求却非常的灵活:1、 在内存中持久化,供测试、演示使用。2、 如果用户的数据很少,将用户信息持据化到文本文件中。3、 如果用户信息很多,并需要一些灵活的查询,则需要使用 JDBC 技术将用将用
19、户信息持久化到数据库中。4、 面对企业复杂关联的数据,甚至需要使用持久层框架来实现用户信息的持久化,比如:iBATIS、Hibernate 等。如何去设计、实现我们这个持久化类呢?我们遵循软件开发的原则“首先让它跑起来,再去优化(重构)它” ,我们首先实现最简单的在内存中持久化用户信息。既然我们要保存和取得用户信息,首先应该设计用户类。代码如下:User.javapublic class User private Long id;private String name;private String password;private String group;public User(String
20、 name,String password)this.name = name;this.password = password;/相应的 get/set 方法持久化类有两个方法,分别在内存中保存和获取 User 对象。代码如下:MemoryUserPersist.javapublic class MemoryUserPersist private static Map users = new HashMap();staticUser defaultAdmin = new User(“Moxie“,“pass“);users.put(defaultAdmin.getName(),defaultA
21、dmin);Spring 教程Email:版权所有,如果转载和篡改,请注明出处8public MemoryUserPersist ()public void saveUser(User user)users.put(user.getName(),user);public User LoadUser(String userName)return (User)users.get(userName);用户持久化类完成之后,我们就可以在客户端 UserRegister 中使用它了。例如:用户注册时,UserRegister 代码片断如下:MemoryUserPersist userPersist =
22、new MemoryUserPersist ();userPersist.saveUser(user); 可是,现在如果要在文本文件中持久化 User,又该如何实现呢?实现一个TextUserPersist 类,这个并不困难。但客户端代码将面临重大灾难:找到所有使用过MemoryUserPersist 的客户端类,将他们中的 MemoryUserPersist 逐个手工修改为 TextUserPersist,并且重新编译,当然以前的测试也必须全部从头来过!人生的浩劫只是刚刚开始,因为根据前面的需求我们至少要分别实现四种持久化方式!这时,你一定和我一样在期待着救世主的早日降临接口(Interfa
23、ce) 。面向接口编程什么是接口? 接口定义了行为的协议,这些行为在继承接口的类中实现。 接口定义了很多方法,但是没有实现它们。类履行接口协议并实现所有定义在接口中的方法。 接口是一种只有声明没有实现的特殊类。接口的优点: Client 不必知道其使用对象的具体所属类。 一个对象可以很容易地被(实现了相同接口的)的另一个对象所替换。 对象间的连接不必硬绑定(hardwire)到一个具体类的对象上,因此增加了灵活性。 松散藕合(loosens coupling) 。 增加了重用的可能性。接口的缺点:设计的复杂性略有增加(用户持久化类)重构第一步面向接口编程1、 设计用户持久化类的接口 UserD
24、ao,代码如下:Spring 教程Email:版权所有,如果转载和篡改,请注明出处9public interface UserDao public void save(User user);public User load(String name);2、 具体的持久化来必须要继承 UserDao 接口,并实现它的所有方法。我们还是首先实现内存持久化的用户类:public class MemoryUserDao implements UserDaoprivate static Map users = new HashMap();staticUser user = new User(“Moxie“
25、,“pass“);users.put(user.getName(),user);public void save(User user) users.put(user.getId(),user);public User load(String name) return (User)users.get(name);MemoryUserDao 的实现代码和上面的 MemoryUserPersist 基本相同,唯一区别是MemoryUserDao 类继承了 UserDao 接口,它的 save()和 load()方法是实现接口的方法。这时,客户端 UserRegister 的代码又该如何实现呢?Use
26、rDao userDao = new MemoryUserDao();userDao.save(user);(注:面向对象“多态”的阐述)如果我们再切换到文本的持久化实现 TextUserDao,客户端代码仍然需要手工修改。虽然我们已经使用了面向对象的多态技术,对象 userDao 方法的执行都是针对接口的调用,但 userDao 对象的创建却依赖于具体的实现类,比如上面 MemoryUserDao。这样我们并没有完全实现前面所说的“Client 不必知道其使用对象的具体所属类 ”。如何解决客户端对象依赖具体实现类的问题呢?下面该是我们的工厂(Factory)模式出场了!重构第二步工厂(Fac
27、tory)模式我们使用一个工厂类来实现 userDao 对象的创建,这样客户端只要知道这一个工厂类就可以了,不用依赖任何具体的 UserDao 实现。创建 userDao 对象的工厂类 UserDaoFactory 代码如下:public class UserDaoFactory public static UserDao createUserDao()return new MemoryUserDao();Spring 教程Email:版权所有,如果转载和篡改,请注明出处10客户端 UserRegister 代码片断如下:UserDao userDao = UserDaoFactory. Cr
28、eateUserDao();userDao.save(user);现在如果再要更换持久化方式,比如使用文本文件持久化用户信息。就算有再多的客户代码调用了用户持久化对象我们都不用担心了。因为客户端和用户持久化对象的具体实现完全解耦。我们唯一要修改的只是一个 UserDaoFactory 类。重构第三步工厂(Factory)模式的改进到这里人生的浩劫已经得到了拯救。但我们仍不满足,因为假如将内存持久化改为文本文件持久化仍然有着硬编码的存在UserDaoFactory 类的修改。代码的修改就意味着重新编译、打包、部署甚至引入新的 Bug。所以,我们不满足,因为它还不够完美!如何才是我们心目中的完美方
29、案?至少要消除更换持久化方式时带来的硬编码。具体实现类的可配置不正是我们需要的吗?我们在一个属性文件中配置 UserDao 的实现类,例如:在属性文件中可以这样配置:userDao = com.test.MemoryUserDao。UserDao 的工厂类将从这个属性文件中取得 UserDao 实现类的全名,再通过 Class.forName(className).newInstance()语句来自动创建一个 UserDao 接口的具体实例。UserDaoFactory 代码如下:public class UserDaoFactory public static UserDao createU
30、serDao()String className = “; / 从属性文件中取得这个 UserDao 的实现类全名。UserDao userDao = null;try userDao = (UserDao)Class.forName(className).newInstance(); catch (Exception e) e.printStackTrace(); return userDao;通过对工厂模式的优化,我们的方案已近乎完美。如果现在要更换持久化方式,不需要再做任何的手工编码,只要修改配置文件中的 userDao 实现类名,将它设置为你需要更换的持久化类名即可。我们终于可以松下一
31、口气了?不,矛盾仍然存在。我们引入了接口,引入了工厂模式,让我们的系统高度的灵活和可配置,同时也给开发带来了一些复杂度:1、本来只有一个实现类,后来却要为这个实现类引入了一个接口。2、引入了一个接口,却还需要额外开发一个对应的工厂类。3、工厂类过多时,管理、维护非常困难。比如:当 UserDao 的实现类是 JdbcUserDao,它使用 JDBC 技术来实现用户信息从持久化。也许要在取得 JdbcUserDao实例时传入数据库 Connection,这是仍少 UserDaoFactory 的硬编码。当然,面接口编程是实现软件的可维护性和可重用行的重要原则已经勿庸置疑。这样,第一个复杂度问题是
32、无法避免的,再说一个接口的开发和维护的工作量是微不足道的。但Spring 教程Email:版权所有,如果转载和篡改,请注明出处11后面两个复杂度的问题,我们是完全可以解决的:工厂模式的终极方案IoC 模式。重构第四步IoC 容器使用 IoC 容器,用户注册类 UserRegister 不用主动创建 UserDao 实现类的实例。由 IoC容器主动创建 UserDao 实现类的实例,并注入到用户注册类中。我们下面将使用 Spring 提供的 IoC 容器来管理我们的用户注册类。用户注册类 UserRegister 的部分代码如下:public class UserRegister private
33、 UserDao userDao = null;/由容器注入的实例对象public void setUserDao(UserDao userDao)this.userDao = userDao;/ UserRegister 的业务方法在其它的 UserRegister 方法中就可以直接使用 userDao 对象了,它的实例由 Spring 容器主动为它创建。但是,如何组装一个 UserDao 的实现类到 UserRegister 中呢?哦,Spring提供了配置文件来组装我们的组件。Spring 的配置文件 applicationContext.xml 代码片断如下:控制反转(IoC)/依赖注
34、入(DI)什么是控制反转/依赖注入?控制反转(IoC=Inversion of Control)IoC,用白话来讲,就是由容器控制程序之间的(依赖)关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:(依赖)控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。IoC 也称为好莱坞原则(Hollywood Principle):“Dont call us, well call you”。即,如果大腕明星想演节目,不用自己去找好莱坞公司,而是由好莱坞公司主动去找他们(当然,之前这些明星必须要在好莱坞登记过) 。正在业界为 IoC 争吵不休时,大师级人物 Marti
35、n Fowler 也站出来发话,以一篇经典文章Inversion of Control Containers and the Dependency Injection pattern为 IoC 正名,至此,IoC 又获得了一个新的名字:“依赖注入 (Dependency Injection) ”。相对 IoC 而言, “依赖注入”的确更加准确的描述了这种古老而又时兴的设计理念。Spring 教程Email:版权所有,如果转载和篡改,请注明出处12从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。例如前面用户注册的例子。U
36、serRegister 依赖于 UserDao 的实现类,在最后的改进中我们使用 IoC 容器在运行期动态的为 UserRegister 注入 UserDao 的实现类。即 UserRegister对 UserDao 的依赖关系由容器注入,UserRegister 不用关心 UserDao 的任何具体实现类。如果要更改用户的持久化方式,只要修改配置文件 applicationContext.xm 即可。依赖注入机制减轻了组件之间的依赖关系,同时也大大提高了组件的可移植性,这意味着,组件得到重用的机会将会更多。依赖注入的三种实现形式我们将组件的依赖关系由容器实现,那么容器如何知道一个组件依赖哪些
37、其它的组件呢?例如用户注册的例子:容器如何得知 UserRegister 依赖于 UserDao 呢。这样,我们的组件必须提供一系列所谓的回调方法(这个方法并不是具体的 Java 类的方法) ,这些回调方法会告知容器它所依赖的组件。根据回调方法的不同,我们可以将 IoC 分为三种形式:Type1接口注入( Interface Injection)它是在一个接口中定义需要注入的信息,并通过接口完成注入。Apache Avalon 是一个较为典型的 Type1 型 IOC 容器,WebWork 框架的 IoC 容器也是 Type1 型。当然,使用接口注入我们首先要定义一个接口,组件的注入将通过这个
38、接口进行。我们还是以用户注册为例,我们开发一个 InjectUserDao 接口,它的用途是将一个UserDao 实例注入到实现该接口的类中。InjectUserDao 接口代码如下:public interface InjectUserDao public void setUserDao(UserDao userDao);UserRegister 需要容器为它注入一个 UserDao 的实例,则它必须实现 InjectUserDao 接口。UserRegister 部分代码如下:public class UserRegister implements InjectUserDaoprivate
39、 UserDao userDao = null;/该对象实例由容器注入public void setUserDao(UserDao userDao) this.userDao = userDao;/ UserRegister 的其它业务方法同时,我们需要配置 InjectUserDao 接口和 UserDao 的实现类。如果使用 WebWork 框架则配置文件如下:requestcom.dev.spring.simple.MemoryUserDaoSpring 教程Email:版权所有,如果转载和篡改,请注明出处13com.dev.spring.simple.InjectUserDao这样,当
40、 IoC 容器判断出 UserRegister 组件实现了 InjectUserDao 接口时,它就将MemoryUserDao 实例注入到 UserRegister 组件中。Type2设值方法注入(Setter Injection)在各种类型的依赖注入模式中,设值注入模式在实际开发中得到了最广泛的应用(其中很大一部分得力于 Spring 框架的影响) 。基于设置模式的依赖注入机制更加直观、也更加自然。前面的用户注册示例,就是典型的设置注入,即通过类的 setter 方法完成依赖关系的设置。Type3构造子注入(Constructor Injection)构造子注入,即通过构造函数完成依赖关系
41、的设定。将用户注册示例该为构造子注入,UserRegister 代码如下:public class UserRegister private UserDao userDao = null;/由容器通过构造函数注入的实例对象public UserRegister(UserDao userDao)this.userDao = userDao;/业务方法几种依赖注入模式的对比总结接口注入模式因为历史较为悠久,在很多容器中都已经得到应用。但由于其在灵活性、易用性上不如其他两种注入模式,因而在 IOC 的专题世界内并不被看好。Type2 和 Type3 型的依赖注入实现则是目前主流的 IOC 实现模式。
42、这两种实现方式各有特点,也各具优势。Type2 设值注入的优势1 对于习惯了传统 JavaBean 开发的程序员而言,通过 setter 方法设定依赖关系显得更加直观,更加自然。2 如果依赖关系(或继承关系)较为复杂,那么 Type3 模式的构造函数也会相当庞大(我们需要在构造函数中设定所有依赖关系) ,此时 Type2 模式往往更为简洁。3 对于某些第三方类库而言,可能要求我们的组件必须提供一个默认的构造函数(如 Struts 中的 Action) ,此时 Type3 类型的依赖注入机制就体现出其局限性,难以完成我Spring 教程Email:版权所有,如果转载和篡改,请注明出处14们期望的
43、功能。Type3 构造子注入的优势:1 “在构造期即创建一个完整、合法的对象” ,对于这条 Java 设计原则,Type3 无疑是最好的响应者。2 避免了繁琐的 setter 方法的编写,所有依赖关系均在构造函数中设定,依赖关系集中呈现,更加易读。3 由于没有 setter 方法,依赖关系在构造时由容器一次性设定,因此组件在被创建之后即处于相对“不变”的稳定状态,无需担心上层代码在调用过程中执行 setter 方法对组件依赖关系产生破坏,特别是对于 Singleton 模式的组件而言,这可能对整个系统产生重大的影响。4 同样,由于关联关系仅在构造函数中表达,只有组件创建者需要关心组件内部的依赖
44、关系。对调用者而言,组件中的依赖关系处于黑盒之中。对上层屏蔽不必要的信息,也为系统的层次清晰性提供了保证。5 通过构造子注入,意味着我们可以在构造函数中决定依赖关系的注入顺序,对于一个大量依赖外部服务的组件而言,依赖关系的获得顺序可能非常重要,比如某个依赖关系注入的先决条件是组件的 UserDao 及相关资源已经被设定。可见,Type3 和 Type2 模式各有千秋,而 Spring、PicoContainer 都对 Type3 和 Type2类型的依赖注入机制提供了良好支持。这也就为我们提供了更多的选择余地。理论上,以Type3 类型为主,辅之以 Type2 类型机制作为补充,可以达到最好的
45、依赖注入效果,不过对于基于 Spring Framework 开发的应用而言,Type2 使用更加广泛。BeanFactoryBeanFactory 是 Spring 的“心脏” 。它就是 Spring IoC 容器的真面目。Spring 使用BeanFactory 来实例化、配置和管理 Bean。但是,在大多数情况我们并不直接使用BeanFactory,而是使用 ApplicationContext。它也是 BeanFactory 的一个实现,但是它添加了一系列“框架”的特征,比如:国际化支持、资源访问、事件传播等。ApplicationContext 我们将在后面章节中介绍。BeanFac
46、tory 其实是一个接口org.springframework.beans.factory.BeanFactory,它可以配置和管理几乎所有的 Java 类。当然,具体的工作是由实现 BeanFactory 接口的实现类完成。我们最常用的 BeanFactory 实现是 org.springframework.beans.factory.xml.XmlBeanFactory。它从 XML 文件中读取 Bean 的定义信息。当 BeanFactory 被创建时,Spring 验证每个 Bean的配置。当然,要等 Bean 创建之后才能设置 Bean 的属性。单例(Singleton)Bean 在
47、启动时就会被 BeanFactory 实例化,其它的 Bean 在请求时创建。根据 BeanFactory 的 Java 文档(Javadocs)介绍, “Bean 定义的持久化方式没有任何的限制:LDAP、RDBMS、XML、属性文件,等等” 。现在 Spring 已提供了 XML 文件和属性文件的实现。无疑,XML 文件是定义 Bean 的最佳方式。BeanFactory 是初始化 Bean 和调用它们生命周期方法的“吃苦耐劳者” 。注意,BeanFactory 只能管理单例(Singleton)Bean 的生命周期。它不能管理原型(prototype,非单BeanFactory Appl
48、icationContextSpring 教程Email:版权所有,如果转载和篡改,请注明出处15例)Bean 的生命周期。这是因为原型 Bean 实例被创建之后便被传给了客户端 ,容器失去了对它们的引用。BeanFactory 管理 Bean(组件)的生命周期下图描述了 Bean 的生命周期。它是由 IoC 容器控制。IoC 容器定义 Bean 操作的规则,即Bean 的定义(BeanDefinition) 。Bean 的定义包含了 BeanFactory 在创建 Bean 实例时需要的所有信息。BeanFactory 首先通过构造函数创建一个 Bean 实例,之后它会执行 Bean 实例的一系列之前初始化动作,初始化结束 Bean 将进入准备就绪(ready)状态,这时应用程序就可以获取这些 Bean 实例了。最后,当你销毁单例(Singleton)Bean 时,它会调用相应的销毁方法,结束 Bean 实例的生命周期。(图Bea