Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.4.2 依赖注入和配置的细节

6.1.1 细节上的依赖注入和配置

如上一节所述,你可以将bean属性和构造函数参数定义为对其他托管bean(协作者)的引用,或者作为内联定义的值。为此,Spring的基于XML的配置元数据支持其<property /><constructor-arg />元素中的子元素类型。

直接值(基本类型、String等)

<property />元素的value属性将属性或构造函数参数指定为人类可读的字符串表示形式。Spring转换服务用于将这些值从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命名空间进行更简洁的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定义时使用支持自动属性填充的IntelliJ IDEASpring Tool SuiteSTS)等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>

Spring容器通过使用JavaBeans PropertyEditor机制将<value />元素内的文本转换为java.util.Properties实例。嵌套<value />元素是一个很好的快捷方式,并且是Spring团队推荐使用的少数几个地方之一。

idref元素

idref元素只是一种防错方法,可以将容器中另一个beanid(字符串值 - 而不是引用)传递给<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 beantargetName属性的值执行验证。当client bean实际实例化时,才会发现错误(最有可能导致致命结果)。 如果client bean是原型bean,则只能在部署容器后很长时间才能发现此错误和产生的异常。

注意:4.0 beans xsd不再支持idref元素的local属性,因为它不再提供值给常规bean的引用。 在升级到4.0架构时,只需将现有的idref本地引用更改为idref bean即可。

<idref />元素传值的常见地方(至少在Spring 2.0之前的版本中)是在AOP拦截器的配置中定义ProxyFactoryBean bean的地方。指定拦截器名称时使用<idref />元素可以防止拦截器ID拼写错误。

对其他Bean的引用

ref元素是<constructor-arg /><property />定义元素中的最后一个元素。在这里,你将bean的指定属性的值设置为对容器管理的另一个bean(协作者)的引用。引用的bean是属性将被设置的bean的依赖项,并且在设置属性之前根据需要按需初始化。(如果协作者是单例bean,它可能已经被容器初始化了。)所有引用最终都是对另一个对象的引用。作用范围和校验取决于你是否通过beanlocalparent属性指定其他对象的id / name

通过<ref />标记的bean属性指定目标bean是最常用的形式,并允许创建对同一容器或父容器中的任何bean的引用,而不管它是否在同一个XML文件中。bean属性的值可以与目标beanid属性相同,也可以是目标beanname属性中的值之一。

<ref bean="someBean"/>

通过parent属性指定目标bean会创建一个属于当前容器的父容器中的bean的引用。 parent属性的值可以与目标beanid属性相同,也可以与目标beanname属性中的一个值相同,并且目标bean必须位于当前bean的父容器中。你主要是在拥有容器的层次结构并且希望通过一个与父bean具有相同名称的代理包装一个父容器中的现有的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>

注意:4.0 bean xsd不再支持ref元素的local属性,因为它不再提供常规bean引用的值。 升级到4.0架构时,只需将现有的ref本地引用更改为ref bean

内部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或名称; 如果指定,则容器不使用此类值作为标识符。容器还会在创建时忽略scope标志:内部bean始终是匿名的,并且始终通过外部bean创建它们。 除了注入到封闭bean,不可能将内部bean注入协作bean,或者单独访问它们。

作为极端情况,可以接收来自自定义范围的销毁回调,例如,对一个包含在单例bean中的request范围的内部bean:内部bean实例的创建将绑定到其包含的bean,但是销毁回调允许它加入到request范围的生命周期中。这不是常见的情况; 内部bean通常只是共享其包含bean的范围。

集合

<list /><set /><map /><props />元素中,分别设置Java Collection类型ListSetMapProperties的属性和参数。

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">[email protected]</prop>
            <prop key="support">[email protected]</prop>
            <prop key="development">[email protected]</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>

mapkey值或者value值,或者set值,也可以是以下任何元素:

bean | ref | idref | list | set | map | props | value | null
集合合并

Spring容器还支持集合的合并。 应用程序开发人员可以定义<list /><map /><set /><props />元素的父样式,并且可以具有从父集合中继承并覆盖的值的<list /><map /><set /><props />元素的子样式。也就是说,子集合的值是合并父集合和子集合的元素的结果,子集合的元素覆盖父集合中指定的值。

关于合并的这一部分讨论了父子bean机制。在继续本节之前,不熟悉父bean和子bean定义的读者可以阅读相关部分

以下示例阐释了集合合并:

<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">[email protected]</prop>
                <prop key="support">[email protected]</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">[email protected]</prop>
                <prop key="support">[email protected]</prop>
            </props>
        </property>
    </bean>
<beans>

注意在子bean定义的adminEmails属性的<props />元素上使用merge = true属性。当容器解析并实例化子bean时,生成的实例有一个adminEmails属性集合,其中包含子项的adminEmails集合与父项的adminEmails集合的合并结果。

[email protected]

[email protected]

[email protected]

子的Properties集合的值继承了父<props />的所有属性元素,并且子的support值覆盖父集合中的值。

此合并行为同样适用于<list /><map /><set />集合类型。 在<list />元素的特定情况下,保持与List集合类型相关联的语义,即有序的值集合的概念; 父级的值位于所有子级列表的值之前。对于MapSetProperties集合类型,不存在排序。因此,对于作为容器内部使用的,以MapSetProperties实现类型为基础的集合类型,没有排序语义。

集合合并的局限性

你不能合并不同的集合类型(例如MapList),如果你尝试这样做,则会抛出相应的Exception。必须在更低级的,继承的子定义上指定merge属性; 在父集合定义上指定merge属性是多余的,不会导致所需的合并。

强类型的集合

Java 5引入了泛型类型,因此你可以使用强类型集合。也就是说,例如,可以声明只能包含String元素Collection类型。 如果你使用Spring依赖注入一个强类型的Collectionbean中,你可以利用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>

foo beanaccounts属性准备好注入时,可以通过反射获得有关强类型Map <StringFloat>的元素类型的泛型信息。因此,Spring的类型转换体系结构将各种值元素识别为Float类型,并将字符串值9.99,2.753.99转换为实际的Float类型。

Null和空字符串值

Spring将属性为空的参数视为空Strings。以下基于XML的配置元数据片段将email属性设置为空String值(“”)。

<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命名空间简化XML配置

p命名空间可以让你使用bean元素的属性代替嵌套的<property />元素来描述属性值和/或协作bean

Spring支持具有命名空间的可扩展配置格式,这些命名空间基于XML Schema定义。 本章中讨论的bean配置格式定义在XML Schema文档中。然而,p命名空间未在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="[email protected]"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="[email protected]"/>
</beans>

该示例显示了bean定义中名为emailp命名空间中的属性。 这告诉Spring包含一个属性声明。 如前所述,p命名空间没有模式定义,因此你可以将attribute的名称设置为property的名称。

下一个示例包含超过两个的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 johnbean jane的引用,而第二个bean的定义使用p:spouse-ref =”jane”作为属性,去做与第一个Bean完全相同的事情。在这种情况下,spouse是属性名称,而-ref部分表示这不是直接值,而是对另一个bean的引用。

注意:p命名空间不如标准XML格式灵活。例如,声明属性引用的格式与以Ref结尾的属性冲突,而标准XML格式则不然。 我们建议你仔细选择你的方法并将其告诉给你的团队成员,以避免生成同时使用这三种方法的XML文档。

c命名空间简化XML配置

类似于c命名空间简化XML配置”章节,Spring 3.1中新引入的c命名空间允许使用内联属性来配置构造函数参数代替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/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="bar" class="x.y.Bar"/>
    <bean id="baz" class="x.y.Baz"/>

    <!-- traditional declaration -->
    <bean id="foo" class="x.y.Foo">
        <constructor-arg ref="bar"/>
        <constructor-arg ref="baz"/>
        <constructor-arg value="[email protected]"/>
    </bean>

    <!-- c-namespace declaration -->
    <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="[email protected]"/>

</beans>

c:名称空间使用与p:(以-ref结尾的,用于bean引用)相同的惯例,用于按名称设置构造函数参数。同样,它需要声明,即使它没有在XSD模式中定义(但它存在于Spring核心中)。

对于像构造函数参数名称不可用的这样极少的情况(通常,如果字节码是在没有调试信息的情况下编译的话),可以使用参数索引:

<!-- c-namespace index declaration -->
<bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>

注意:由于XML语法,索引表示法要求:以_作为开头的XML属性名称不能以数字开头(即使某些IDE允许它)。

实际上,构造函数解析机制在匹配参数方面非常有效,因此除非确实需要,否则我们建议在整个配置中使用名称表示法。

复合属性名称

只要除最终属性名称之外的此路径的所有组件都不为null,你可以在设置bean属性时使用复合或嵌套属性名称。请思考以下bean定义。

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

foo bean有一个fred属性,它有一个bob属性,它有一个sammy属性,最后的sammy属性被设置为值123。为了使其生效,foofred属性和bob属性,在构造bean之后,fred禁止为null,否则会抛出NullPointerException

猜你喜欢

转载自www.cnblogs.com/springmorning/p/10327832.html