Spring IoC,IoC原理

一、IoC概念及原理

IOC的别名:依赖注入(DI)

2004年,Martin Fowler探讨了同一个问题,既然IOC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦
我们举一个生活中的例子,来帮助理解依赖注入的过程。大家对USB接口和USB设备应该都很熟悉吧,USB为我们使用电脑提供了很大的方便,现在有很多的外部设备都支持USB接口。

图5:USB接口和USB设备

现在,我们利用电脑主机和USB接口来实现一个任务:从外部USB设备读取一个文件。
电脑主机读取文件的时候,它一点也不会关心USB接口上连接的是什么外部设备,而且它确实也无须知道。它的任务就是读取USB接口,挂接的外部设备只要符合USB接口标准即可。所以,如果我给电脑主机连接上一个U盘,那么主机就从U盘上读取文件;如果我给电脑主机连接上一个外置硬盘,那么电脑主机就从外置硬盘上读取文件。挂接外部设备的权力由我作主,即控制权归我,至于USB接口挂接的是什么设备,电脑主机是决定不了,它只能被动的接受。电脑主机需要外部设备的时候,根本不用它告诉我,我就会主动帮它挂上它想要的外部设备,你看我的服务是多么的到位。这就是我们生活中常见的一个依赖注入的例子。在这个过程中,我就起到了IOC容器的作用
通过这个例子,依赖注入的思路已经非常清楚:当电脑主机读取文件的时候,我就把它所要依赖的外部设备,帮他挂接上。整个外部设备注入的过程和一个被依赖的对象在系统运行时被注入另外一个对象内部的过程完全一样。
我们把依赖注入应用到软件系统中,再来描述一下这个过程:
对象A依赖于对象B,当对象 A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A。IOC容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了,而再也不用去关心你所用的东西是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由IOC容器包办。
在传统的实现中,由程序内部代码来控制组件之间的关系。我们经常使用new关键字来实现两个组件之间关系的组合,这种实现方式会造成组件之间耦合。IOC很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,也就是说由容器在运行期将组件间的某种依赖关系动态注入组件中。

 IOC为我们带来了什么好处

我们还是从USB的例子说起,使用USB外部设备比使用内置硬盘,到底带来什么好处?
第一、USB设备作为电脑主机的外部设备,在插入主机之前,与电脑主机没有任何的关系,只有被我们连接在一起之后,两者才发生联系,具有相关性。所以,无论两者中的任何一方出现什么的问题,都不会影响另一方的运行。这种特性体现在软件工程中,就是可维护性比较好,非常便于进行单元测试,便于调试程序和诊断故障。代码中的每一个Class都可以单独测试,彼此之间互不影响,只要保证自身的功能无误即可,这就是组件之间低耦合或者无耦合带来的好处。
第二、USB设备和电脑主机的之间无关性,还带来了另外一个好处,生产USB设备的厂商和生产电脑主机的厂商完全可以是互不相干的人,各干各事,他们之间唯一需要遵守的就是USB接口标准。这种特性体现在软件开发过程中,好处可是太大了。每个开发团队的成员都只需要关心实现自身的业务逻辑,完全不用去关心其它的人工作进展,因为你的任务跟别人没有任何关系,你的任务可以单独测试,你的任务也不用依赖于别人的组件,再也不用扯不清责任了。所以,在一个大中型项目中,团队成员分工明确、责任明晰,很容易将一个大的任务划分为细小的任务,开发效率和产品质量必将得到大幅度的提高。
第三、同一个USB外部设备可以插接到任何支持USB的设备,可以插接到电脑主机,也可以插接到DV机,USB外部设备可以被反复利用。在软件工程中,这种特性就是可复用性好,我们可以把具有普遍性的常用组件独立出来,反复利用到项目中的其它部分,或者是其它项目,当然这也是面向对象的基本特征。显然,IOC不仅更好地贯彻了这个原则,提高了模块的可复用性。符合接口标准的实现,都可以插接到支持此标准的模块中。
第四、同USB外部设备一样,模块具有热插拔特性。IOC生成对象的方式转为外置方式,也就是把对象生成放在配置文件里进行定义,这样,当我们更换一个实现子类将会变得很简单,只要修改配置文件就可以了,完全具有热插拨的特性。
以上几点好处,难道还不足以打动我们,让我们在项目开发过程中使用IOC框架吗?

5.  IOC容器的技术剖析(原理)
IOC中最基本的技术就是“反射(Reflection)”编程,目前.Net C#、Java和PHP5等语言均支持,其中PHP5的技术书籍中,有时候也被翻译成“映射”。有关反射的概念和用法,大家应该都很清楚,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象。这种编程方式可以让对象在生成时才决定到底是哪一种对象。反射的应用是很广泛的,很多的成熟的框架,比如象Java中的Hibernate、Spring框架,.Net中 NHibernate、Spring.Net框架都是把“反射”做为最基本的技术手段。
反射技术其实很早就出现了,但一直被忽略,没有被进一步的利用。当时的反射编程方式相对于正常的对象生成方式要慢至少得10倍。现在的反射技术经过改良优化,已经非常成熟,反射方式生成对象和通常对象生成方式,速度已经相差不大了,大约为1-2倍的差距。
我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
6.  IOC容器的一些产品
Sun ONE技术体系下的IOC容器有:轻量级的有Spring、Guice、Pico Container、Avalon、HiveMind;重量级的有EJB;不轻不重的有JBoss,Jdon等等。Spring框架作为Java开发中SSH(Struts、Spring、Hibernate)三剑客之一,大中小项目中都有使用,非常成熟,应用广泛,EJB在关键性的工业级项目中也被使用,比如某些电信业务。
.Net技术体系下的IOC容器有:Spring.Net、Castle等等。Spring.Net是从Java的Spring移植过来的IOC容器,Castle的IOC容器就是Windsor部分。它们均是轻量级的框架,比较成熟,其中Spring.Net已经被逐渐应用于各种项目中。
7. 使用IOC框架应该注意什么
使用IOC框架产品能够给我们的开发过程带来很大的好处,但是也要充分认识引入IOC框架的缺点,做到心中有数,杜绝滥用框架。
第一、软件系统中由于引入了第三方IOC容器,生成对象的步骤变得有些复杂,本来是两者之间的事情,又凭空多出一道手续,所以,我们在刚开始使用IOC框架的时候,会感觉系统变得不太直观。所以,引入了一个全新的框架,就会增加团队成员学习和认识的培训成本,并且在以后的运行维护中,还得让新加入者具备同样的知识体系。
第二、由于IOC容器生成对象是通过反射方式,在运行效率上有一定的损耗。如果你要追求运行效率的话,就必须对此进行权衡。
第三、具体到IOC框架产品(比如:Spring)来讲,需要进行大量的配制工作,比较繁琐,对于一些小的项目而言,客观上也可能加大一些工作成本。
第四、IOC框架产品本身的成熟度需要进行评估,如果引入一个不成熟的IOC框架产品,那么会影响到整个项目,所以这也是一个隐性的风险。
我们大体可以得出这样的结论:一些工作量不大的项目或者产品,不太适合使用IOC框架产品。另外,如果团队成员的知识能力欠缺,对于IOC框架产品缺乏深入的理解,也不要贸然引入。最后,特别强调运行效率的项目或者产品,也不太适合引入IOC框架产品,象WEB2.0网站就是这种情况。

7.IoC的概述

  IoC(控制反转:Inverse of Control)是Spring容器的内核,AOP、声明式事务等功能在此基础上开花结果。它涉及代码解耦、设计模式、代码优化等问题的考量。

  Spring通过一个配置文件描述Bean及Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。Spring的IoC容器在完成这些底层工作的基础上,还提供了Bean实例缓存、生命周期管理、Bean实例代理、事件发布、资源装载的高级服务。

 BeanFactory继承关系图

  org.springframework.beans及org.springframework.context包是Spring IoC容器的基础。Bean工厂(org.springframework.beans.factory.BeanFactory)是Spring框架中最核心的接口,它提供了高级IoC的配置机制。BeanFactory使管理不同类型的Java对象成为可能,ApplicationContext是BeanFactory的扩展,功能得到了进一步增强,比如更易与Spring AOP集成、消息资源处理(国际化处理)、事件传递、其它第三方框架(ehCache)集成及各种不同应用层的context实现(如针对web应用的WebApplicationContext)。一般称BeanFactory为IoC容器,而称ApplicationContext为应用上下文。

  对于两者用途,我们可以进行简单划分:BeanFactory是Spring框架的基础设施,面向Spring本身;ApplicationContext面向使用Spring框架的开发者。我们一般都不直接用BeanFactory,而是用它的实现类ApplicationContext,这个类会自动解析我们配置的applicationContext.xml,然后根据我们配置的bean来new对象,将new好的对象放进一个Map中,键就是我们bean的id,值就是new的对象。几乎所有的应用场合我们都直接使用ApplicationContext而非底层的BeanFactory。BeanFactory与ApplicationContext的区别

二、Bean的概念

  由IOC容器管理的那些组成你应用程序的对象我们就叫它Bean, Bean就是由Spring容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别了。那IOC怎样确定如何实例化Bean、管理Bean之间的依赖关系以及管理Bean呢?这就需要配置元数据,在Spring中由BeanDefinition代表,后边会详细介绍,配置元数据指定如何实例化Bean、如何组装Bean等。

三、装配Bean:即依赖注入 

在Spring中,对象间的协作是通过IoC机制完成的。
反向控制也叫依赖注入(Dependency Injection,DI),简单来说就是将JavaBean需要的对象通过配置文件加载进来。

Spring提供了两种装配Bean的容器,一是BeanFactoy,另一个是ApplicationContext。
两者做为容器,所有的Bean都应该通过容器装配,而容器也知道自己都装配了哪些Bean。

Bean装配实际上就是让容器知道程序中都有哪些Bean,可以通过以下两种方式实现:

  • 配置文件(最为常用,体现了依赖注入DI的思想)
  • 编程方式(写的过程中向BeanFactory去注册)

ApplicationContext与BeanFactory都是接口,ApplicationContext是由BeanFactory接口扩展而来,它增强了BeanFactory的功能。

Bean容器能够加载的对象并不一定是严格遵循JavaBeans规范的Java类,任何可实例化的类都可以通过Spring Bean容器加载进来。通常称这些类为POJO。

要记住,BeanFactory不仅仅只具备实例化Bean的功能,它还知道所有的Bean,可以配置和管理它们。
Spring给出一些BeanFactory的实现类,其中最为常用的是XmlBeanFactory。
1、通过文件系统
Resource res = new FileSystemResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
2、通过类路径
ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
3、通过ApplicationContext加载
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
BeanFactory factory = (BeanFactory) appContext;

四、bean装配方式:

通过Setter方法、通过构造函数、自动装配三种方法都可以达到装配Bean的目的。
其中使用setter方法装配Bean是最为常用的。

先说一下setter方法的装配:
setter方法可以装配基本类型、Bean类型、内部bean类型、集合类型、设置空值

所谓Bean类型属性是指这个属性类型是个javaBean,并且这个javaBean必须已经在Spring中注册过了!

 

自动装配:

可以通过spring框架自动为bean装配属性。
自动装配只能装配bean类型,即取代ref元素。
使用自动装配只需在bean元素中加入属性autowire,自动装配可被手动装配覆盖。
也可以选择在beans中加入default-autowire属性,为所有bean设置默认自动装配。

“no“-不使用自动装配,spring推荐。
“byName”-依据名称或ID装配bean,如果没有找到,则不装配,可依赖dependency-check属性检查是否装配。
 “byType”-依据类型自动装配,如果存在两个以上同种类型,则抛出异常。
“constructor”-依据当前bean构造函数装配,查找与构造函数参数同类型bean
“autodetect”-自动检测,先通过constructor,再使用byType

Spring是不推荐使用自动装配的,首先是系统开销的问题,还有就是同名的设置可能会导致意外的问题……
优点
自动装配可以大大地减少属性和构造器参数的指派。
自动装配也可以在解析对象时更新配置。
任何事物有好就有坏,那自动装配有啥缺点呢?
缺点
在property和constructor-arg设置中的依赖总是重载自动装配,我们无法对原始类型(如int,long,boolean等就是首字母小写的那些类型),还有String,Classes做自动装配。这是受限于设计。
自动装配跟直接装配(explicit wiring)相比较,在准确性方便还是差那么点,虽然没有明确地说明,但是Spring还是尽量避免这种模棱两可的情况,导致出现没预料到的结果。
Spring容器生成文档的工具可能会不能使用装配的信息。
容器中多个bean的定义可能要对setter和构造器参数做类型匹配才能做依赖注入,虽然对于array,collection和map来说不是啥问题,但是对于只有单一值的依赖来讲,这就有点讲不清楚了,所以如果没有唯一的bean定义,那只能抛出异常。

单例和原型的问题:依赖于singleton参数,true为单例模式。
延迟加载:体现在整个容器初始化的时候立刻加载bean还是等到使用这个bean的时候才加载。
   factory.getBean的时候就已经是开始使用bean了,并不一定要访问其中的属性才算是使用。

   只有ApplicationContext才能做到容器初始化的时候立刻加载bean,BeanFactory是做不到的。

注册bean的时候,name属性是用的别名,id属性标明真名。name一般是在web开发里面去用,name里面是可以包含一些特殊符号的,用于告诉开发者当前的属性代表什么具体的实际意思。

setter方法、构造函数和自动装配三种装配bean的方式,见Spring Bean管理

五、Spring Bean 生命周期

任何一个事物都有自己的生命周期,生命的开始、生命中、生命结束。大家最熟悉的应该是servlet 的生命周期吧。和 servlet 一样 spring bean 也有自己的生命周期。本文我就向大家讲述 spring bean 的生命周期,这个对理解 spring 框架有非常好的作用。

大家应该知道spring 中有几种供 springbean 生存的容器: BeanFactory 、 ApplicationContext 、webApplicationContext 。由于 ApplicationContext 和 webApplicationContext 基本一样。所有这里就只介绍BeanFactory 和 ApplicationContext 。

理解springBean 的生命周期主要通过两个层面来理解。其一是 Bean 的作用范围,其一是实例化 Bean 时所经历的一系列阶段。

一、 BeanFactory

下图描述了BeanFactory 中 bean 生命周期的完整过程

1. 当调用者通过 getBean( name )向 容器寻找Bean 时,如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口,在实例 bean 之前,将调用该接口的 postProcessBeforeInstantiation ()方法,

2. 根据配置情况调用 Bean构造函数或工厂方法实例化 bean  

3. 如果容器注册了 org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口,在实例 bean 之后,调用该接口的 postProcessAfterInstantiation ()方法,可以在这里对已经实例化的对象进行一些装饰。

4. 受用依赖注入,Spring 按照 Bean 定义信息配置 Bean 的所有属性 ,在设置每个属性之前将调用InstantiationAwareBeanPostProcess接口的 postProcessPropertyValues ()方法 。 

5 .如果 Bean 实现了 BeanNameAware 接口,工厂调用 Bean 的 setBeanName() 方法传递 Bean 的 ID 。 

6 .如果 Bean 实现了 BeanFactoryAware 接口,工厂调用 setBeanFactory() 方法传入工厂自身。 

7 .如果 BeanPostProcessor 和 Bean 关联,那么 将调用该接口 的postProcessBeforeInitialzation() 方法 对 bean进行加工操作,这个非常重要, spring 的 AOP 就是用它实现的。  

8. 如果bean 实现了 InitializingBean 接口,将调用 afterPropertiesSet ()方法

9 如果Bean 指定了 init-method 方法,它将被调用。 

10 如果有BeanPsotProcessor 和 Bean 关联,那么它们的 postProcessAfterInitialization() 方法将被调用。 到这个时候, Bean 已经可以被应用系统使用了  。

11. 如果在<bean> 中指定了该 bean 的作用范围为 scope="prototype", 将 bean 的调用者,调用者管理该 bean 的生命周期, spring 不在管理该 bean 。

12. 如果在<bean> 中指定了该 bean 的作用范围为 scope="singleton", 则将该 bean 放入 springIOC 的缓存池中,将触发 spring 对该 bean 的生命周期管理。

13. 有两种方法可以把它从Bean Factory 中删除掉 :  

1.如果 Bean 实现了 DisposableBean 接口, destory() 方法被调用。 

2.如果指定了订制的销毁方法,就调用这个方法。 

总结:

Bean的完整生命周期从 spring 容器开始实例化 bean 开始,到销毁。可以从三点来理解

1、 bean自身的方法:包括构造方法、 set 方法、 init-method 指定的方法、 destroy-method 指定的方法

2、 Bean级生命周期接口方法:如 BeanNameAware 、 BeanFactoryAware 等这些接口方法由 bean类实现。

3、 容器级生命周期接口方法:上图中带星的。有InstantiationAwareBeanPostProcessor 、 BeanPostProcessor 等。一般称为后处理 器。他们一般不由bean 本身实现,独立存在,注册到 spring 容器中。 Spring 通过接口反射预先知道,当 spring 容器创建任何 bean 时,这些后处理器都会发生作用。所以他们是全局的,用户可以通过编码对只感兴趣的 bean 进行处理。

 Bean级生命周期接口和容器级生命周期接口是个性和共性辩证统一的思想,前者解决 bean 的个性化处理问题,而后者解决容器中某些 bean 共性化处理问题。

二、 ApplicationContext

下图描述了ApplicationContext 的生命周期

 

通过上图很容易发现其实应该上下文和BeanFactory 只是多了一个接口, 如果Bean 实现了 ApplicationContextAwre接口, setApplicationContext() 方法被调用。

还有如果配置文件中生明了工厂后处理器接口 BeanFactoryPostProcessor的实现类,则应用上下文在装配配置文件之后初始化 bean 之前将调用该接口对配置信息进行加工。

还有应该上下文的这些后处理器只要和普通的bean 一样配置在 spring 配置文件中就行了,不需要事先声明。

三、 总结  

Spring为 bean 提供了细致周全的生命周期过程,通过实现特定的接口或通过《 bean 》属性设置,都可以对 bean 的生命周期过程施加影响。我们可以随意的配置 bean 的属性,使用非常灵活。但笔者在这里建议大家不要过多的使用 bean 实现接口,因为这样会使你的代码和 spring 聚合比较紧密。可以考虑使用后处理 bean ,来实现一些特殊的功能,并且非常的方便。

 

六、bean的作用域

一、Spring Framework支持五种作用域(其中有三种只能用在基于web的Spring ApplicationContext)。 
内置支持的作用域分列如下:

当通过spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:

  • singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例

  • prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例

  • request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效

  • session:对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效

  • globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效

  其中比较常用的是singleton和prototype两种作用域。对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态

  如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。

 

Spring Bean线程安全这个问题

Spring Bean线程安全这个问题,要从单例与原型Bean分别进行说明。
prototype Bean:对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
singleton Bean:对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。

如果singleton Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。

  • 有状态对象(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。
  • 无状态对象(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。

对于有状态的bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。

使用ThreadLocal的好处
使得多线程场景下,多个线程对这个单例Bean的成员变量并不存在资源的竞争,因为ThreadLocal为每个线程保存线程私有的数据。这是一种以空间换时间的方式。

当然也可以通过加锁的方法来解决线程安全,这种以时间换空间的场景在高并发场景下显然是不实际的。

 

七、深入理解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 管理其生命周期行为。

posted on 2015-08-09 12:09  duanxz  阅读(740)  评论(0编辑  收藏  举报