Spring 说明文档7——IoC容器(4)

来源:互联网 发布:美国爆炸氢弹数据多少 编辑:程序博客网 时间:2024/05/16 00:48

说实话,翻译真的很累,自己翻译过才发现。今天翻翻博客,发现还真有人看这个文章,虽然自己翻译的不好,有一个回复也真的很兴奋。新的一年还是会继续翻译文档的,但翻译很难(对我这种英语小白来说),没法保证持续的更新,只能是不定期的更新了,希望在速度和质量上,能让大家接受,也能给自己一个积累吧。


之前没有翻译,一个是因为工作比较忙,一个也是因为想积累一些再发,结果就停下来了,现在先把积累的发出来,后面继续吧。


3.4.2 依赖和配置详细描述


如前面所描述的,你能够在定义bean的属性和构造函数的参数时引用其他被管理的bean,或者也可以将这些bean定义在行内。spring基于xml的元数据描述支持通过<property/>和<constructor-arg/>元素做这样的事情。


3.4.2.1 直接的值(基础类型,字符串等)


一个可读的字符串通过<property/> 元素的value属性为bean指定属性或者构造参数。就像之前提到的,javabean的PropertyEditors 将这些值从字符串转换到属性和构造参数实际使用的类型。


<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-3.0.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更加简洁,然而,参数类型不再是在设计期声明而要在运行时才能找到,除非你使用如 IntelliJ IDEA或者SpringSource Tool Suite 这样支持创建bean时自动完成属性的IDE。我们推荐这样的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>


通过javabeans的PropertyEditor 机制Spring 构造器转换<value/>中的文本形成java.util.Properties 的实例。这是一个快捷的方法。这是Spring团队赞同<value/> 元素内嵌套value 属性的少数几个用法之一。


idref元素


idref元素用来将容器内其它bean的id传给<constructor-arg/> 或 <property/>元素(字符值传递而非引用传递),同时提供错误验证功能。


<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是否存在。而第二种方式中,传给client bean的targetName属性值并没有被验证。只有在client bean被确实的实例化时才被发现(可能伴随着致命的错误)。


此外,如果被引用的bean在同一XML文件内,且bean名字就是bean id,那么可以使用local属性,此属性允许XML解析器在解析XML文件时来对引用的bean进行验证。


<property name="targetName">
   <!-- a bean with an id of 'theTargetBean' must exist, else an XML exception will be thrown -->
   <idref local="theTargetBean"/>
</property>


上面的例子与在ProxyFactoryBean 定义中使用<idref/>元素指定AOP 拦截器的相同之处在于:如果使用<idref/>元素指定拦截器名字,可以避免因一时疏忽导致的拦截器ID拼写错误。


3.4.2.2 引用其他bean(合作者)
ref元素是 <constructor-arg/> 或者 <property/> 内部的最终元素。通过它,设置一个bean的属性引用另外一个被容器管理的bean。这个引用的bean被bean的属性依赖,在需要的时候被初始化(如果是单例模式的bean,可能已经被容器初始化)。所有引用最终引用一个对象。生命周期、合法性的判断依赖于通过bean、local或者parent属性指定对象的的id或者name。


通过ref标签的bean属性指定目标bean是最常用的一种形式。这样在同样的构造器和父构造器中创建任何bean的引用,无论是否在同一XML文件中。bean属性的值与目标bean的id相同,或者是目标bean的name属性中的一个。


<ref bean="someBean"/>


通过local属性指定目标bean的id影响xml解析器查找同一个xml文件中的bean。local属性的值必须是同一个xml文件中的目标bean的id值。如果在同一个文件中没有标记元素,xml解析器抛出一个错误。同时,如果目标bean在同一个xml文件中,使用local属性是最好的选择(为了更早发现问题)。


<ref local="someBean"/>


通过parent属性在当前容器的父容器中创建目标bean的引用。parent的值同样可以使用目标bean的id或者某个name。目标bean必须在当前容器的父容器中,使用parent属性的主要用途是为了用某个与父容器中的bean同名的代理来包装父容器中的一个bean。


<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
  <!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService"  <-- bean name is the same as the parent bean -->
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/>  <!-- notice how we refer to the parent bean -->
    </property>
  <!-- insert other configuration and dependencies as required here -->
</bean>


3.4.2.3 内部bean
在<property/> 或者 <constructor-arg/>元素内部定义bean元素被称作内部bean


<bean id="outer" class="...">
<!-- instead of using a reference to a target bean, simply define the target bean inline -->
<property name="target">
  <bean class="com.example.Person"> <!-- this is the inner bean -->
    <property name="name" value="Fiona Apple"/>
    <property name="age" value="25"/>
  </bean>
</property>
</bean>


内部bean不需要定义id或者name;容器忽略这些值。它忽略了scope属性,内部bean总是匿名的,且具有prototype 的scope属性。不可能将内部bean注入到包含该内部bean之外的bean。


3.4.2.4 集合

使用<list/>, <set/>, <map/>, 和 <props/> 元素,分别将java的集合类型List、Set、Map和Properties设置到属性和参数。


<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
  <props>
      <prop key="administrator">administrator@example.org</prop>
      <prop key="support">support@example.org</prop>
      <prop key="development">development@example.org</prop>
  </props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
  <list>
      <value>a list element followed by a reference</value>
      <ref bean="myDataSource" />
  </list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
  <map>
      <entry key="an entry" value="just some string"/>
      <entry key ="a ref" value-ref="myDataSource"/>
  </map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
  <set>
      <value>just some string</value>
      <ref bean="myDataSource" />
  </set>
</property>
</bean>


map对象的key、value值或者set对象的值可以是以下元素中的一种:


bean | ref | idref | list | set | map | props | value | null


集合合并


在spring2.0开始,容器支持集合和合并。开发者可以定义<list/>, <map/>, <set/> 或 <props/>元素作为父样式,定义子样式的<list/>, <map/>, <set/> 或 <props/>元素继承并覆盖父集合的值。也就是说,子集合的值是父、子集合合并的结果,子集合元素覆盖了父集合中特定的元素。


这里合并讨论了父子bean的机制。对于不常使用的人,可以先去阅读相关章节再继续(见3.7)。


下面例子展示了集合的合并


<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 adminEmails属性定义中的<props/>元素中使用了marge=true属性。当子bean被解析并实例化时,实例的结果是adminEmails Properties 与它的父bean合并的结果。


administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk


子Properties集合继承了父<props/>的结果并且support 值被覆盖。


合并的行为同样可应用于<list/>, <map/>, 和 <set/>的集合类型。 <list/>元素有点特殊,涉及到list集合类型的语义方面,也就是说,集合的顺序是被维护的。父值的顺序在子值之前。作为Map、Set和Properties集合类型,就不存在顺序了。因此作为相关的Map、Set及Properties实现基础的集合类型在容器内部没有排序的语义。


集合合并的局限


你不能够合并不同类型的集合(如一个Map和一个List合并),如果你去做了,将抛出一个异常。marge属性必须在低层,继承的,子bean定义中使用;在父bean中指定marge属性是没有结果的。合并特性出现在spring2.0以后


强类型集合(java 5以后)
在java 5及以后的版本中,你能够使用强类型集合(使用一般的类型)。也就是说,定义一个只能容纳String元素的集合类型是可能的(如下面的例子)。如果你使用spring依赖注入将一个强类型集合注入到bean中,你会带有Spring类型转换的特性,将你的强类型实例转换到一个恰当的类型后,增加到集合中。


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>


在foobean的accounts属性被注入之前,通过反射,利用强类型Map<String, Float>的泛型信息,Spring的底层类型转换机制将会把各种value元素值转换为Float类型,因此字符串9.99、2.75及3.99就会被转换为实际的Float类型。


3.4.2.5 Null和空字符


Spring将属性中的空参数当做空字符串处理。下面的基于xml的配置元数据给email属性设置一个空字符串("")。


<bean class="ExampleBean">
<property name="email" value=""/>
</bean>




在上面的范例中与其等价的java代码为:“exampleBean.setEmail("")”。<null/>元素代表null值,如下例:


<bean class="ExampleBean">
<property name="email"><null/></property>
</bean>


上面的配置与“exampleBean.setEmail(null).”java代码等价。


3.4.2.6 XML中使用p-namspace


p-namespace能够使你用bean元素的属性代替<property/>元素描述属性的值和引用的bean。


spring2.0以后,在xml schema定义的基础上,使用namespace扩展了配置格式。bean配置格式的讨论在相关章节中。然而,namespace不能够定义在XSD中,并只存在于spring和核心中。


下面的嗲吗展示了两个xml片段产生同样的解析结果:第一个使用xml格式,另一个使用p-namespace。


<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-3.0.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定义中使用p-namespace调用email。告诉spring 包含属性定义。像之前提到的,p-namespace没有schema定义,所以,你能够指定一个属性的名称。


下面的例子包含多个引用同一个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-3.0.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-namespace的属性,而且使用特殊的格式定义了属性的引用。第一个bean使用<property name="spouse" ref="jane"/>穿件一个叫john的bean应用了jane bean,第二个bean使用p:spouse-ref="jane" 做了同样的事情。在例子中 spouse是一个属性的名字,然而“-ref”部分指出这不是一个直接的值,而是指定一个bean的引用。


注意
p-namespace不像标准的xml格式那么灵活。例如,定义属性引用的格式与ref结束的属性是冲突的。然而,标准xml格式就没有这个问题。仅以你小心的选择你使用的方法并与你的团队沟通,避免在xml文档中同时使用三个方法。


3.4.2.7 合并属性名称


当设置bean的属性时,你能够使用合并或者内部的属性名,只要所有组件的路径除了最后的属性都不为null。参考下面的范例:


<bean id="foo" class="foo.Bar">
<property name="fred.bob.sammy" value="123" />
</bean>


foo bean有个fred属性,此属性有个 bob属性,而bob属性又有个sammy属性,最后把sammy属性设置为123。为了让此定义能工作, foo的fred属性及fred 的bob属性在bean被构造后都必须非空,否则将抛出NullPointerException异常。


3.4.3 使用depends-on


如果一个bean依赖于其他的bean,通常意味着一个bean作为属性设置到其他bean中。典型的情况是你使用<ref/>元素完成xml元数据配置。然而,有时两个bean的依赖并不那么直接;例如,类的内部静态初始化触发一个数据库驱动的注册。depend-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="x.y.jdbc.JdbcAccountDao" />




注意
在bean定义中的depends-on属性能够定义初始化的次序(只针对单例bean来说)和销毁的次序。依赖bean使用depends-on定义关系,给定的bean先销毁。depends-on也能够控制停止的顺序。


3.4.4 延迟初始化bean


默认情况下,ApplicationContext实例在初始化进程中尽可能的配置和创建所有的单例bean。通常,预先初始化是合适的,因为配置和周边环境中的错误会被及时的发现,而不用等到几小时甚至几天后。如果不希望使用这种行为,你能够使用延迟初始化的方法代替单例bean在定义时的初始化。延迟初始化bean告诉IoC容器当第一次bean请求时完成初始化,而不是在启动时。


在XML中,这种行为通过bean元素的lazy-init属性控制,例如:


<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>


<bean name="not.lazy" class="com.foo.AnotherBean"/>


当配置被ApplicationContext使用之前,被延迟的bean不会被ApplicationContext在启动时初始化,相对的,非延迟的bean会更早的完成实例化过程。


然而,当一个延迟初始化bean被非延迟单例bean依赖,由于需要保证单例依赖的安全性,ApplicationContext会在启动时实例化这个延迟初始化的bean。延迟实例化bean被注入到非延迟单例bean中。


你还能够通过beans元素的default-lazy-init 元素在容器层上使用延迟实例化方法。例如:


<beans default-lazy-init="true">
  <!-- no beans will be pre-instantiated... -->
</beans>


3.4.5 自动装配合作者


spring容器能够自动装配bean之间的关系。你能够决定spring自动装配bean的合作关系。自动装配有如下的优点:


。自动装配能够显著的减少需要配置的属性和构造器参数(其他机制在本章的其他地方讨论)。


。自动装配能够根据你的对象发展更新配置。例如:如果你需要增加依赖一个类,自动装配能够不修改配置的情况下自动完成这个更新。因此,不需要在胆码改变时明确的切换选项,使得代码更加稳定,这种开发阶段非常有用。


基于XML的元数据配置你能够通过bean元素的autowire属性指定自动装配。自动装配功能有5中模式,你能够为每个bean指定自动装配,因此能选择如何自动装配每个bean。


表3.2 自动装配模式


no默认的,不自动装配,bean的引用必须通过ref元素声明。在较大的部署中不推荐改变默认设置,因为指定合作者明确的给出了更多更清晰的控制。一定程度上,他起到系统的结构化文档作用。


byName依靠属性名称自动装配。Spring查找与bean同名的属性进行装配。例如:如果一个bean定义了这个自动装配形式,它包含一个叫做master属性(这里,它也含有setMaster(..)方法),spring查找叫做master的bean,将其set到master属性中。


byType如果容器中明确的村中一个bean与某个bean属性的类型相同,允许进行自动装配。如果存在多于一个bean与其类型相同,系统抛出一个致命异常。表明你不能使用byType进行自动装配。如果没有找到依赖类型的bean,什么也不会做,这个属性不会被设置。


constructor类似于byType,仅适用于构造函数。如果容器中不能明确的确定一个bean的构造函数参数的类型,抛出一个致命错误。




使用byType或者constructor自动装配模式,你能够装配数据和类型集合。这种情况下,容器内所有自动装配的候选被提供出来满足依赖。如果明确指定String作为Map的key类型,你能够强类型化自动装配。自动装配所有指定类型的bean实例,使用bean的name作为map的key。


你能将依赖检查与自动装配行为合并在自动装配完成后进行检查。


3.4.5.1 自动装配的局限性和缺点


自动装配从头至尾的在一个项目中使用是最好的。如果不是全部的使用,它会使开发者在定义一个或两个bean是产生困惑。


要考虑如下自动装配缺点和局限性:


。属性和构造函数参数的明确配置可以覆盖自动装配。你不能自动装配简单属性,如基本类型、String、Classes,这是在设计期的现在。


。自动装配很少去明确的进行装配。虽然上面的表格标识,spring会很小心的避免装配产生歧义,它仍然没有一个明确的结果,spring管理的对象关系不再是一个明确的文档。


。装配信息不能通过工具生成文档。


。Multiple bean definitions within the container may match the type specified by the setter method or constructor argument to be autowired. For arrays, collections, or Maps, this is not necessarily a problem. However for dependencies that expect a single value, this ambiguity is not arbitrarily resolved. If no unique bean definition is available, an exception is thrown.


针对后面的情况,你可以有这么集中选择:


。在关心的部分放弃自动装配


。参照下一章节的描述,使用autowire-candidate 属性为false,避免自动装配


。在指定的单例bean定义中,指定bean的primary属性为true,作为主要的候选


。如果你使用java5,或者更高版本,使用基于注释的配置完成更细粒度的配置。详见章节3.9


3.4.5.2 从自动装配中排除bean


在pre-bean基础上,你能从自动装配中排除一个bean。在spring的xml格式中设置bean的autowire-candidate 属性为false;容器对指定的bean不使用自动装配(包括注释样式的配置,如 @Autowired)。


你能够通过表达式匹配bean的名字限制自动装配。顶级元素<beans/>通过default-autowire-candidates 属性接收一个或多个表达式。例如:限制以“Repository”结尾的bean进行自动装配,指定值为“*Repository”。多个表达式使用逗号分隔。bean元素的属性autowire-candidate设置为true或者false具有更高的优先级,这些bean将忽略default-autowire-candidates的设置。