//www.springframework.org/schema/beans"
xmlns:
xsi="http:
//www.w3.org/2001/XMLSchema-instance"
xmlns:
context="http:
//www.springframework.org/schema/context"
xmlns:
aop="http:
//www.springframework.org/schema/aop"
xmlns:
tx="http:
//www.springframework.org/schema/tx"
xsi:
schemaLocation="
http:
//www.springframework.org/schema/beanshttp:
//www.springframework.org/schema/beans/spring-beans-3.0.xsd
http:
//www.springframework.org/schema/contexthttp:
//www.springframework.org/schema/context/spring-context-3.0.xsd
http:
//www.springframework.org/schema/aophttp:
//www.springframework.org/schema/aop/spring-aop-3.0.xsd
http:
//www.springframework.org/schema/txhttp:
//www.springframework.org/schema/tx/spring-tx-3.0.xsd
">
………………………
4:
配置applicationContext.xml如下:
java代码:
编写客户端如下:
java代码:
packagecn.javass.Spring3.hello;
importorg.springframework.context.ApplicationContext;
importorg.springframework.context.support.ClassPathXmlApplicationContext;
publicclassClient{
publicstaticvoidmain(String[]args){
ApplicationContextcontext=newClassPathXmlApplicationContext(
newString[]{"applicationContext.xml"});
HelloApiapi=(HelloApi)context.getBean("helloBean");
Strings=api.helloSpring3(3);
System.out.println("thes="+s);
}
}
审视和结论
1:
所有代码中(除测试代码之外),并没有出现Spring的任何组件。
2:
客户代码(这里就是我们的测试代码)仅仅面向接口编程,而无需知道实现类的具体名称。
同时,我们可以很简单的通过修改配置文件来切换具体的底层实现类。
结论
1:
首先,我们的组件并不需要实现框架指定的接口,因此可以轻松的将组件从Spring脱离,甚至不需要任何修改(这在基于EJB架实现的应用中是难以想象的)。
2:
其次,组件间的依赖关系减少,极大改善了代码的可重用性和可维护性
3:
面向接口编程
什么是Spring中的Bean
在Spring中,那些组成应用的主体及由SpringIoC容器所管理的对象被称之为bean。
简单地讲,bean就是由Spring容器初始化、装配及被管理的对象,除此之外,bean就没有特别之处了(与应用中的其他对象没有什么区别)。
而bean定义以及bean相互间的依赖关系将通过配置元数据来描述。
为什么使用Bean这个名字
使用‘bean’这个名字而不是‘组件’(component)或‘对象’(object)的动机源于Spring框架本身(部分原因则是相对于复杂的EJB而言的)。
Spring的IoC容器
org.springframework.beans.factory.BeanFactory是SpringIoC容器的实际代表者,IoC容器负责容纳bean,并对bean进行管理。
SpringIoC容器将读取配置元数据;并通过它对应用中各个对象进行实例化、配置以及组装。
通常情况下我们使用简单直观的XML来作为配置元数据的描述格式。
在XML配置元数据中我们可以对那些我们希望通过SpringIoC容器管理的bean进行定义。
IoC/DI是Spring最核心的功能之一,Spring框架所提供的众多功能之所以能成为一个整体正是建立在IoC的基础之上
BeanFactory和ApplicationContext
org.springframework.beans及org.springframework.context包是SpringIoC容器的基础。
BeanFactory提供的高级配置机制,使得管理任何性质的对象成为可能。
ApplicationContext是BeanFactory的扩展,功能得到了进一步增强,比如更易与SpringAOP集成、消息资源处理(国际化处理)、事件传递及各种不同应用层的context实现(如针对web应用的WebApplicationContext)。
接口选择之惑
在实际应用中,用户有时候不知道到底是选择BeanFactory接口还是ApplicationContext接口。
但是通常在构建JavaEE应用时,使用ApplicationContext将是更好的选择,因为它不仅提供了BeanFactory的所有特性,同时也允许使用更多的声明方式来得到我们想要的功能。
简而言之,BeanFactory提供了配制框架及基本功能,而ApplicationContext则增加了更 多支持企业核心内容的功能。
ApplicationContext完全由BeanFactory扩展而来,因而BeanFactory所具备的能力和行为也适用于ApplicationContext。
SpringIoC容器的实例化非常简单,如下面的例子:
1:
第一种:
java代码:
Resourceresource=newFileSystemResource("beans.xml");
BeanFactoryfactory=newXmlBeanFactory(resource);
2:
第二种:
java代码:
ClassPathResourceresource=newClassPathResource("beans.xml");
BeanFactoryfactory=newXmlBeanFactory(resource);
3:
第三种:
java代码:
ApplicationContextcontext=newClassPathXmlApplicationContext(
newString[]{"applicationContext.xml","applicationContext-part2.xml"});
//ofcourse,anApplicationContextisjustaBeanFactory
BeanFactoryfactory=(BeanFactory)context;
读取多个配置文件
第一种方法:
为了加载多个XML文件生成一个ApplicationContext实例,可以将文件路径作为字符串数组传给ApplicationContext构造器。
而beanfactory将通过调用beandefintionreader从多个文件中读取bean定义。
通常情况下,Spring团队倾向于上述做法,因为这样各个配置并不会查觉到它们与其他配置文件的组合。
第二种方法:
使用一个或多个的元素来从另外一个或多个文件加载bean定义。
所有的元素必须放在元素之前以完成bean定义的导入。
让我们看个例子:
java代码:
配置文件中常见的配置内容
在IoC容器内部,bean的定义由BeanDefinition对象来表示,该定义将包含以下信息:
1:
全限定类名:
这通常就是已定义bean的实际实现类。
如果通过调用staticfactory方法来实例化bean,而不是使用常规的构造器,那么类名称实际上就是工厂类的类名。
2:
bean行为的定义,即创建模式(prototype还是singleton)、自动装配模式、依赖检查模式、初始化以及销毁方法。
这些定义将决定bean在容器中的行为。
3:
用于创建bean实例的构造器参数及属性值。
比如使用bean来定义连接池,可以通过属性或者构造参数指定连接数,以及连接池大小限制等。
4:
bean之间的关系,即协作(或者称依赖)。
Bean的命名
每个bean都有一个或多个id(或称之为标识符或名称,在术语上可以理解成一回事),这些id在当前IoC容器中必须唯一。
当然也可以为每个bean定义一个name,但是并不是必须的,如果没有指定,那么容器将为其生成一个惟一的name。
对于不指定name属性的原因我们会在后面介绍(比如内部bean就不需要)。
Bean命名的约定
bean的命名采用标准的Java命名约定,即小写字母开头,首字母大写间隔的命名方式。
如accountManager、accountService等等。
对bean采用统一的命名约定将会使配置更加简单易懂。
而且在使用SpringAOP,这种简单的命名方式将会令你受益匪浅。
Bean的别名
一个Bean要提供多个名称,可以通过alias属性来加以指定,示例如下:
容器如何实例化Bean
当采用XML描述配置元数据时,将通过元素的class属性来指定实例化对象的类型。
class属性主要有两种用途:
在大多数情况下,容器将直接通过 反射调 用指定类的构造器来创建bean(这有点等类似于在Java代码中使用new操作符);在极少数情况下,容器将调用类的静态工厂方法来创建bean实例,class属性将用来指定实际具有静态工厂方法的类(至于调用静态工厂方法创建的对象类型是当前class还是其他的class则无关紧要)。
用构造器来实例化Bean,前面的实例就是
使用静态工厂方法实例化
采用静态工厂方法创建bean时,除了需要指定class属性外,还需要通过factory-method属性来指定创建bean实例的工厂方法,示例如下:
class="examples.ExampleBean2"
factory-method="createInstance"/>
使用实例工厂方法实例化
使用此机制,class属性必须为空,而factory-bean属性必须指定为当前(或其祖先)容器中包含工厂方法的bean的名称,而该工厂bean的工厂方法本身必须通过factory-method属性来设定,并且这个方法不能是静态的,示例如下:
使用容器
从本质上讲,BeanFactory仅仅只是一个维护bean定义以及相互依赖关系的高级工厂接口。
使用getBean(String)方法就可以取得bean的实例;BeanFactory提供的方法极其简单。
n依赖注入(DI) 背后的基本原理
是对象之间的依赖关系(即一起工作的其它对象)只会通过以下几种方式来实现:
构造器的参数、 工厂方法的参数,或 给由构造函数或者工厂方法创建的对象设 置属性。
因此,容器的工作就是创建bean时注入那些依赖关系。
相对于由bean自己来控制其实例化、直接在构造器中指定依赖关系或则类似服务定位器(ServiceLocator)模式这3种自主控制依赖关系注入的方法来说,控制从根本上发生了倒转,这也正是控制反转IoC名字的由来。
应用依赖注入(DI)的好处、
应用DI原则后,代码将更加清晰。
而且当bean自己不再担心对象之间的依赖关系(以及在何时何地指定这种依赖关系和依赖的实际类是什么)之后,实现更高层次的 松耦合将易如反掌。
依赖注入(DI)基本的实现方式
DI主要有两种注入方式,即 Setter注入和构造器注入。
通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即可实现基于setter的DI。
示例如下:
示例——Java类
java代码:
publicclassHelloImplimplementsHelloApi{
privateStringname="";
publicvoidsetName(Stringname){
this.name=name;
}
publicStringhelloSpring3(inta){
System.out.println("helloSpring3==="+a+",name="+name);
return"Ok,a="+a;
}
}
示例——配置文件
javassSpring3
示例——Java类
java代码:
publicclassHelloImplimplementsHelloApi{
privateStringname="";
publicHelloImpl(Stringname){
this.name=name;
}
publicStringhelloSpring3(inta){
System.out.println("helloSpring3==="+a+",name="+name);
return"Ok,a="+a;
}
}
示例——配置文件
java代码:
javassSpring3
默认解析方式
构造器参数将根据类型来进行匹配。
如果bean定义中的构造器参数类型明确,那么bean定义中的参数顺序就是对应构造器参数的顺序
构造器参数类型匹配
可以使用type属性来显式的指定参数所对应的简单类型。
例如:
java代码:
构造器参数的索引
使用index属性可以显式的指定构造器参数出现顺序。
例如:
java代码:
构造器参数的名称
在Spring3里面,可以使用构造器参数的名称来直接赋值。
例如:
java代码:
直接量(基本类型、Strings类型等)
元素通过字符串来指定属性或构造器参数的值。
JavaBean属性编辑器将把字符串从java.lang.String类型转化为实际的属性或参数类型。
示例:
java代码:
oracle.jdbc.driver.OracleDriver