SpringFramework核心技术一(IOC:详细的依赖和配置)

依赖和配置的详解

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


一、直接值(元数据、字符串和其他)

1.1 几种参数的属性的解释

在value所述的属性<property/>元素指定Bean的属性或构造器参数的使人友好可读的字符串表示。Spring的转换服务用于将这些值从a String字符串传唤为属性或参数的实际类型。

  • 1.1 以一个数据库配置类为例
  • 1.2 传统的配置方式
<!-- 为数据库类BasicDataSource的属性配置值-->
<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>
  • 1.3 基于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>
  • 1.4 基于java.util.Properties的配置
    Spring容器通过使用JavaBeans 机制将元素内部的文本转换为 java.util.Properties实例PropertyEditor。这是一个很好的捷径,它是Spring团队倾向于<value/>在value属性样式上使用嵌套元素的少数几个地方之一。
<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>
  • 1.5 idref元素
    该idref元素只是一种防错的方式,将容器中另一个bean 的id(字符串值 - 不是引用)传递给or 元素。
<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>
  • 1.6 上面的bean定义片段与下面的片段完全等价(在运行时):
<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>
  • 1.7 1.5中的idref比1.6中的形式更可取,因为使用idref标签允许容器在部署时验证所引用的命名bean实际存在。在第二种变体中,不会对传递给bean targetName属性的值执行验证client。当clientbean实际实例化时,只会发现错误(最有可能致命的结果)。如果这个client bean是一个原型 bean,那么这个错字和产生的异常可能只会在容器被部署后很长时间才被发现。

  • 1.8 4.0 beans xsd不再支持local该idref元素的属性,因为它不再提供超过常规bean引用的值。只需将现有idref local引用更改为idref bean升级到4.0模式时。

  • 1.9 其中一个共同的地方(至少在早期比Spring 2.0版本)元素带来的值在配置AOP拦截在 ProxyFactoryBeanbean定义。当您指定拦截器名称时使用元素可以防止拼写错误拦截器ID。

二、引用其他Bean类

2.1 ref

  • 该ref元素是一个<constructor-arg/><property/>定义元素中的最后一个元素。在这里,您将bean的指定属性的值设置为对由容器管理的另一个bean(协作者)的引用。被引用的bean是其属性将被设置的bean的依赖项,并且在属性设置之前根据需要初始化它。(如果协作者是单例bean,它可能已被容器初始化。)所有引用最终都是对另一个对象的引用。划定范围和有效性取决于是否通过指定其他对象的ID /名称bean,local,或parent属性。
  • 通过标记的bean属性指定目标bean 是最通用的形式,并且允许创建对同一个容器或父容器中的任何bean的引用,而不管它是否位于同一个XML文件中。该bean属性的值可能id与目标bean 的属性相同,或者与目标bean属性中的值之一 相同name。
<ref bean="someBean"/>

2.2 对当前容器的父容器中的bean的引用

  • 通过parent属性指定目标Bean 将创建对当前容器的父容器中的bean的引用。该parent 属性的值可能id与目标bean 的属性或目标bean 的属性中的一个值相同name,并且目标bean必须位于当前bean的父容器中。您主要在具有容器层次结构时使用此bean参考变体,并且想要使用与父bean名称相同的代理将父容器中的现有bean包装在父容器中。
  • 父容器中的bean
<!-- in the parent context 父容器中的bean:SimpleAccountService-->
<bean id="accountService" class="com.foo.SimpleAccountService">
    <!-- insert dependencies as required as here -->
</bean>
  • 子容器中创建对父容器中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 beans xsd不再支持local该ref元素的属性,因为它不再提供超过常规bean引用的值。只需将现有ref local引用更改为ref bean升级到4.0模式时。

三、内部类

  • A <bean/>element inside the <property/>or <constructor-arg/>elements defines a so-called inner bean.
    一个beanA定义在属性和构造器标签里面,这个A叫做内部类。
<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>
  • 定义一个内部类A,不要求定义A的id和名字。如果你偏要给内部类定义Id和名字,容器并不会在内部类中使用这些属性。容器在创建内部类的时候,也会忽略你指定的Id和名字。
  • 内部类始终是匿名的,并且内部类是在创建外部类的时候才能创建的。是不可能将内部类直接注入到其他Bean中,也是不能够脱离外部Bean直接去访问内部类的。
  • 作为一个实例,它是可以从自定义范围内去接收销毁回调函数。
    例如:对于包含在单例bean中的请求范围的内部Bean:创建内部Bean实例将绑定到它的包含bean上,但是销毁回调函数将允许它参数到请求的生命周期中去。
    这不是一个常见的情况,内部类通常只是简单的分享它的bean的范围。

四、集合

4.1 集合说明

Xml中的<list/><set/><map/>,和<props/>元素,分别对应于Java的Collection类型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">[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>

4.2 集合合并

  • Spring容器也支持集合的合并。应用程序开发人员可以定义一个父风格<list/><map/><set/><props/>元素,并有孩子式的<list/><map/><set/><props/>元素继承和父集合覆盖值。也就是说,最终集合的值是合并父集合和子集合元素的结果。
<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定义的merge=true属性的元素上使用 adminEmails属性child。当childbean被容器解析并实例化时,生成的实例具有一个adminEmails Properties集合,该集合包含合并子集合 adminEmails与父adminEmails集合的结果。
administrator=administrator@example.com 
sales=sales@example.com 
support = support@example.co.uk
  • 这一合并行为同样适用于<list/><map/><set/> 集合类型。在<list/>元素的特定情况下,与List集合类型相关联的语义(即ordered 值集合的概念)被保留; 父项的值在所有子项列表的值之前。在的情况下Map,Set和Properties集合类型,没有顺序存在。因此,没有排序的语义在背后的关联的集合类型的效果Map,Set以及Properties该容器内部使用实现类型。
  • 集合合并的限制
    您不能合并不同的集合类型(如a Map和a List),并且如果您确实尝试这样做,Exception则会引发适当的集合类型。该merge属性必须在较低的继承的子定义上指定; merge在父集合定义上指定属性是多余的,并且不会导致所需的合并。

  • 合并强类型集合
    随着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;
    }
}

.xml文件配置

<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类型。

五、空值和字符串

5.1 空值

Spring将属性的空值视为空白的Strings。

  • 以下是基于xml文件配置元数据将emai属性设置为空String值(”“)
<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>
  • 等于下面的java代码
exampleBean.setEmail("");
  • <null/>元素来处理null值
<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

六、带有p命名空间的XML快捷方式(不推荐使用)

p-名称空间使您可以使用bean元素的属性(而不是嵌套 <property/>元素)来描述属性值和/或合作bean。
Spring支持带有命名空间的可扩展配置格式 ,这些命名空间基于XML模式定义。beans本章讨论的配置格式在XML Schema文档中定义。但是,p-namespace并未在XSD文件中定义,而只存在于Spring的核心中。

  • 以下示例显示了解析为相同结果的两个XML片段:第一个使用标准XML格式,第二个使用p命名空间。
  • 该示例在bean定义中显示了名为email的p名称空间中的一个属性。这告诉Spring包含一个属性声明。如前所述,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定义,这两个定义都可以引用另一个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"/>
        <!-- 定义一个bean名子是spouse,引用了名字为jane的Bean-->
        <property name="spouse" ref="jane"/>
    </bean>

    <!-- 这个bean名字是John Doe,也引用了jane的bean -->

    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

    <!-- 定义一个bean 名字为jane-- >

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>
  • 正如你所看到的,这个例子不仅包含使用p-命名空间的属性值,还使用特殊格式来声明属性引用。第一个bean定义用于<property name="spouse" ref="jane"/>创建bean spouse引用jane,而第二个bean定义p:spouse-ref="jane"用作属性来完成同样的事情。在这种情况下spouse是属性名称,而该-ref部分表明这不是一个正值,而是对另一个bean的引用。
  • p-名称空间不如标准XML格式那么灵活。例如,声明属性引用的格式与结尾的属性发生冲突Ref,而标准的XML格式则不会。我们建议您谨慎选择您的方法,并将其传达给您的团队成员,以避免生成同时使用所有三种方法的XML文档。

七、带有c-namespace的XML快捷方式

与带有p-命名空间的XML快捷方式类似,Spring 3.1中新引入的c-命名空间允许使用内联属性来配置构造函数参数,而不是嵌套constructor-arg元素。

7.1

  • 让我们回顾一下从例子中基于构造函数的依赖注入与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>

7.2

  • 该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允许),索引表示法也要求存在领先。
  • 实际上,构造器解析 机制在匹配参数方面非常有效,所以除非真的需要,否则我们建议在整个配置中使用名称符号。

八、复合属性名称

在设置bean属性时,只要最终属性名称以外的路径的所有组件都不是,就可以使用复合或嵌套属性名称null。考虑下面的bean定义。

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

上面foobean有一个fred的属性,这个fred属性又有bob属性,bob又有sammy属性,并且最终这个sammy被设置成了123。
为了支持这个的工作,foo中的fred和fred中的bob,在这个foobean构造完成后必须不能为null,否则会报NullPointerException(空指针异常)。

好啦,这个详细的依赖和配置解释,官方文档暂时只给了这么多。

猜你喜欢

转载自blog.csdn.net/wd2014610/article/details/80329232