注:spring 4.0 再也不支持 ref local 标签。(参考:http://blog.csdn.net/fo11ower/article/details/51162312)html
1.简介java
org.springframework.beans 及 org.springframework.context 包是 Spring IoC 容器的基础。Blinux
BeanFactory 提供的高级配置机制,使得管理任何性质的对象成为可能。ApplicationContext 是 BeanFactory 的扩展,功能获得了进一步的增强。简而言之,BeanFactory 提供了配置框架及基本功能,而 ApplicationContext 彻底由 BeanFactory 扩展而来,于是 BeanFactory 所具有的能力和行为也适用于 ApplicationContext。web
2.容器和 bean 的基本原理spring
在 Spring 中,那些组成应用的主体及由 Spring IoC 容器所管理的对象被称之为 bean。简单的讲,bean 就是由 Spring 容器初始化、装配及被管理的对象。而 bean 定义以及 bean 相互间的依赖关系将经过配置元数据来描述。编程
2.1容器api
org.springframework.factory.BeanFactory 是 SpringIoC 容器的实际表明者,IoC 容器负责容纳此前所描述的 bean,并对 bean 进行管理。数组
在 Spring 中,BeanFactory 是 IoC 容器的核心接口。它的职责包括:实例化、定位、配置应用程序中的对象及创建这些对象间的依赖。session
Spring 提供了不少易用的 BeanFactory 实现,XmlBeanFactory 就是最经常使用的一个。http://docs.spring.io/spring/docs/current/javadoc-api/app
2.1.1配置元数据
从上图能够看到,Spring IoC 容器将读取配置元数据;并经过它对应用中各个对象进行实例化、配置及组装。
Spring 支持三种配置元数据格式:XML 格式、Java 属性文件格式或使用 Spring 公共 API 编程实现。一般状况下咱们使用简单直观的 XML 来做为配置元数据的描述格式。
Spring IoC 容器能够经过多种途径来加载配置元数据,好比本地系统、Java Classpath 等。
Spring IoC 容器至少包含一个 bean 定义,但大多数状况下会有多个 bean 定义。
bean 定义与应用程序中实际使用的对象一一对应。一般状况下 bean 的定义包括:服务层对象、数据访问层对象(DAO)、相似 Structs Action 的表示层对象、Hibernate SessionFactory 对象、JMS Queue 对象等等。
如下是一个基于 XML 配置元数据的基本结构:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"> 6 7 <bean id="ioCDemo01" class="demo.ioc.IoCDemo01" > 8 </bean> 9 10 </beans>
2.2实例化容器
1 package demo.ioc; 2 3 import org.springframework.beans.factory.BeanFactory; 4 import org.springframework.beans.factory.xml.XmlBeanFactory; 5 import org.springframework.context.ApplicationContext; 6 import org.springframework.context.support.ClassPathXmlApplicationContext; 7 import org.springframework.core.io.ClassPathResource; 8 import org.springframework.core.io.FileSystemResource; 9 import org.springframework.core.io.Resource; 10 11 public class Test { 12 13 public static void main(String[] args) { 14 String fileName = "applicationContext.xml"; 15 16 //1 17 // Resource resource = new FileSystemResource(fileName); 18 // BeanFactory factory = new XmlBeanFactory(resource); 19 20 //2 21 // ClassPathResource resource = new ClassPathResource(fileName); 22 // BeanFactory factory = new XmlBeanFactory(resource); 23 24 //3 25 ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{fileName}); 26 BeanFactory factory = (BeanFactory) context; 27 28 IoCDemo01 ioCDemo01 = (IoCDemo01) factory.getBean("ioCDemo01"); 29 ioCDemo01.show(); 30 } 31 32 }
2.2.1组成基于 XML 配置元数据
将 XML 配置文件分拆成多个部分是很是有用的。为了加载多个 XML 文件生成一个 ApplicationContext 实例,能够将文件路径做为字符串数组传给 ApplicationContext 构造器。而 beanfactory 将经过调用 bean defintion reader 从多个文件中读取 bean 定义。
一般状况下,更倾向于上述作法,由于这样各个配置并不会查觉到与其余配置文件的组合。另一种方法是使用一个或多个的 <import /> 元素来从另一个或多个文件加载 bean 定义。全部的 <import /> 元素必须放在 <bean /> 元素以前以完成 bean 的导入。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"> 6 7 <import resource="applicationContext2.xml"/> 8 9 <bean id="ioCDemo01" class="demo.ioc.IoCDemo01" > 10 </bean> 11 12 </beans>
2.3多种 bean
Spring IoC 容器管理一个或多个 bean,这些 bean 将经过配置文件中的 bean 定义被建立。
在容器内部,这些 bean 的定义由 BeanDefinition 对象来表示,该定义将包含如下信息:
全限定类名:这一般就是已定义 bean 的实际实现类。若是一般调用 static factory 方法来实例化 bean,而不是使用常规的构造器,那么类名实际上就是工厂类的类名。
bean 行为的定义,即建立模式(prototype 仍是 singleton)、自动装配模式、依赖检查模式、初始化及销毁方法。这些定义将决定 bean 在容器中的行为。
用于建立 bean 实例的构造器参数及属性值。好比使用 bean 来定义链接池,能够经过属性或者构造参数指定链接数,以及链接池大小限制等。
bean 之间的关系,即协做(或者称依赖)。
除了经过 bean 定义来描述要建立的指定 bean 的属性以外,某些 BeanFactory 的实现也容许将那些非 BeanFactory 建立的、已有的用户对象注册到容器中,好比使用 DefaultListableBeanFactory 的 registerSingleton(..) 方法。不过大多数应用仍是采用元数据定义为主。
2.3.1命名 bean
bean 的命名采用标准的 Java 命名约定,即小写字母开头,首字母大写间隔的命名方式。
对 bean 采用统一的命名约定将会使配置更加简单易懂。并且在使用 Spring AOP 时,若是要发通知(advice)给一组名称相关的 bean 时,这种简单的命名方式会受益不浅。
每一个 bean 都有一个或多个 id(或称之为标识符或名称)。这些 id 在当前 IoC 容器中必须惟一。若是一个 bean 有多个 id,那么其余的 id 在本质上将被认为是别名。
当使用基于 XML 的配置元数据时,将经过 id 或 name 属性来指定 bean 标识符。id 属性具备惟一性,并且是一个整整的 XML ID 属性,所以其余 xml 元素在引用该 id 时,能够利用 XML 解析器的验证功能。一般状况下最好为 bean 指定一个 id。尽管 XML 规范规定了 XML ID 命名的有效字符,可是 bean 标识符的定义不受该限制,由于除了使用指定的 xml 字符来做为 id,还能够为 bean 指定别名,要实现这一点能够在 name 属性中使用逗号、冒号或者空格将多个 id 分隔。为一个 bean 提供一个 name 并非必须的,若是没有指定,那么容器将为其生成一个惟一的 name。
2.3.1.1bean 的别名
在对 bean 进行定义时,除了使用 id 属性来指定名称以外,为了提供多个名称,须要经过 alias 属性来加以指定。
在定义 bean 时就指定全部的别名并不老是恰当的。有时咱们指望能在当前位置为那些在别处定义的 bean 引入别名。在 XML 配置文件中,能够单独的 <alias /> 元素来完成 bean 别名的定义。
1 <alias name="ioCDemo01" alias="a1"/>
2.3.2实例化bean
当须要的时候,容器会从 bean 定义列表中取得一个指定的 bean 定义,并根据 bean 定义里面的配置元数据使用反射机制来建立一个实际的对象。
当采用 XML 描述配置元数据时,将经过 <bean /> 元素的 class 属性来指定实例化对象的类型。class 属性一般是必须的(实例工厂方法实例化和 bean 定义的继续除外)。
class 属性主要有两种用途:在大多数状况下,容器将直接经过反射调用指定类的构造器来建立 bean;在极少数状况下,容器将调用类的静态工厂方法来建立 bean 实例,class 属性将用来指定实际具备静态工厂方法的类。
2.3.2.1用构造器来实例化
1 <bean id="ioCDemo01" class="demo.ioc.IoCDemo01" > 2 </bean>
2.3.2.2使用静态工厂方法实例化
1 <bean id="calendar01" class="java.util.Calendar" factory-method="getInstance"> 2 </bean>
2.3.2.3使用实例工厂方法实例化
与使用静态工厂方法实例化相似,用来进行实例化的实例工厂方法位于另一个已有的 bean 中,容器将调用该 bean 的工厂方法来建立一个新的 bean 实例。
为使用此机制,class 属性必须为空,而 factory-bean 属性必须制定为当前(或其祖先)容器中包含工厂方法的 bean 的名称,而该工厂 bean 的方法自己经过 factory-method 属性来设定。
1 <bean id="clazz01" class="java.lang.Class" factory-method="forName"> 2 <constructor-arg> 3 <value>demo.ioc.IoCDemo01</value> 4 </constructor-arg> 5 </bean> 6 7 <bean id="ioCDemo012" factory-bean="clazz01" factory-method="newInstance"> 8 </bean>
虽然设置 bean 属性的机制仍然在这里被说起,但隐式的作法是由工厂 bean 本身来管理以及经过依赖注入(DI)来进行配置。
2.4使用容器
BeanFactory 提供六种方法供客户代码调用:
3.依赖
3.1注入依赖
依赖注入(DI)背后的基本原理是对象之间的依赖关系(即一块儿工做的其余对象)只会经过如下几种方式来实现:构造器的参数、工厂方法的参数,或给由构造函数或者工厂方法建立的对象设置属性。所以,容器的工做就是建立 bean 时注入那些依赖关系。相对于由 bean 本身来控制其实例化、直接在构造器中指定依赖关系或者相似服务定位器模式这 3 种自主控制依赖关系注入的方法来讲,控制从根本上发生了倒转,这也正是控制反转名字的由来。
DI 主要有两种注入方式,即 Setter 注入和构造器注入。
3.1.1Setter 注入
经过调用无参构造器或无参 static 工厂方法实例化 bean 以后,调用该 bean 的 setter 方法,便可实现基于 setter 的 DI。
1 <bean id="setter01" class="demo.ioc.di.SetterDemo01"> 2 <property name="a01"> 3 <ref bean="di01"/> 4 </property> 5 <property name="no"> 6 <value>1001</value> 7 </property> 8 </bean> 9 10 <bean id="di01" class="demo.ioc.di.A01"> 11 <property name="description"> 12 <value>AAAAA</value> 13 </property> 14 </bean>
1 package demo.ioc.di; 2 3 public class SetterDemo01 { 4 5 private A01 a01; 6 7 private int no; 8 9 public A01 getA01() { 10 return a01; 11 } 12 13 public void setA01(A01 a01) { 14 this.a01 = a01; 15 } 16 17 public int getNo() { 18 return no; 19 } 20 21 public void setNo(int no) { 22 this.no = no; 23 } 24 25 @Override 26 public String toString() { 27 return "SetterDemo01 [a01=" + a01.getDescription() + ", no=" + no + "]"; 28 } 29 30 31 32 }
1 package demo.ioc.di; 2 3 public class A01 { 4 5 private String description; 6 7 public String getDescription() { 8 return description; 9 } 10 11 public void setDescription(String description) { 12 this.description = description; 13 } 14 15 }
3.1.2构造器注入
基于构造器的 DI 经过调用带参数的构造器来实现,每一个参数表明着一个协做者。此外,还可经过给静态工厂方法传参数来构造 bean。
1 <bean id="di01" class="demo.ioc.di.A01"> 2 <property name="description"> 3 <value>AAAAA</value> 4 </property> 5 </bean> 6 7 <bean id="constructDemo01" class="demo.ioc.di.ConstructDemo01"> 8 <constructor-arg type="String" value="Tom"> 9 </constructor-arg> 10 <constructor-arg type="String" value="male"> 11 </constructor-arg> 12 <constructor-arg type="int" value="18"> 13 </constructor-arg> 14 <constructor-arg type="String" value="i like play football"> 15 </constructor-arg> 16 <constructor-arg> 17 <ref bean="di01"/> 18 </constructor-arg> 19 </bean>
1 package demo.ioc.di; 2 3 public class ConstructDemo01 { 4 5 private String name; 6 private String sex; 7 private int age; 8 private String description; 9 private A01 a01; 10 11 public ConstructDemo01(String name, String sex, int age, String description, A01 a01) { 12 super(); 13 this.name = name; 14 this.sex = sex; 15 this.age = age; 16 this.description = description; 17 this.a01 = a01; 18 } 19 20 public void introduce() { 21 System.out.println("Hello, my name is " + name + ", i'm " + age + " years old."); 22 System.out.println("Description : " + a01.getDescription()); 23 } 24 25 }
如何在构造器注入和 Setter 注入之间进行选择?
因为大量的构造器参数多是程序变得笨拙,特别是当前某些属性是可选的时候。所以一般状况下,Spring 开发团队提倡使用 setter 注入。并且 setter DI 在之后的某个时候还能够将实例从新配置(或从新注入)。
尽管如此,构造器注入由于某些缘由仍是收到了一些人的青睐。一次性将全部依赖注入的作法意味着,在未彻底初始化的状态下,此对象不会返回给客户端代码(或被调用),此外对象也不能再次被从新配置(或从新注入)。
BeanFactory 对于它所管理的 bean 提供两种注入依赖方式(实际上它也支持同时使用构造器注入和 Setter 方式注入依赖)。须要注入的依赖保存在 BeanDefinition 中,它能根据指定的 PropertyEditor 实现将属性从一种格式转换成另一种格式。
处理 bean 依赖关系一般按一下步骤进行:
1)根据定义 bean 的配置(文件)建立并初始化 BeanFactory 实例(大部分的 Spring 用户使用支持 XML 格式配置文件的 BeanFactory 或 ApplicationContext 实现)。
2)每一个 bean 的依赖将以属性、构造器参数、或静态工厂方法参数的形式出现。当这些 bean 被实际建立时,这些依赖也将会提供给该 bean。
3)每一个属性或构造器参数既能够是一个实际的值,也能够是对该容器中另外一个 bean 的引用。
4)每一个指定的属性构造器参数必须可以被转换成属性或构造参数所需的类型。默认状况下,Spring 会可以以 String 类型提供值转换成各类内置类型,好比 int、long、String、boolean 等。
Spring 会在容器被建立时验证容器中每一个 bean 的配置,包括验证那些 bean 所引用的属性是否指向一个有效的 bean。然而,在 bean 被实际建立以前,bean 的属性并不会被设置。对于那些 singleton 类型和被设置为提早实例化的 bean(好比 ApplicationContext 中的 singleton bean)而言,bean 实例将于容器同时被建立。而另一些 bean 则会在须要的时候被建立,伴随着 bean 被实际建立,做为该 bean 的依赖以及依赖 bean 的依赖 bean(以此类推)也将被建立和分配。
循环依赖。当你主要使用构造器注入的方式配置 bean 时,颇有可能会产生循环依赖的状况。好比说,一个类 A,须要经过构造器注入类 B,而类 B 又须要经过构造器注入类 A。若是为类 A 和类 B 配置的 bean 被互相注入的话,那么 Spring IoC 容器将在运行时检测出循环引用,并抛出 BeanCurrentlyInCreationException 异常。
对于此问题,一个可能的解决方法就是修改源代码,将构造器注入改成 setter 注入。另外一个解决方法就是彻底放弃使用构造器注入,只是用 setter 注入。
Spring 会在 bean 建立时采起设置属性和依赖关系(只在须要时建立所依赖的其余对象)。Spring 容器被正确加载以后,当获取一个 bean 实例时,若是在建立 bean 或者设置依赖时出现问题,那么将抛出一个异常。因缺乏或设置了一个无效属性而致使抛出一个异常的状况的确是存在的。由于一些配置问题而致使潜在的可见性被延迟,因此在默认状况下,ApplicationContect 实现中的 bean 采用提早实例化的 singleton 模式。在实际须要以前建立这些 bean 将带来时间与内存的开销。而这样作的好处就是 ApplicationContext 被加载的时候能够尽早的发现一些配置的问题。不过用户能够根据须要采用延迟实例化来替代默认的 singleton 模式。
当协做 bean 被注入到依赖 bean 时,协做 bean 必须在依赖 bean 以前彻底配置好。即协做 bean 实例化完成,相关的依赖也设置完成。
3.2构造器参数的解析
构造器参数将根据类型来进行匹配。若是 bean 定义中的构造器参数类型明确,那么 bean 定义中的参数顺序就是对应构造器参数的顺序。
3.2.1构造器参数类型匹配
能够在构造器参数定义中使用 type 属性来显式的指定参数所对应的简单类型。
1 <bean id="constructDemo02" class="demo.ioc.di.ConstructDemo02"> 2 <constructor-arg type="String" value="Tom"> 3 </constructor-arg> 4 <constructor-arg type="int" value="1"> 5 </constructor-arg> 6 </bean>
1 package demo.ioc.di; 2 3 public class ConstructDemo02 { 4 5 private String s1; 6 private int s2; 7 8 public ConstructDemo02(String s1, int s2) { 9 super(); 10 this.s1 = s1; 11 this.s2 = s2; 12 } 13 14 @Override 15 public String toString() { 16 return "ConstructDemo02 [s1=" + s1 + ", s2=" + s2 + "]"; 17 }; 18 19 }
3.2.2构造器参数索引
经过使用 index 属性能够显式的指定构造器参数出现顺序。
1 <bean id="constructDemo03" class="demo.ioc.di.ConstructDemo02"> 2 <constructor-arg index="0" value="Jerry"> 3 </constructor-arg> 4 <constructor-arg index="1" value="100"> 5 </constructor-arg> 6 </bean>
使用 index 属性除了能够解决多个简单类型构造参数形成的模棱两可的问题以外,还能够用来解决两个构造参数类型相同形成的麻烦。index 属性值从 0 开始。
指定构造器参数索引是使用构造器 IoC 首选的方式。
3.3bean 属性及构造器参数详解
3.3.1直接量(基本类型、Strings 类型等)
<value /> 元素经过字符串来指定属性或构造器参数的值。
3.3.1.1idref 元素
idref 元素用来将容器内其余 bean 的 id 传给 <constructor-arg /> 或 <property /> 元素,同时提供错误验证功能。
使用 idref 表级容许容器在部署时,验证所被引用的 bean 是否存在。
若是被引用的 bean 在同一 XML 文件内,且 bean 名字就是 bean id,那么可使用 local 属性。
idref元素容许将容器中另外一个bean的bean id(这是字符串值不是引用)传递给一个<property />或<constructor-arg />。在给定的示例中,它清楚地显示了如何将bean id传递给另外一个bean,并显示bean ID。(参考:http://www.roseindia.net/tutorial/spring/spring3/ioc/springidrefelement.html#)
1 package demo.ioc.di2; 2 3 public class FirstBean { 4 5 private String message = null; 6 7 public String getMessage() { 8 return message; 9 } 10 11 public void setMessage(String message) { 12 this.message = message; 13 } 14 15 }
1 package demo.ioc.di2; 2 3 public class AnotherBean { 4 private String amessage = null; 5 6 public String getAmessage() { 7 return amessage; 8 } 9 10 public void setAmessage(String amessage) { 11 this.amessage = amessage; 12 } 13 14 public void display() { 15 System.out.println(amessage); 16 } 17 }
1 package demo.ioc.di2; 2 3 import org.springframework.beans.factory.BeanFactory; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 public class IDREFMain { 7 public static void main(String[] args) { 8 BeanFactory beanfactory = new ClassPathXmlApplicationContext("applicationContextdi2.xml"); 9 AnotherBean bean = (AnotherBean) beanfactory.getBean("another"); 10 bean.display(); 11 } 12 }
1 <bean id="first" class="demo.ioc.di2.FirstBean"> 2 <property name="message" value="Spring is simple." /> 3 </bean> 4 5 <bean id="another" class="demo.ioc.di2.AnotherBean"> 6 <property name="amessage"> 7 <idref bean="first" /> 8 </property> 9 </bean>
3.3.2引用其余的 bean
在 <constructor-arg /><property /> 元素内部还可使用 ref 元素。该元素用来将 bean 中指定属性的值设置为对容器中的另一个 bean 的引用。如前所述,该引用 bean 将被做为依赖注入,并且在注入以前会被初始化(若是是 singleton bean 则已被容器初始化)。尽管都是对另一个对象的引用,可是经过 id/name 指向另一个对象却有三种不一样的形式,不一样的形式将决定如何处理做用域及验证。
第一种形式也是最多见的形式,是经过使用 <ref /> 标记指定 bean 属性的目标 bean,经过该标签能够引用同一容器或父容器内的任何 bean(不管是否在同一 XML 文件中)。XML bean 元素的值既能够是指定的 bean 的 id也能够是其 name 值。
1 <ref bean="someBean" />
第二种形式是使用 ref 的 local 属性指定目标的 bean,它能够利用 XML 解析器来验证所引用的 bean 是否存在同一文件中。local 属性值必须是目标 bean 的 id 属性值。若是在同一配置文件中没有找到引用的 bean,XML 解析器将抛出一个异常。若是目标 bean 是在同一文件内,使用 local 方式就是最好的选择。
1 <ref local="someBean" />
第三种方式是经过使用 ref 的 parent 属性来引用当前容器的父容器中的 bean。parent 属性值既能够是目标 bean 的 id,也能够是 name 属性值。并且目标 bean 必须在当前容器的父容器中。使用 parent 属性的主要用途是为了用某个与父容器中的 bean 同名的代理来包装父容器中的一个 bean。parent 属性的使用并不常见。
3.3.3内部 bean
所谓的内部 bean 是指在一个 bean 的 <property /> 或 <constructor-arg /> 元素中使用 <bean /> 元素定义的 bean。内部 bean 定义不须要有 id 或 name 属性,即便指定 id 或 name 属性值也将会被容器忽略。
内部 bean 中的 singleton 标记 及 id 或 name 属性将被忽略。内部 bean 老是匿名的且他们老是 prototype 模式的。同时将内部 bean 注入到包含该内部 bean 以外的 bean 是不可能的。
实例:http://www.yiibai.com/spring/spring-inner-bean-examples.html
注入的时候注意构造函数。Setter 注入的话别忘了默认的构造函数。
3.3.4集合
经过 <list />、<set />、<map /> 及 <props /> 元素能够定义和设置与 Java Collection 类型对应 List、Set、Map 及 Properties 的值。
实例:http://blog.csdn.net/thc1987/article/details/5790792
3.3.4.1集合合并
从 2.0 开始,Spring IoC 容器将支持集合的合并。这样咱们能够定义 parent-style 和 child-style 的 <list />、<set />、<map /> 及 <props /> 元素,子集合的值从其父集合继承和覆盖二来;也就是说,父子集合元素合并后的值就是子集合中的最终结果,并且子集合中的元素值将覆盖父集合中对应的值。
1 <bean id="father" abstract="true" class="demo.ioc.combine.Father"> 2 <property name="list"> 3 <list> 4 <value>test1</value> 5 <value>test2</value> 6 <value>test3</value> 7 </list> 8 </property> 9 </bean> 10 11 <bean id="son" parent="father" class="demo.ioc.combine.Son"> 12 <property name="list" > 13 <list merge="true"> 14 <value>host1</value> 15 <value>host2</value> 16 </list> 17 </property> 18 </bean>
1 package demo.ioc.combine; 2 3 import java.util.List; 4 import java.util.Map; 5 6 public class Father { 7 8 private List<String> list; 9 10 private Map<Integer, String> map; 11 12 public List<String> getList() { 13 return list; 14 } 15 16 public void setList(List<String> list) { 17 this.list = list; 18 } 19 20 public Map<Integer, String> getMap() { 21 return map; 22 } 23 24 public void setMap(Map<Integer, String> map) { 25 this.map = map; 26 } 27 28 }
1 package demo.ioc.combine; 2 3 import java.util.List; 4 import java.util.Map; 5 6 import org.springframework.context.ApplicationContext; 7 import org.springframework.context.support.ClassPathXmlApplicationContext; 8 9 public class Son extends Father { 10 11 private List<String> list; 12 13 public List<String> getList() { 14 return list; 15 } 16 17 public void setList(List<String> list) { 18 this.list = list; 19 } 20 21 public void print() { 22 if (list != null) { 23 for (String s : list) { 24 System.out.println(s); 25 } 26 } 27 } 28 29 public static void main(String[] args) { 30 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContextCombine.xml"); 31 Son son = (Son) context.getBean("son"); 32 son.print(); 33 } 34 }
参考:http://blog.csdn.net/opnmzxcvb/article/details/4971799
http://liyixing1.iteye.com/blog/1036520
对于 <list />、<map /> 及 <set /> 集合将从父 <props /> 继承全部属性元素。同时子 bean 的suppory 值将覆盖父集合的相应值。
对于 <list /> ,父 bean 的列表内容将排在子 bean 列表内容的前面。对于 Map、Set 及 Properties 集合类型没有顺序的概念,所以做为相关的 Map、Set 及 Properties 实现基础的集合类型在容器内部没有排序的语义。
不一样的集合类型是不能合并的,不然会抛出相应的 Exception。merge 属性必须在继承的子 bean 中定义,而在父 bean 的集合属性上指定的 merge 属性将被忽略。
3.3.4.2强类型集合
将 value 元素值转换为相应的泛型类型的值。
3.3.5Nulls
<null /> 用于处理 null 值。Spring会把属性的空参数当作空字符串处理。而 null 值可使用 <null /> 元素来表示。
3.3.6XML-based configura metada shortcus
针对常见的 value 值或 bean 的引用,Spring 提供了简化格式用于替代 <value /> 和 <ref /> 元素。<property />、<constructor-arg /> 及 <entry /> 元素都支持 value 属性,他能够替代内嵌的 <value /> 元素。
3.3.7组合属性名称
当设置 bean 的组合属性时,除了最后一个属性外,只要其余属性值不为 null,组合或嵌套属性名是彻底合法的。
组合属性在 bean 被构造后必须非空,不然会抛出长异常。
实例:http://blog.csdn.net/confirmaname/article/details/9362379
3.4使用 depends-on
一个 bean 对另外一个 bean 的依赖最简单的作法就是将一个 bean 设置为另一个 bean 的属性。在 xml 配置文件中最多见的就是使用 <ref /> 元素。有时候它还有另一种变体,若是一个 bean 能感知 IoC 容器,只要给出他所依赖的 id,那么久能够经过编程的方式从容器中取得它所依赖的对象。
不管采用哪种方法,被依赖 bean 将在依赖 bean 以前被适当的初始化。
在少数状况下,有时候 bean 之间的依赖关系并非那么的直接。depends-on 属性能够用于当前 bean 初始化以前显式强制一个或多个 bean 被初始化。
实例:http://blog.csdn.net/pzw_0612/article/details/48021509
3.3.5延迟初始化 bean
ApplicationContext 实现的默认行为就是在启动时将全部 singleton bean 提早进行实例化。提早实例化意味着做为初始化过程的一部分,ApplicationContext 实例会建立并配置全部的 singleton bean。
若是不想让一个 singleton bean 在 ApplicationContext 实如今初始化时被提早实例化,那么能够将 bean 设置为延迟实例化。一个延迟初始化 bean 将告诉 IoC 容器是在启动时仍是在第一次被用到时实例化。
在 XML 配置文件中,延迟初始化将经过 <bean /> 元素中的 lazy-init 属性来进行控制。
若是一个 bean 被设置为延迟初始化,而另外一个非延迟初始化的 singleton bean 依赖于它,那么当 ApplicationContext 提早实例化 singleton bean 时,它必须也确保全部上述 singleton 依赖也被预先初始化,固然也包括设置为延迟实例化的 bean。
在容器层次中经过在 <beans /> 元素上使用 'default-lazy-init' 属性来控制延迟初始化也是可能的。
1 <beans default-lazy-init="true"> 2 ... 3 <beans>
3.3.6自动装配(autowire)协做者
实例:http://blog.csdn.net/fengyun111999/article/details/6320486
Spring IoC 容器能够自动装配相互协做 bean 之间的关联关系。所以,若是可能的话,能够自动让 Spring 经过检查 BeanFactory 中的内容,来替咱们指定 bean 的协做者。因为 autowire 能够针对单个 bean 进行设置,所以可让有些 bean 使用 autowire,有些 bean 不采用。autowire 的方便之处在减小或者消除属性或构造参数的设置,这样能够给咱们的配置文件减减肥。在 xml 配置文件中,autowire 一共有五种类型,能够在 <bean /> 元素中使用 autowire 属性指定。
模式 |
说明 |
no |
(默认)不采用autowire机制.。这种状况,当咱们须要使用依赖注入,只能用<ref/>标签。 |
byName |
经过属性的名称自动装配(注入)。Spring会在容器中查找名称与bean属性名称一致的bean,并自动注入到bean属性中。固然bean的属性须要有setter方法。例如:bean A有个属性master,master的setter方法就是setMaster,A设置了autowire="byName",那么Spring就会在容器中查找名为master的bean经过setMaster方法注入到A中。 |
byType |
经过类型自动装配(注入)。Spring会在容器中查找类(Class)与bean属性类一致的bean,并自动注入到bean属性中,若是容器中包含多个这个类型的bean,Spring将抛出异常。若是没有找到这个类型的bean,那么注入动做将不会执行。 |
constructor |
相似于byType,可是是经过构造函数的参数类型来匹配。假设bean A有构造函数A(B b, C c),那么Spring会在容器中查找类型为B和C的bean经过构造函数A(B b, C c)注入到A中。与byType同样,若是存在多个bean类型为B或者C,则会抛出异常。但时与byType不一样的是,若是在容器中找不到匹配的类的bean,将抛出异常,由于Spring没法调用构造函数实例化这个bean。 |
default |
采用父级标签(即beans的default-autowire属性)的配置。 |
若是直接使用 property 和 consturctor-arg 注入依赖的话,那么将老是覆盖自动装配。并且目前也不支持简单类型的自动装配,这里所说的简单类型包括基本类型、String、Class 以及简单类型的数组。自动装配还能够与依赖检查结合使用,这样依赖检查将在自动装配完成以后被执行。
优势:自动装配能显著减小配置的数量。不过,采用 bean 模板也能够达到一样的目的。
自动装配可使配置与 java 代码同步更新。
缺点:Spring 会尽可能避免在装配不明确的时候进行猜想,由于装配不明确可能出现难以预料的结果,并且 Spring 所管理的对象之间的关联关系也再也不能清晰的进行文档化。
对于那些根据 Spring 配置文件生成文档的工具来讲,自动装配将会使这些工具无法生成依赖信息。
若是采用 by type 方式自动装配,那么容器中类型与自动装配 bean 的属性或者构造函数参数类型一致的 bean 只能有一个,若是配置可能存在多个这样的 bean,那么就要考虑采用显示装配了。
3.6.1设置 bean 使自动装配失效
当采用 xml 格式配置 bean 时,<bean /> 元素的 autowire-candidate 属性可被设为 false,这样容器在查找自动装配对象时将不考虑该 bean。
对于那些历来就不会被其余 bean 采用自动装配的方式来注入的 bean 而言,这是有用而。不过这并不意味着被排除的 bean 本身就不能使用自动装配来注入其余 bean,他是能够的,或者更准确的说,应该是他不会被考虑做为其余 bean 自动装配的候选者。
1 <bean id="a" class="demo.ioc.autowire.ClassA" autowire-candidate="false"> 2 <property name="stringa" value="testa"></property> 3 <property name="stringb" value="testb"></property> 4 <property name="a" value="10000"></property> 5 </bean> 6 7 <bean id="b" class="demo.ioc.autowire.ClassB" autowire="byType"></bean>
3.7依赖检查
Spring 除了能对容器中 bean 的依赖设置进行检查外。还能够检查 bean 定义中实际属性值的设置,固然也包括采用自动装配方式设置属性值的检查。
当须要确保 bean 的全部属性值(或者属性类型)被正确设置的时候,那么这个功能会很是有用。固然,在不少状况下,bean 类的某些属性会具备默认值,或者有些属性并不会在全部场景下使用,所以这项功能会存在必定的局限性。就像自动装配同样,依赖检查也能够针对每个 bean 进行设置。依赖检查默认为 none,他有几种不一样的使用模式,在 xml 配置文件中,能够在 bean 定义中为 dependency-check 属性使用如下几种值:
模式 | 说明 |
none | 没有依赖检查,若是 bean 的属性没有值的话能够不用设置 |
simple | 对于原始类型及集合(除协做者外的一切东西)执行依赖检查 |
object | 仅对协做者执行依赖检查 |
all | 对协做者,原始类型及集合执行依赖检查 |
资料:http://biancheng.dnbcw.net/linux/413491.html
Spring 3.0 已取消该属性。
在 spring 3 中替代 dependency-check 有4条建议:
3.8方法注入
在大部分状况下,容器中的 bean 都是 singleton 类型的。若是一个 singleton bean 要引用另一个 singleton bean,或者一个非 singleton bean 要引用另一个非 singleton bean时,一般状况下将一个 bean 定义为另外一个 bean 的 property 值就能够了。不过对于具备不一样生命周期的 bean 来讲这样作就会有问题了,好比在调用一个 singleton 类型 bean A 的某个方法时,须要引用另外一个非 singleton(prototype)类型的 bean B,对于 bean A 来讲,容器只会建立一次,这样就无法在须要的时候每次让容器为 bean A 提供一个新的的 bean B 实例。
对于上面的问题Spring提供了三种解决方案:
实例:http://flysnow.iteye.com/blog/733785
4bean 的做用域
Spring 支持五种做用域(其中有三种只能用在基于 web 的 Spring ApplicationContext)
内置支持的做用域分列以下:
在每一个Spring IoC容器中一个bean定义对应一个对象实例。 |
|
一个bean定义对应多个对象实例。 |
|
在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例,它们依据某个bean定义建立而成。该做用域仅在基于web的Spring ApplicationContext情形下有效。 |
|
在一个HTTP Session中,一个bean定义对应一个实例。该做用域仅在基于web的Spring ApplicationContext情形下有效。 |
|
在一个全局的HTTP Session中,一个bean定义对应一个实例。典型状况下,仅在使用portlet context的时候有效。该做用域仅在基于web的Spring ApplicationContext情形下有效。 |
4.1Singleton做用域
当一个 bean 的做用域为 singleton,那么 Spring IoC 容器中只会存在一个共享的 bean 实例,而且全部对 bean 的请求,只要 id 与该 bean定义相匹配,则只会返回 bean 的同一实例。Singleton 做用域是 Spring 中的缺省做用域。
1 <bean id=".." class=".." scope="singleton"> 2 ... 3 </bean>
4.2Prototype做用域
Prototype 做用域的 bean 会致使在每次对该 bean 请求时都会建立一个新的 bean 实例。根据经验,对全部有状态的 bean 应该使用 prototype 做用域,而对无状态的 bean 则应该使用 singleton 做用域。
1 <bean id=".." class=".." scope="prototype"> 2 ... 3 </bean>
对于 prototype 做用域的 bean,有一点很是重要,那就是 Spring 不能对一个 prototype bean 的整个生命周期负责:容器在初始化、配置、装饰或者是装配玩一个 prototype 实例后,将它交给客户端,随后就对该 prototype 实例漠不关心了。无论何种做用域,容器都会调用全部对象的初始化生命周期回调方法,而对 prototype 而言,任何配置好的析构生命周期毁掉方法都不会被调用。清除 prototype 做用域的对象并释听任何 prototype bean 所持有的昂贵资源,都是客户端代码的职责。(让 Spring 容器释放被 singleton 做用域 bean 占用资源的一种可行方式是,经过使用 bean 的后置处理器,该处理器持有要被清除的 bean 的引用。)
4.3其余做用域
其余做用域,即 request、session 以及 global session 仅在基于 web 的应用中使用。
下面介绍的做用域仅仅在使用基于 web 的 spring applicationcontext 实现(如 xmlwebapplicationcontext)时有用。若是在普通的 spring ioc 容器中,好比像 xmlbeanfactory 或 classpathxmlapplicationcontext,尝试使用这些做用域,将会获得一个 illegalstateexception 异常。
4.3.1初始化 web 配置
要使用 request、session 和 global session 做用域的 bean(即具备 web 做用域的 bean),在开始设置 bean 定义以前,还要作少许的初始配置。
若是使用的是 servlet 2.4 及以上的 web 容器,那么须要在 web 应用的 xml 声明文件 web.xml 中增长下述 ContextListener
1 <web-app> 2 ... 3 <listener> 4 <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> 5 </listener> 6 ... 7 </web-app>
若是使用的是早起版本的 web 容器(Servlet 2.4 之前),那么须要使用一个 javax.servlet.Filter 的实现
1 <web-app> 2 ... 3 <filter> 4 <filter-name>requestContextFilter</filter-name> 5 <filter-class>org.springframework.web.filter.RequestFilter</filter-class> 6 </filter> 7 <filter-mapping> 8 <filter-name>requestContextFilter</filter-name> 9 <url-pattern>/*</url-pattern> 10 </filter-mapping> 11 ... 12 </web-app>
RequestContextListener 和 RequestContextFilter 两个类作的都是一样的工做:将 HTTP request 对象绑定到为该请求提供服务的 Thread。这使得具备 requset 和 session 做用域的 bean 可以在后面的调用链中被访问到。
4.3.2Request 做用域
5定制 bean 特性
5.1Lifecycle 接口
Spring 提供了几个标志接口,这些接口用来改变容器中 bean 的行为;它们包括 InitializingBean 和 DisposableBean。实现这两个接口的 bean 在初始化和析构时容器会调用前者的 afterPropertiesSet() 方法,以及后者的 destory() 方法。
Spring 在内部使用 BeanPostProcessor 实现来处理它能找到的任何标志接口并调用相应的方法。若是须要自定义特性或者生命周期行为,能够实现本身的 BeanPostProcessor。
5.1.1初始化回调
实现 org.springframework.beans.factory.InitializingBean 接口容许容器在设置好 bean 的全部必要属性后,执行初始化事宜。InitializingBean 接口仅指定了一个方法:
1 void afterPropertiesSet() throws Exception;
一般,要避免使用 InitializingBean 接口,并且不鼓励使用该接口,由于这样会将代码和 spring 耦合,能够在 Bean 定义中指定一个普通的初始化方法,即在 xml 配置文件中经过指定 init-method 属性来完成。
1 package demo.ioc.callback; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 public class A { 7 8 private String string; 9 private int i; 10 11 public String getString() { 12 return string; 13 } 14 public void setString(String string) { 15 this.string = string; 16 } 17 public int getI() { 18 return i; 19 } 20 public void setI(int i) { 21 this.i = i; 22 } 23 24 public void init() { 25 System.out.println("init"); 26 } 27 28 public static void main(String[] args) { 29 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContextdiCallback.xml"); 30 // A a = (A) context.getBean("a"); 31 } 32 }
1 package demo.ioc.callback; 2 3 import org.springframework.beans.factory.InitializingBean; 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.context.support.ClassPathXmlApplicationContext; 6 7 public class A2 implements InitializingBean { 8 9 private String string; 10 private int i; 11 12 public String getString() { 13 return string; 14 } 15 public void setString(String string) { 16 this.string = string; 17 } 18 public int getI() { 19 return i; 20 } 21 public void setI(int i) { 22 this.i = i; 23 } 24 25 public void init() { 26 System.out.println("init"); 27 } 28 29 public static void main(String[] args) { 30 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContextdiCallback.xml"); 31 // A a = (A) context.getBean("a"); 32 } 33 public void afterPropertiesSet() throws Exception { 34 // TODO Auto-generated method stub 35 System.out.println("init--"); 36 37 } 38 }
1 <bean id="a" class="demo.ioc.callback.A" init-method="init"> 2 </bean> 3 4 <bean id="a2" class="demo.ioc.callback.A2"> 5 </bean>
5.1.2析构回调
实现 org.springframework.beans.factory.DisposableBean 接口的 bean 容许在容器销毁该 bean 的时候得到一次回调。DisposableBean 接口也只规定了一个方法:
1 void destory() throws Exception;
一般,要避免使用 DisposableBean 标志接口,能够在 bean 定义中指定一个普通的析构方法,即在 xml 配置文件中经过 destory-method 属性来完成。
5.1.2.1缺省的初始化和析构方法
生命周期回调方法的名称最好在一个项目范围内标准化。
能够将 Spring 容器配置成在每一个 bean 上查找实现指定好的初始化和析构回调方法名称。这样就能够简化 bean 定义,好比根据约定将初始化回调命名为 init(),而后在基于 xml 的配置中,就能够省略 init-method="init" 配置,而 Spring IoC 容器将会在 bean 被建立的时候调用该方法。
1 <beans default-init-method="init"> 2 ... 3 </beans>
该属性的出现意味着 Spring IoC 容器会把 bean 上名为 init 的方法识别为初始化方法回调,而且当 bean 被建立和装配的时候,若是 bean 类具备这样的方法,他将会在适当的时候被调用。
相似的,配置析构方法回调是在顶层 <beans /> 元素上使用 default-destory-method 属性。
若是实际的回调方法与默认的命名约定不一样,那么能够经过在 <bean /> 元素上使用 init-method 和 destory-method 属性指定方法名来覆盖缺省设置。
5.1.2.2在非 web 应用中优雅的关闭 spring ioc 容器
在基于 web 的 ApplicationContext 实现中已有相应的代码来处理关闭 web 应用时如何恰当地关闭 Spring IoC 容器。
若是在非 web 应用的环境下使用 Spring 的 IoC 容器,则须要在 JVM 里注册一个关闭钩子(shutdown hook)。为了注册关闭钩子,须要简单地调用在 AbstractApplicationContext 实现中的 registerShutdownHook() 方法便可。
1 AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 2 context.registerShutdownHook();
5.2了解本身
5.2.1BeanFactoryAware
对于实现了 org.springframework.beans.factory.BeanFactoryAware接口的类,当它被 BeanFactory 建立后,他会拥有一个指向建立它的 BeanFactory 的引用。
1 public interface BeanFactoryAware { 2 3 void setBeanFactory(BeanFactory beanFactory) throws BeansException; 4 5 }
这样 bean 能够以编程的方式操控建立他们的 BeanFactory,固然咱们能够将引用的 BeanFactory 造型为已知的子类型来得到更多的功能。它主要用于经过编程来取得 BeanFactory 所管理的其余 bean。虽然在有些场景下这个功能颇有用,可是通常来讲应该尽可能避免使用,由于这样将使代码与 Spring 耦合在一块儿,并且也有违反控制的原则(协做者应该做为属性提供给 bean)。
与 BeanFactoryAware 等效的另外一种选择是使用 org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean。不过该方法依然没有下降与 Spring 的耦合,可是它并无像 BeanFactoryAware 那样,违反 IoC 原则。
ObjectFactoryCreatingFactoryBean 是 FactoryBean 的一个实现,它返回一个执行工厂对象的引用,该对象将执行 bean 的查找。OObjectFactoryCreatingFactoryBean 类实现了 BeanFactoryAware 接口;被实际注入到客户端 bean 的是 ObjectFactory 接口的一个实例。这是 Spring 提供的一个接口(于是依旧没有彻底与 Spring 解耦),客户端可使用 ObjectFactory 的 getObject() 方法来查找 bean。你要作的所有事情就是给 ObjectFactoryCreatingFactoryBean 提供待查找 bean 的名字。
5.2.2BeanNameAware
假如 bean 实现了 org.springframework.beans.factory.BeanNameAware 接口并被部署在一个 BeanFactory 中,BeanFactory 会经过该接口的 setBeanName() 方法以告知其被部署时的 bean id。在 bean 属性被设置完成以后,在向 InitializingBean 的 afterPropertiesSet 或是自定义 init-method 这样的初始化回调执行以前,该接口的回调方法会被调用。
6bean 定义的继承
当使用基于 XML 的配置元数据时,给 parent 属性指定值,意味着子 bean 定义的声明。
若是子 bean 定义没有指定 class 属性,它将使用父 bean 定义的 class 属性,固然也能够覆盖它。在后面一种状况下,子 bean 的 class 属性值必须同父 bean 兼容,也就是它必须接受父 bean 的属性值。
一个子 bean 定义能够从 bean 继承构造器参数值、属性值以及覆盖父 bean 的方法,而且能够有选择地增长新的值。若是制定了 init-method,desroty-method 或 static factory-method,他们就会覆盖父 bean 相应的设置。
剩余的设置老是从子 bean 定义处获得:依赖、自动装配模式、依赖检查、singleton、做用域和延迟初始爱护。
抽象 bean 定义可做为子 bean 定义的模板。若是尝试单独使用这样的父 bean,将会致使错误;一样,容器内部的 preInstantiateSingletons() 方法会彻底忽略 abstract 的 bean 定义。
默认状况下,ApplicationContext(不是 BeanFactory)会预实例化全部 singleton 的bean。所以很重要的一点是:若是只想把一个父 bean 定义当作模板使用,而他又制定了 class 属性,那么就得将 abstract 属性设置为 true,不然应用上下文将会预实例化抽象 bean。
1 <bean id="currency" abstract="true"> 2 <property name="name" value="Tom"></property> 3 <property name="age" value="26"></property> 4 <property name="sex" value="male"></property> 5 </bean> 6 7 <bean id="son" parent="currency" class="demo.ioc.inherit.Son"> 8 <property name="favourite" value="play games"></property> 9 </bean>
7容器扩展点
Spring 框架的 IoC容器被设计为可扩展的。一般咱们并不须要子类化各个 BeanFactory 或 ApplicationContext 实现类。而经过 plugin 各类集成接口实现来进行扩展。
7.1用 BeanPostProcessor 定制 bean
BeanPostProcessor 定义了几个回调方法,实现该接口可提供自定义(或默认地来覆盖容器)的实例化逻辑、依赖解析逻辑等。若是想在 Spring 容器完成 bean 的实例化、配置和其余的初始化后执行一些自定义逻辑,你能够插入一个或多个的 BeanPostProcessor 实现。
若是配置了多个 BeanPostProcessor ,那么能够经过设置 order 属性来控制 BeanPostProcessor 的执行次序(仅当 BeanPostProcessor 实现了 Ordered 接口时,才能够设置此属性,所以在编写本身的 BeanPostProcessor 实现时,就得考虑是否须要实现 Ordered 接口)。
实例:http://winneryj.iteye.com/blog/307736
7.2用 BeanFactoryPostProcessor 定制配置元数据
这个接口跟 BeanPostProcessor 相似,BeanFactoryPostProcessor 能够对 bean 的定义进行处理。也就是说 Spring IoC 容器容许 BeanFactroyPostProcessor 在容器实际实例化任何其余的 bean 以前读取配置与数据,并有可能修改它。
实例:http://blog.csdn.net/caihaijiang/article/details/35552859
若是想改变实际的 bean 实例,那么最好使用 BeanPostProcessor。
BeanFactoryPostProcessor 的做用域范围是容器级的。它只和所使用的容器有关。若是在容器中定义一个 BeanFactoryPostProcessor,它仅仅对此容器中的 bean 进行后置处理。BeanFactoryPostProcessor 不会对定义在另外一个容器中的 bean 进行后置处理,即便这两个容器都是在同一层次上。
不要将 BeanFactoryPostProcessors 标记为延迟加载。不然 Spring 容器将不会注册他们,自定义逻辑就没法实现。若是在 <beans /> 元素的定义中使用了 default-lazy-init 属性,请确信各个 BeanFactoryPostProcessor 标记为 lazy-init="false"