典型的企业应用程序不包含单个对象(或Spring的说法中的bean)。即便最简单的应用程序也有几个对象一块儿工做来展现最终用户将其视为一个连贯的应用程序。下一节将介绍如何从定义许多独立的bean定义到彻底实现的应用程序,在这些应用程序中对象协做实现目标。php
依赖注入(DI)是一个过程,经过这种过程,对象能够经过构造函数参数,工厂方法参数或者在构造或返回对象实例后设置的属性来定义它们的依赖关系,也就是说,它们使用的其余对象从工厂方法。容器 在建立bean时会注入这些依赖关系。这个过程从根本上说是相反的,所以名为 控制反转(IoC),它自己经过使用类的直接构造或服务定位器模式来控制它本身的依赖关系的实例化或位置。html
代码与DI原理相比更加清晰,而且在对象提供依赖关系时解耦更有效。该对象不查找其依赖项,而且不知道依赖项的位置或类。所以,您的类变得更容易测试,特别是当依赖关系位于接口或抽象基类上时,它们容许在单元测试中使用存根或模拟实现。java
DI存在两种主要的变体,基于构造器的依赖注入和基于Setter的依赖注入。mysql
基于构造器的 DI经过容器调用具备多个参数的构造函数完成,每一个参数表示一个依赖项。调用static
具备特定参数的工厂方法来构造bean几乎是等价的,本讨论static
相似地将参数视为构造函数和工厂方法。如下示例显示了只能经过构造函数注入进行依赖注入的类。请注意,这个类没有什么 特别之处,它是一个POJO,它不依赖于容器特定的接口,基类或注释。spring
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on a MovieFinder private MovieFinder movieFinder; // a constructor so that the Spring container can 'inject' a MovieFinder public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually 'uses' the injected MovieFinder is omitted... }
使用参数的类型发生构造函数参数解析匹配。若是bean定义的构造函数参数中没有可能存在的歧义,那么在bean定义中定义构造函数参数的顺序是当实例化bean时将这些参数提供给相应构造函数的顺序。考虑如下课程:sql
package x.y; public class Foo { public Foo(Bar bar, Baz baz) { // ... } }
没有潜在的歧义存在,假设 Bar
和Baz
类不经过继承相关。所以,如下配置能够正常工做,而且不须要在<constructor-arg/>
元素中明确指定构造函数参数索引和/或类型 。数据库
<beans> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> </bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> </beans>
当引用另外一个bean时,类型是已知的,而且能够发生匹配(就像前面的例子那样)。当使用简单类型时,例如 <value>true<value>
Spring没法肯定值的类型,所以没法在没有帮助的状况下按类型进行匹配。考虑如下课程:apache
package examples; public class ExampleBean { // No. of years to the calculate the Ultimate Answer private int years; // The Answer to Life, the Universe, and Everything private String ultimateAnswer; public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } }
在前面的场景中,若是使用属性明确指定构造函数参数的类型,容器 能够使用简单类型的类型匹配 type
。例如:编程
<bean id = “exampleBean” class = “examples.ExampleBean” > <constructor-arg type = “int” value = “7500000” /> <constructor-arg type = “java.lang.String” value = “42” / > </ bean>
使用该index
属性来明确指定构造函数参数的索引。例如:api
<bean id = “exampleBean” class = “examples.ExampleBean” > <constructor-arg index = “0” value = “7500000” /> <constructor-arg index = “1” value = “42” /> </ bean >
除了解决多个简单值的歧义以外,指定索引还解决了构造函数具备两个相同类型参数的含糊问题。请注意,该索引是基于0的。
<bean id = “exampleBean” class = “examples.ExampleBean” > <constructor-arg name = “years” value = “7500000” /> <constructor-arg name = “ultimateanswer” value = “42” /> </ bean >
请记住,要使这项工做脱离框架,您的代码必须在启用了调试标志的状况下编译,以便Spring能够从构造函数中查找参数名称。若是你不能用调试标志编译你的代码(或者不想),你可使用 @ConstructorProperties
JDK标注明确你的构造函数参数。示例类将不得不以下所示:
package examples; public class ExampleBean { // Fields omitted @ConstructorProperties({"years", "ultimateAnswer"}) public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } }
在调用无参数构造函数或无参数static
工厂方法来实例化bean以后,基于Setter的 DI经过调用bean上的容器调用setter方法来完成。
如下示例显示了一个只能使用纯setter注入进行依赖注入的类。这个类是传统的Java。这是一个POJO,它不依赖于容器特定的接口,基类或注释。
public class SimpleMovieLister { // SimpleMovieLister对MovieFinder有依赖性 private MovieFinder movieFinder; //一个setter方法,以便Spring容器能够'注入'一个MovieFinder public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } //实际“使用”注入的MovieFinder的业务逻辑被省略。 }
它ApplicationContext
支持它所管理的bean的基于构造函数和基于setter的DI。它也支持基于setter的DI以后,经过构造函数方法已经注入了一些依赖项。你在的形式配置的依赖关系BeanDefinition
,这与你使用的PropertyEditor
状况下,将属性从一种格式转换为另外一种。然而,大多数Spring用户不直接使用这些类(以编程方式),而是使用XML定义文件,而后将其内部转换为这些类的实例,并用于加载整个Spring IoC容器实例。
基于构造函数或基于setter的DI?
既然你能够混合使用基于构造函数和基于Setter的DI,那么使用强制依赖性的构造函数参数和可选依赖性的设置符是一个很好的经验法则。请注意,在 setter上使用@Required注释能够用来使setter须要依赖关系。
Spring团队一般主张setter注入,由于大量的构造函数参数可能会变得笨拙,特别是当属性是可选的时候。Setter方法也使该类的对象能够从新配置或稍后从新注入。经过JMX MBeans进行管理是一个引人注目的用例。
一些纯粹主义者喜欢基于构造函数的注入。提供全部对象依赖性意味着对象老是以彻底初始化的状态返回给客户端(调用)代码。缺点是物体变得不适合从新配置和从新注入。
使用对某个班级最有意义的DI。有时,在处理您没有来源的第三方课程时,您能够选择。遗留类可能不会公开任何setter方法,所以构造函数注入是惟一可用的DI。
该容器执行bean依赖性解析以下:
使用ApplicationContext
描述全部bean的配置元数据建立和初始化。配置元数据能够经过XML,Java代码或注释来指定。
对于每一个bean,若是使用该属性而不是普通构造函数,则它的依赖关系以属性,构造函数参数或静态工厂方法的参数的形式表示。当bean被实际建立时,这些依赖被提供给bean 。
每一个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另外一个bean的引用。
做为值的每一个属性或构造函数参数都从其指定的格式转换为该属性或构造函数参数的实际类型。默认状况下,Spring可以转换成字符串格式提供给全部的内置类型,好比数值 int
,long
, String
,boolean
,等。
Spring容器在建立容器时验证每一个bean的配置,包括验证bean引用属性是否引用有效的bean。可是,在实际建立 bean以前,bean属性自己不会被设置。Beans是单身做用域而且被设置为预先实例化的(默认的)是在建立容器时建立的。范围在第5.5节“Bean范围”中定义。不然,只有在请求时才建立bean。建立一个bean可能会致使建立一个bean图,由于bean的依赖关系及其依赖关系的依赖关系(等等)被建立和分配。
一般你能够相信春天作正确的事情。它在容器加载时检测配置问题,好比引用不存在的bean和循环依赖关系。当bean实际建立时,Spring会尽量晚地设置属性并解决依赖关系。这意味着,若是在建立该对象或其某个依赖关系时遇到问题,那么请求对象时,正确加载的Spring容器可能会稍后生成异常。例如,因为缺乏或无效的属性,bean抛出异常。某些配置问题的可能延迟可见性是缘由ApplicationContext
实现默认预先实例化单例bean。在实际须要这些bean以前,为了建立这些bean须要必定的时间和内存,您ApplicationContext
会在建立时发现配置问题 ,而不是稍后。您仍然能够重写此默认行为,以便单例bean将会进行延迟初始化,而不是预先实例化。
若是不存在循环依赖关系,则当一个或多个协做bean被注入到一个依赖bean中时,每一个协做bean都被注入到依赖bean 以前彻底配置。这意味着若是bean A对bean B有依赖性,Spring IoC容器在调用bean A上的setter方法以前彻底配置bean B.换句话说,bean被实例化(若是不是预先实例化的单例),它的将设置依赖关系,并调用相关的生命周期方法(例如配置的init方法或InitializingBean回调方法)。
<bean id="exampleBean" class="examples.ExampleBean"> <!-- setter injection using the nested <ref/> element --> <property name="beanOne"><ref bean="anotherExampleBean"/></property> <!-- setter injection using the neater 'ref' attribute --> <property name="beanTwo" ref="yetAnotherBean"/> <property name="integerProperty" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i) { this.i = i; } }
在前面的例子中,setters被声明为与XML文件中指定的属性相匹配。如下示例使用基于构造函数的DI:
<bean id="exampleBean" class="examples.ExampleBean"> <!-- constructor injection using the nested <ref/> element --> <constructor-arg> <ref bean="anotherExampleBean"/> </constructor-arg> <!-- constructor injection using the neater 'ref' attribute --> <constructor-arg ref="yetAnotherBean"/> <constructor-arg type="int" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ExampleBean( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { this.beanOne = anotherBean; this.beanTwo = yetAnotherBean; this.i = i; } }
在bean定义中指定的构造函数参数将用做参数的构造函数 ExampleBean
。
如今考虑一下这个例子的一个变体,在这里不是使用构造函数,而是告诉Spring调用一个static
工厂方法来返回对象的一个实例:
<bean id = “exampleBean” class = “examples.ExampleBean” factory-method = “createInstance” > <constructor-arg ref = “anotherExampleBean” /> <constructor-arg ref = “yetAnotherBean” /> <constructor-arg value = “1” /> </ bean> <bean id = “anotherExampleBean” class = “examples.AnotherBean” /> <bean id = “yetAnotherBean” class = “examples.YetAnotherBean”/>
public class ExampleBean { // a private constructor private ExampleBean(...) { ... } // a static factory method; the arguments to this method can be // considered the dependencies of the bean that is returned, // regardless of how those arguments are actually used. public static ExampleBean createInstance ( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...); // some other operations... return eb; } }
static
工厂方法的参数是经过<constructor-arg/>
元素提供的,就像实际使用构造函数同样。工厂方法返回的类的类型没必要与包含static
工厂方法的类属于同一类型,尽管在本例中是这样。实例(非静态)工厂方法将以基本相同的方式使用(除了使用factory-bean
属性而不是class
属性),所以在此不讨论细节。
如前一节所述,您能够将bean属性和构造函数参数定义为对其余托管bean(协做者)的引用,或者将它们定义为内联定义的值。Spring的基于XML的配置元数据为此支持其元素<property/>
和 <constructor-arg/>
元素中的子元素类型 。
在value
所述的属性 <property/>
元素指定属性或构造器参数的人类可读的字符串表示。如前所述,JavaBeans PropertyEditors
用于将这些字符串值从a转换String
为属性或参数的实际类型。
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- results in a setDriverClassName(String) call --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="root"/> <property name="password" value="masterkaoli"/> </bean>
如下示例使用p-namespace进行更简洁的XML配置。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/mydb" p:username="root" p:password="masterkaoli"/> </beans>
前面的XML更简洁;然而,错字是在运行时而不是设计时发现的,除非您在建立bean定义时使用支持自动属性完成的IDE(例如IntelliJ IDEA或SpringSource Tool Suite(STS))。强烈建议这种IDE帮助。 您还能够将java.util.Properties实例配置为:
<bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <!-- typed as a java.util.Properties --> <property name="properties"> <value> jdbc.driver.className=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mydb </value> </property> </bean>
Spring容器经过使用JavaBeans 机制将<value/>
元素内部的文本 转换为 java.util.Properties
实例PropertyEditor
。这是一个很好的捷径,它是Spring团队同意<value/>
在value
属性样式上使用嵌套元素的少数几个地方之一。
<bean id = “theTargetBean” class = “...” /> <bean id = “theClientBean” class = “...” > <property name = “targetName” > <idref bean = “theTargetBean” /> </ property> </ bean>
上面的bean定义片断 与下面的片断彻底等价(在运行时):
<bean id = “theTargetBean” class = “...” /> <bean id = “client” class = “...” > <property name = “targetName” value = “theTargetBean” /> </ bean>
第一种形式比第二种形式更可取,由于使用 idref
标签容许容器在部署时验证 所引用的命名bean实际存在。在第二种变体中,不会对传递给bean targetName
属性 的值执行验证 client
。当client
bean实际实例化时,只会发现错误(最有可能致命的结果)。若是这个client
bean是一个 原型 bean,那么这个错字和产生的异常可能只会在容器被部署后很长时间才被发现。
此外,若是被引用的bean位于同一个XML单元中,而且bean名称是bean id,则可使用该 local
属性,该属性容许XML解析器自己在XML文档分析时更早地验证bean id。
<property name = “targetName” > <! - 一个ID为'theTargetBean'的bean必须存在; 不然会抛出异常 - > <idref local = “theTargetBean” /> </ property>
其中<IDREF />元素带来值A共同的地方(至少在早期比Spring 2.0版本)是在配置AOP拦截在 ProxyFactoryBean
bean定义。指定拦截器名称时使用<idref />元素能够防止拼写错误拦截器ID。
该ref
元素是一个<constructor-arg/>
或 <property/>
定义元素中的最后一个 元素。在这里,您将bean的指定属性的值设置为对由容器管理的另外一个bean(协做者)的引用。被引用的bean是其属性将被设置的bean的依赖项,而且在属性设置以前根据须要初始化它。(若是协做者是单身bean,它可能已被容器初始化。)全部引用最终都是对另外一个对象的引用。划定范围和有效性取决因而否经过指定其余对象的ID /名称 bean
,local,
或 parent
属性。
经过标记bean
属性指定目标bean <ref/>
是最通用的形式,而且容许建立对同一个容器或父容器中的任何bean的引用,而无论它是否位于同一个XML文件中。该bean
属性的值可能id
与目标bean 的属性相同,或者与目标bean属性中的值之一相同name
。
<ref bean = “someBean” />
经过local
属性指定目标bean 利用XML解析器验证同一文件中的XML id引用的能力。local
属性的值 必须id
与目标bean 的属性相同 。若是在同一文件中找不到匹配的元素,则XML解析器会发出错误。所以,若是目标bean位于同一个XML文件中,则使用本地变体是最佳选择(以便尽量早地了解错误)。
<ref local = “someBean” />
经过parent
属性指定目标Bean 将建立对当前容器的父容器中的bean的引用。该parent
属性的值可能id
与目标bean 的属性或目标bean 的属性中的一个值相同name
,而且目标bean必须位于当前bean的父容器中。您主要在具备容器层次结构时使用此bean参考变体,而且想要使用与父bean名称相同的代理将父容器中的现有bean包装在父容器中。
<! - 在父上下文中 - > <bean id = “accountService” class = “com.foo.SimpleAccountService” > <! - 根据须要插入依赖关系 - > </ bean> <! - 在子(后代)上下文中 - > <bean id = “accountService” < - bean名称与父bean相同 - > 类= “的org.springframework.aop.framework.ProxyFactoryBean”> <property name = “target” > <ref parent = “accountService” /> <! - 注意咱们如何引用父bean - > </ property> <! - 根据须要插入其余配置和依赖关系 - > </ bean>
甲<bean/>
内部的元件 <property/>
或 <constructor-arg/>
元件定义了一个所谓的 内部bean。
<bean id = “outer” class = “...” > <! - 而不是使用对目标bean的引用,只需定义目标bean inline - > <property name = “target” > <bean class = “com.example.Person” > <! - 这是内部bean - > <property name = “name” value = “Fiona Apple” /> <property name = “age” value = “25” /> < / bean> </ property> </ bean>
内部bean定义不须要定义的id或名称; 该容器忽略这些值。它也忽略了 scope
国旗。内部bean 始终是匿名的,而且它们 始终使用外部bean建立。这是 不是能够内部bean注入到协做不是进入封闭豆等豆类。
在<list/>
, <set/>
,<map/>
,和 <props/>
元素,你将Java的性能和参数Collection
类型 List
,Set
, Map
,和 Properties
分别。
<bean id = “moreComplexObject” class = “example.ComplexObject” > <! - 致使setAdminEmails(java.util.Properties)调用 - > <property name = “adminEmails” > <props> <prop key = “管理员“ > administrator@example.org </ prop> <prop key = ”support“ > support@example.org </ prop> <prop key = ”development“ > development@example.org </ prop> </ props> </ property> <! - 产生一个setSomeList(java.util.List)调用 - > <property name = “someList” > <list> <value>一个列表元素,后跟一个引用 </ value> <ref bean = “myDataSource “ /> </ list> </ property> <! - 致使setSomeMap(java.util.Map)调用 - > <property name = ”someMap“ > <map> <entry key = ”entry“ value = “只是一些字符串” /> <entry key = “a ref” value-ref = “myDataSource”/> </ map> </ property> <! - 致使setSomeSet(java.util.Set)调用 - > <property name = “someSet” > <set> <value>只是一些字符串</ value> <ref bean = “myDataSource” /> </ set> </ property> </ bean>
映射键或值或设置值的值也能够是如下任何元素:
bean | ref | idref | 列表| set | 地图| 道具| 值| 空值
从Spring 2.0开始,容器支持 合并集合。应用程序开发人员能够定义一个父风格<list/>
, <map/>
,<set/>
或 <props/>
元素,并有孩子式的 <list/>
,<map/>
,<set/>
或<props/>
元素继承和父集合覆盖值。也就是说,子集合的值是合并父集合和子集合元素的结果,子集合元素覆盖父集合中指定的值。
这部分关于合并讨论了父子bean机制。不熟悉父代和子代bean定义的读者可能但愿在继续以前阅读相关章节。
如下示例演示了集合合并:
<beans> <bean id="parent" abstract="true" class="example.ComplexObject"> <property name="adminEmails"> <props> <prop key="administrator">administrator@example.com</prop> <prop key="support">support@example.com</prop> </props> </property> </bean> <bean id="child" parent="parent"> <property name="adminEmails"> <!-- the merge is specified on the *child* collection definition --> <props merge="true"> <prop key="sales">sales@example.com</prop> <prop key="support">support@example.co.uk</prop> </props> </property> </bean> <beans>
注意在bean定义的merge=true
属性的<props/>
元素上使用 adminEmails
属性 child
。当 child
bean被容器解析并实例化时,生成的实例具备一个adminEmails
Properties
集合,该 集合包含合并子集合 adminEmails
与父 adminEmails
集合的结果。
administrator=administrator@example.com sales=sales@example.com support = support@example.co.uk
孩子Properties
集合的值设置继承父全部属性元素 <props/>
,和孩子的为值 support
值将覆盖父集合的价值。
这一合并行为一样适用于 <list/>
,<map/>
和 <set/>
集合类型。在<list/>
元素的特定状况下,与List
集合类型相关联的语义(即ordered
值集合的概念)被保留; 父项的值在全部子项列表的值以前。在的状况下Map
, Set
和 Properties
集合类型,没有顺序存在。所以,没有排序的语义在背后的关联的集合类型的效果 Map
,Set
以及 Properties
该容器内部使用实现类型。
您不能合并不一样的集合类型(如a Map
和a List
),而且若是您确实尝试这样作,Exception
则会引起适当的集合类型 。该 merge
属性必须在较低的继承的子定义上指定; merge
在父集合定义上指定属性是多余的,而且不会致使所需的合并。合并功能仅在Spring 2.0及更高版本中可用。
在Java 5及更高版本中,您可使用强类型集合(使用泛型类型)。也就是说,能够声明一个 Collection
只能包含String
元素的类型(例如)。若是您使用Spring将强类型依赖注入 Collection
到bean中,则能够利用Spring的类型转换支持,以便强类型Collection
实例的元素在添加到类型以前转换为适当的类型Collection
。
public class Foo { private Map<String, Float> accounts; public void setAccounts(Map<String, Float> accounts) { this.accounts = accounts; } }
<beans> <bean id="foo" class="x.y.Foo"> <property name="accounts"> <map> <entry key="one" value="9.99"/> <entry key="two" value="2.75"/> <entry key="six" value="3.99"/> </map> </property> </bean> </beans>
当bean 的accounts
属性 foo
准备注入时,有关强类型的元素类型的泛型信息 Map<String, Float>
可经过反射得到。所以,Spring的类型转换基础结构将各类值元素识别为类型 Float
和字符串值9.99, 2.75
,并将3.99
其转换为实际Float
类型。
<bean class = “ExampleBean” > <property name = “email” value = “” /> </ bean>
前面的示例等同于如下Java代码: exampleBean.setEmail("")
。该 <null/>
元素处理null
值。例如
<bean class = “ExampleBean” > <property name = “email” > <null /> </ property> </ bean>
以上配置至关于如下Java代码: exampleBean.setEmail(null)
。
p-名称空间使您可使用bean
元素的属性(而不是嵌套 <property/>
元素)来描述属性值和/或合做bean。
Spring 2.0和更高版本支持带有命名空间的可扩展配置格式,这些命名空间基于XML模式定义。beans
本章讨论的配置格式在XML Schema文档中定义。可是,p-namespace并未在XSD文件中定义,而只存在于Spring的核心中。
如下示例显示了解析为相同结果的两个XML片断:第一个使用标准XML格式,第二个使用p命名空间。
<beans xmlns = “http://www.springframework.org/schema/beans” xmlns:xsi = “http://www.w3.org/2001/XMLSchema-instance” xmlns:p = “http:// www .springframework.org / schema / p“ xsi:schemaLocation = ”http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd“ > <bean name = “classic” class = “com.example.ExampleBean” > <property name = “email” value = “ foo@bar.com ”/> </ bean> <bean name = “p-namespace” class = “com.example.ExampleBean” p:email = “ foo@bar.com ” /> </ beans>
该示例在bean定义中显示了名为email的p名称空间中的一个属性。这告诉Spring包含一个属性声明。如前所述,p-名称空间没有模式定义,所以您能够将该属性的名称设置为属性名称。
下一个示例包含两个更多的bean定义,这两个定义均可以引用另外一个bean:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="john-classic" class="com.example.Person"> <property name="name" value="John Doe"/> <property name="spouse" ref="jane"/> </bean> <bean name="john-modern" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/> <bean name="jane" class="com.example.Person"> <property name="name" value="Jane Doe"/> </bean> </beans>
正如你所看到的,这个例子不只包含使用p-命名空间的属性值,还使用特殊格式来声明属性引用。第一个bean定义用于 <property name="spouse" ref="jane"/>
建立从bean john
到bean 的引用jane
,而第二个bean定义 p:spouse-ref="jane"
用做属性来完成一样的事情。在这种状况下spouse
是属性名称,而该-ref
部分代表这不是一个正值,而是对另外一个bean的引用。
![]() |
p-名称空间不如标准XML格式那么灵活。例如,声明属性引用的格式与结尾的属性发生冲突 |
相似于“带有p-namespace的XML快捷方式”一节,Spring 3.1中新引入的c-namespace容许使用内联属性来配置构造函数参数,而不是嵌套constructor-arg
元素。
让咱们回顾一下从例子中称为“基于构造函数的依赖注入”一节与c
命名空间:
<beans xmlns = “http://www.springframework.org/schema/beans” xmlns:xsi = “http://www.w3.org/2001/XMLSchema-instance” xmlns:c = “http:// www .springframework.org /模式/ C” 的xsi:的schemaLocation = “http://www.springframework.org/schema/beans HTTP://www.springframework.org/schema/beans/spring-beans.xsd”> <bean id = “bar” class = “xyBar” /> <bean id = “baz” class = “xyBaz” /> < - 'traditional'declaration - > <bean id = “foo” class = “xyFoo” > <constructor-arg ref = “bar” /> <constructor-arg ref = “baz” /> <constructor-arg value = “ foo@bar.com ” /> </ bean> < - 'c-namespace'声明- > <bean id = “foo” class = “xyFoo” c:bar-ref = “bar” c:baz-ref = “baz” c:email = “ foo@bar.com ” > </ beans>
该c:
命名空间使用相同的约定做为p:
一个(后-ref
为bean引用),供他们的名字设置构造函数的参数。一样,即便它没有在XSD模式中定义(但它存在于Spring内核中),也须要声明它。
对于构造函数参数名称不可用的罕见状况(一般若是字节码是在没有调试信息的状况下编译的),可使用回退参数索引:
< - 'c-namespace'索引声明 - > <bean id = “foo” class = “xyFoo” c:_ 0 -ref = “bar” c:_ 1 -ref = “baz” >
![]() |
因为XML语法,索引表示法要求存在前导_,由于XML属性名称不能以数字开头(即便某些IDE容许)。 |
实际上,构造器解析机制在匹配参数方面很是有效,因此除非真的须要,不然咱们建议在整个配置中使用名称符号。
<bean id = “foo” class = “foo.Bar” > <property name = “fred.bob.sammy” value = “123” /> </ bean>
该foo
bean有一个fred
属性,该属性具备一个属性,该bob
属性具备 sammy
属性,而且该最终 sammy
属性被设置为该值 123
。为了使这一工做, fred
财产foo
和bob
财产fred
毫不能 null
豆后构造,或 NullPointerException
抛出。
若是一个bean是另外一个bean的依赖,那一般意味着一个bean被设置为另外一个bean的属性。一般,您可使用基于XML的配置元数据中的<ref/>
元素完成此操做 。可是,有时豆类之间的依赖性不那么直接; 例如,类中的静态初始化程序须要被触发,例如数据库驱动程序注册。在depends-on
使用此元素的bean被初始化以前,该属性能够明确地强制一个或多个bean被初始化。如下示例使用该 depends-on
属性来表示对单个bean的依赖关系:
<bean id = “beanOne” class = “ExampleBean” depends-on = “ manager ”/> <bean id = “ manager ”class =“ManagerBean”/>
要表示对多个bean的依赖关系,请提供一个bean名称列表做为depends-on
属性的值,并使用逗号,空格和分号做为有效分隔符
<bean id = “beanOne” class = “ExampleBean” depends-on = “manager,accountDao” > <property name = “manager” ref = “manager” /> </ bean> <bean id = “manager” class = “ ManagerBean“ /> <bean id = ”accountDao“ class = ”xyjdbc.JdbcAccountDao“ />
![]() |
|
默认状况下, ApplicationContext
实现急切地建立和配置全部的singleton bean做为初始化过程的一部分。一般,这种预先实例化是可取的,由于配置或周围环境中的错误是当即发现的,而不是几小时甚至几天后。当发生这种状况是不理想的,你能够经过标记bean定义为延迟初始化,防止单豆的预实例化。一个惰性初始化bean告诉IoC容器在第一次请求时建立一个bean实例,而不是在启动时。
在XML中,此行为由元素lazy-init
上的属性 控制 <bean/>
; 例如:
<bean id = “lazy” class = “com.foo.ExpensiveToCreateBean” lazy-init =“true” /> <bean name = “not.lazy” class = “com.foo.AnotherBean” />
当前面的配置被an消费时 ApplicationContext
,名为bean的bean lazy
在ApplicationContext
启动时并无被急切地预先实例化 ,而not.lazy
bean被急切地预先实例化。
然而,当一个懒惰初始化bean是一个未通过延迟初始化的单例bean的依赖时,它 ApplicationContext
会在启动时建立延迟初始化的bean,由于它必须知足单例的依赖关系。懒惰初始化的bean被注入一个单独的bean中,而且没有被初始化。
您还能够经过使用元素default-lazy-init
上的属性 来控制容器级别的延迟初始化<beans/>
; 例如:
<beans default-lazy-init = “true” > <! - 没有bean将被预先实例化 - > </ beans>
Spring容器能够自动链接合做bean之间的关系。您能够容许Spring经过检查Bean的内容来自动为bean解析协做者(其余bean)ApplicationContext
。自动装配具备如下优势:
当使用基于XML的配置元数据[2]时,可使用元素的autowire
属性为 bean定义指定autowire模式 <bean/>
。自动装配功能有五种模式。您能够指定每一个 bean的自动装配,所以能够选择自动装配的自动装配。
表5.2。自动装配模式
模式 | 说明 |
---|---|
没有 | (默认)无自动装配。Bean引用必须经过一个 |
昵称 | 按物业名称自动装配。Spring会查找与须要自动装配的属性同名的bean。例如,若是一个bean定义被设置为autowire的名称,而且它包含一个主属性(也就是说,它有一个 setMaster(..)方法),Spring会查找名为的bean定义 |
byType的 | 若是属性类型中只有一个bean存在于容器中,则容许属性为自动装配。若是存在多于一个,则会引起致命异常,这代表您可能不会为该bean 使用byType自动装配。若是没有匹配的bean,则什么都不会发生; 该物业未设置。 |
constructor | 相似于byType,但适用于构造函数参数。若是容器中不存在惟一的构造函数参数类型的bean,则会引起致命错误。 |
使用byType或构造函数 自动装配模式,您能够连线阵列和类型集合。在这种状况下 全部被提供在所述容器内自动装配候选匹配所指望的类型,为了知足的依赖性。若是预期的密钥类型是,则能够自动装入强类型的地图 String
。自动装配的Maps值将由全部与预期类型匹配的bean实例组成,Maps键将包含相应的bean名称。
您能够将autowire行为与依赖性检查结合起来,这是在自动装配完成后执行的。
自动装配在项目中一导致用时效果最佳。若是一般不使用自动装配,开发人员可能会使用它来仅链接一个或两个bean定义。
考虑自动装配的局限性和缺点:
显式依赖关系property
和 constructor-arg
设置老是覆盖自动装配。您不能自动调用所谓的 简单属性,如基元 Strings
,和Classes
(以及这种简单属性的数组)。这个限制是经过设计。
自动装配不如准确布线。虽然,如上表所述,Spring在注意避免猜想可能会有意想不到的结果的状况下进行猜想,但您的Spring管理的对象之间的关系再也不明确记录。
布线信息可能没法用于可能从Spring容器生成文档的工具。
容器中的多个bean定义能够匹配由setter方法或构造函数参数指定的类型以进行自动装配。对于数组,集合或地图,这不必定是个问题。然而,对于指望单一值的依赖关系,这种不明确性不是任意解决的。若是没有惟一的bean定义可用,则抛出异常。
在后一种状况下,您有几种选择:
放弃自动布线以支持显式布线。
经过设置其autowire-candidate
属性, 避免为bean定义自动装配, false
以下一节所述。
经过将元素 的属性 设置为,将单个bean定义指定为 主要候选者 。primary
<bean/>
true
若是您使用的是Java 5或更高版本,请参见第5.9节“基于注释的容器配置”中所述实现基于注释的配置可用的更细粒度的控制。
在每一个bean的基础上,您能够从自动装配中排除一个bean。在Spring的XML格式中,设置元素的autowire-candidate
属性<bean/>
为 false
; 容器使该特定的bean定义对自动装配基础结构不可用(包括注释样式配置等@Autowired
)。
您还能够根据与bean名称的模式匹配来限制自动导向候选项。顶级<beans/>
元素在其default-autowire-candidates
属性中接受一个或多个模式 。例如,要将autowire候选者状态限制为名称以存储库结尾的任何bean ,请提供值* Repository。要提供多种模式,请在逗号分隔列表中定义它们。 bean定义属性的显式值true
或者false
对于bean定义autowire-candidate
属性的显式值老是优先的,对于这样的bean,模式匹配规则不适用。
这些技术对于不想经过自动装配注入其余bean的bean很是有用。这并不意味着排除的bean自己不能使用自动装配进行配置。相反,该bean自己不是自动装配其余bean的候选者。
在大多数应用场景中,容器中的大部分bean都是单例。当单例bean须要与另外一个单例bean协做,或者非单例bean须要与另外一个非单例bean协做时,一般经过将一个bean定义为另外一个bean的属性来处理依赖。当bean生命周期不一样时会出现问题。假设单例bean A须要使用非单例(原型)bean B,可能在A上的每一个方法调用上。容器只建立一次单例bean A,所以只有一次机会来设置属性。每次须要时,容器都不能向bean A提供bean B的新实例。
解决方案是放弃一些控制反转。您能够经过实现 接口来让bean A知道容器ApplicationContextAware
,而且每当bean A须要时,经过对容器的getBean(“B”)调用请求(一般是新的)bean B实例。如下是这种方法的一个例子:
// a class that uses a stateful Command-style class to perform some processing package fiona.apple; // Spring-API imports import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class CommandManager implements ApplicationContextAware { private ApplicationContext applicationContext; public Object process(Map commandState) { // grab a new instance of the appropriate Command Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } protected Command createCommand() { // notice the Spring API dependency! return this.applicationContext.getBean("command", Command.class); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
前面的内容是不可取的,由于业务代码知道而且耦合到Spring框架。方法注入是Spring IoC容器的一个高级特性,它容许以干净的方式处理这个用例。
查找方法注入是容器覆盖容器管理的bean上方法的能力,以返回容器中另外一个命名bean的查找结果。查找一般包含一个原型bean,如前一节所述。Spring Framework经过使用CGLIB库中的字节码生成来动态生成覆盖该方法的子类,从而实现了此方法注入。
![]() |
为了使这个动态子类工做,Spring容器将继承的类不能成为 |
查看CommandManager
前面的代码片断中的类,你会发现Spring容器会动态地覆盖该createCommand()
方法的实现 。您的 CommandManager
类将不会有任何Spring依赖关系,如在从新构造的示例中所示:
<!-- a stateful bean deployed as a prototype (non-singleton) --> <bean id="command" class="fiona.apple.AsyncCommand" scope="prototype"> <!-- inject dependencies here as required --> </bean> <!-- commandProcessor uses statefulCommandHelper --> <bean id="commandManager" class="fiona.apple.CommandManager"> <lookup-method name="createCommand" bean="command"/> </bean>
在包含要注入的方法的客户端类(在这种状况下为CommandManager)中,要注入的方法须要如下形式的签名: <public | protected> [abstract] <return-type> theMethodName(no-arguments); 若是方法是抽象的,则动态生成的子类将实现该方法。不然,动态生成的子类会覆盖原始类中定义的具体方法。例如:
<!-- a stateful bean deployed as a prototype (non-singleton) --> <bean id="command" class="fiona.apple.AsyncCommand" scope="prototype"> <!-- inject dependencies here as required --> </bean> <!-- commandProcessor uses statefulCommandHelper --> <bean id="commandManager" class="fiona.apple.CommandManager"> <lookup-method name="createCommand" bean="command"/> </bean>
在包含要注入的方法的客户类( CommandManager
在这种状况下)中,要注入的方法须要如下形式的签名:
<public | protected> [abstract] <return-type> theMethodName(no-arguments);
若是该方法是abstract
,则动态生成的子类将实现该方法。不然,动态生成的子类会覆盖原始类中定义的具体方法。例如
<! - 做为原型部署的有状态bean(非单 实例) - > <bean id = “command” class = “fiona.apple.AsyncCommand” scope = “prototype” > <! - 根据须要在这里注入依赖关系- > </ bean> <! - commandProcessor使用statefulCommandHelper - > <bean id = “commandManager” class = “fiona.apple.CommandManager” > <lookup-method name = “createCommand” bean = “command” /> </ bean>
标识为commandManager的bean createCommand()
在须要命令 bean 的新实例时调用其本身的方法。你必须当心地将command
bean 部署为原型,若是这其实是须要的话。若是它做为单例部署,command
则每次都返回同一个bean 实例。
![]() |
感兴趣的读者也能够找到 |
一种比查找方法更少用的方法注入形式注入可以用另外一种方法实现来替换托管bean中的任意方法。在实际须要功能以前,用户能够安全地跳过本节的其他部分。
使用基于XML的配置元数据,您可使用该 replaced-method
元素将现有的方法实现替换为已部署的bean。考虑下面的类,咱们想要覆盖一个方法computeValue:
public class MyValueCalculator { public String computeValue(String input) { // some real code... } // some other methods... }
实现org.springframework.beans.factory.support.MethodReplacer
接口的类 提供了新的方法定义。
/** meant to be used to override the existing computeValue(String) implementation in MyValueCalculator */ public class ReplacementComputeValue implements MethodReplacer { public Object reimplement(Object o, Method m, Object[] args) throws Throwable { // get the input value, work with it, and return a computed result String input = (String) args[0]; ... return ...; } }
部署原始类并指定方法覆盖的bean定义以下所示:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator"> <!-- arbitrary method replacement --> <replaced-method name="computeValue" replacer="replacementComputeValue"> <arg-type>String</arg-type> </replaced-method> </bean> <bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
您可使用<arg-type/>
元素中的一个或多个包含 元素 <replaced-method/>
来指示被覆盖的方法的方法签名。只有当方法过载而且类中存在多个变体时,参数的签名才是必需的。为了方便,参数的类型字符串多是彻底限定类型名称的子字符串。例如,如下所有匹配 java.lang.String
:
java.lang.String String Str
因为参数的数量一般足以区分每种可能的选择,所以只需键入与参数类型匹配的最短字符串,此快捷键就能够节省大量输入。