Spring核心技术(三)——Spring的依赖及其注入(续)

本文将继续前文,针对依赖注入的细节进行描述

依赖注入细节

如前文所述,开发者可以通过定义Bean的依赖的来引用其他的Bean或者是一些值的,Spring基于XML的配置元数据通过支持一些子元素<property/>以及<constructor-arg/>来达到这一目的。

内在值类型(Java Primitives类型,字符串等)

元素<property/>value属性来对人友好易读的形式配置一个属性或者构造参数。Spring的便利之处就是用来将这些字符串的值转换成指定的类型。

<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更为的简洁,但是因为属性的类型是在运行时确定的,而非设计时确定的,所以除非使用IntelliJ IDEA或者Spring Tool Suite这些工具才能在定义Bean的时候自动完成属性配置。当然很推荐使用这些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的容器会将<value/>里面的文本通过使用JavaBean的PropertyEditor机制转换成一个java.util.Properties实例。这也是一个捷径,也是一些Spring团队更喜欢使用嵌套的<value/>元素而不是value属性风格。

idref元素

idref元素是一种简单的提前校验错误的方式,通过id来关联容器中的其他的Bean的方式。

<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一定存在。而第二个版本的话,是没有任何校验的。只有实际上引用了Bean client,在实例化client的时候才会发现。如果client是一个prototype的Bean,那么类似拼写之类的错误会在容器部署以后很久才能发现。

idref元素的local属性在4.0以后的xsd中已经不再支持了,而是使用了bean引用。如果更新了版本的话,需要将idref local引用都转换成 idref bean即可。

引用其他的Bean

ref元素在<constructor-arg/>或者<property/>中的一个终极标签。开发者可以通过这个标签配置一个Bean来引用另一个Bean。当需要引用一个Bean的时候,被引用的Bean会先实例化,然后配置属性,也就是引用的依赖。如果该Bean是单例Bean的话,那么该Bean会早由容器初始化。最终的引用另一个对象的所有引用。Bean的范围以及校验取决于开发者是否通过bean,local,parent这些属性来指定对象的id或者name属性。

通过指定Bean的bean属性中的<ref/>来指定依赖是最常见的一种方式,可以引用容器或者父容器中的Bean,无论是否在同一个XML文件定义都可以引用。其中bean属性中的值可以和其他引用Bean中的id属性一致,或者和其中的一个name属性一致的。

<ref bean="someBean"/>

通过指定Bean的parent属性会创建一个引用到当前容器的父容器之中。parent属性的值可以跟跟目标Bean的id属性一致,或者和目标Bean的name属性中的一个一致,目标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>

idref标签一样,ref元素中的local标签在xsd 4.0以后已经不再支持了,开发者可以通过将已存在的ref local改为ref bean来完成更新Spring。

内部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或者名字的。如果指定了,容器也不会用之作为分别Bean的区分标识。容器同时也会无视内部Bean的scope标签:内部Bean 总是 匿名的,而且 总是 随着外部的Bean同时创建的。开发者是无法将内部的Bean注入到外部Bean以外的其他Bean的。

当然,也有可能从指定范围接收到破坏性回调。比如:一个请求范围的内部Bean包含了一个单例的Bean,那么内部Bean实例会绑定到包含的Bean,而包含的Bean允许访问到requestscope生命周期。这种场景不常见,内部Bean通常只是共享它的外部Bean。

集合

<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">[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>

当然,map的key或者value,或者是集合的value都可以配置为下列之中的一些元素:

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

集合合并

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>

可以发现,我们在childBean上使用了merge=true属性。当childBean由容器初始化且实例化的时候,其实例中包含的adminEmails集合就是childadminEmails以及parentadminEmails集合。如下:

[email protected].com
[email protected].com
[email protected].co.uk

childProperties集合的值继承了parent<props/>child的值也支持重写parent的值。

这个合并的行为和<list/>,<map/>以及<set/>之类的集合类型的行为是类似的。<list/>的特定的例子中,与List集合类型类似,有隐含的ordered概念的。所有的父元素里面的值,是在所有孩子元素的值之前的。但是像Map,Set或者Properties的集合类型,是不存在顺序的。

集合合并的限制

开发者是不能够合并不同类型的集合的(比如MapList合并),如果开发者这么做,会抛出异常。merge的属性是必须特指到更低级或者继承者,子节点的定义上。特指merge属性到父集合的定义上是冗余的,而且在合并上也没有任何效果。

强类型集合

在Java 5以后,开发者可以使用强类型的集合了。也就是,开发者可以声明一个Collection类型,然后这个集合只包含String元素(举例来说)。如果开发者通过Spring来注入强类型的Collection到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>

foo的属性accounts准备注入的时候,accounts的泛型信息Map<String, Float>就会通过反射拿到。这样,Spring 的类型转换系统能够识别不同的类型,如上面的例子Float然后将字符串的值9.99, 2.75以及3.99转换成对应的Float类型。

Null以及空字符串

Spring将会将属性的空参数,直接当成空字符串来处理。下面的基于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)

XML 快捷方式p命名空间

p命名空间令开发者可以使用bean的属性,而不用使用嵌套的<property/>元素,就能描述开发者想要注入的依赖。

Spring是支持基于XML的格式化的命名空间扩展的。本节讨论的beans的配置都是基于XML的,p命名空间并不是定义在XSD文件,而是定义在Spring Core之中的。

下面展示了两种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中展示了email属性的定义。这种定义告知Spring这是一个属性的声明。如前面所描述,p命名空间并没有标准模式定义,所以你可以配置属性的名字为依赖名字。

下面的例子包括了2个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>

从上述的例子中可以看出,john-modern不止包含一个属性,也同时使用了特殊的格式来声明一个引用指向另一个Bean。第一个Bean定义使用的是<property name="spouse" ref="jane"/>来创建的Bean引用到另外一个Bean,而第二个Bean的定义使用了p:spouse-ref="jane"来作为一个指向Bean的引用。在这个例子中spouse是属性的名字,而-ref部分表名这个依赖不是直接的类型,而是引用另一个Bean。

p命名空间并不同标准的XML格式一样灵活。比如,声明属性的引用可能和一些以Ref结尾的属性相冲突,而标准的XML格式就不会。Spring团队推荐开发者能够和团队商量一下,要使用哪一种方式,而不要同时使用3种方法。

XML 快捷方式c命名空间

与p命名空间类似,c命名空间是在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来配置引用)。而且,同样的,c命名空间也不是定义在XSD的模式之中(但是在Spring Core之中)。

在少数的例子之中,构造函数的参数名字并不可用(通常,如果字节码没有debug信息的编译),开发者可以使用下面的例子:

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

根据XML语法,索引的概念的存在要求使用_作为XML属性名字不能以数字开始。

实际上,构造函数的解析机制在匹配参数是很高效的,除非必要,Spring团队推荐在配置中使用命名空间。

混合的属性

开发者可以在配置属性的时候配置混合的属性,只要所有的组件路径(除了最后一个属性名字)不能为null

参考如下的定义。

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

foo有一个fred的属性,而其中fred属性有一个bob属性,而bob属性之中有一个sammy属性,那么最后这个sammy属性会配置为123。想要上述的配置能够生效,fred属性需要有一个bob属性且在fred构造只是构造之后不为null,否则会抛出NullPointerException

时间: 2024-10-17 19:42:54

Spring核心技术(三)——Spring的依赖及其注入(续)的相关文章

spring学习(三) ———— spring事务操作

前面一篇博文讲解了什么是AOP.学会了写AOP的实现,但是并没有实际运用起来,这一篇博文就算是对AOP技术应用的进阶把,重点是事务的处理. --wh 一.jdbcTemplate 什么是JdbcTemplate? spring提供用于操作数据库模版,类似Dbutils,通俗点讲,我们操作数据库,spring也会帮我们提供一个操作数据库的工具供我们使用,而不用我们自己手动编写连接数据库,获取结果集等等操作,这个工具就是JdbcTemplate.跟Dbutils一样,想要使用JdbcTemplate

玩转Spring MVC(三)----spring基本配置文件

这篇文章总结一下spring mvc的基本配置,首先贴一张我的项目的目录截图,有一些多余的文件,大家不必在意: 用到的一些jar包在这:<a>http://download.csdn.net/detail/u012116457/8426199</a> 接下来是web.xml的配置: <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0"

Spring总结三:DI(依赖注入)

简介: 所谓的依赖注入,其实是当一个bean实例引用到了另外一个bean实例时spring容器帮助我们创建依赖bean实例并注入(传递)到另一个bean中,比如你使用Spring容器创建的对象A里面需要引用对象B或者集合之类的,Spring容器会在创建A的实现类和B的实现类后,把B的实现类注入A实例中(通过反射),而且这些其他的对象怎么创建什么时候创建A实不需要知道的,这样就完成了各个对象之间的关系控制. 依赖注入的三种方式: 1,构造方法参数注入 2,属性setter方法注入 3,接口注入 S

Spring 3.0 学习-DI 依赖注入_创建Spring 配置-使用一个或多个XML 文件作为配置文件,使用自动注入(byName),在代码中使用注解代替自动注入,使用自动扫描代替xml中bea

文章大纲 在xml中声明bean和注入bean 在xml中声明bean和自动注入bean 自动扫描bean和自动注入bean 对自动扫描bean增加约束条件 首次接触spring请参考 Spring 3.0 学习-环境搭建和三种形式访问 1.典型的Spring XML 配置文件表头 <?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 --> <beans xmlns=

Spring 的核心机制:依赖注入(控制反转)

一.说到依赖注入(控制反转),先要理解什么是依赖. Spring 把相互协作的关系称为依赖关系.假如 A 组件调用了 B 组件的方法,我们可称A 组件依赖于 B 组件. 二.什么是依赖注入. 在传统的程序设计过程中,通常由调用者来创建被调用者的实例. 在依赖注入的模式下,创建被调用者的工作不再由调用者来完成,因此称为控制反转:创建被调用者实例的工作通常由Spring 容器来完成,然后注入给调用者,因此也称为依赖注入. 三.依赖注入的好处. 依赖注入让 Spring 的 Bean 以被指文件组织在

Spring.Net控制翻转、依赖注入、面向切面编程

Spring.Net快速入门:控制翻转.依赖注入.面向切面编程 Spring.Net主要功能: 1.IoC:控制翻转(Inversion of Control)  理解成抽象工厂翻转控制:就是创建对象的权利由开发人员自己控制New,转到了由容器来控制. 2.DI:依赖注入(Dependency Injection)依赖注入:就是在通过容器开创建对象的时候,在对象的初始化是可以给一些属性.构造方法的参数等注入默认值(可以是复杂的类型). 3.AOP:面向切面编程  (类似:管道.MVC过滤器等)

学习spring容器IOC(3)之依赖和依赖注入

学习开涛跟我学spring3 http://jinnianshilongnian.iteye.com/ 为什么要应用依赖注入,应用依赖注入能给我们带来哪些好处呢? 1.动态替换Bean依赖对象,程序更灵活:替换Bean依赖对象,无需修改源文件:应用依赖注入后,由于可以采用配置 文件方式实现,从而能随时动态的替换Bean的依赖对象,无需修改java源文件: 2.更好实践面向接口编程,代码更清晰:在Bean中只需指定依赖对象的接口,接口定义依赖对象完成的功能,通过容 器注入依赖实现: 3.更好实践优

spring ioc三种注入方式

spring ioc三种注入方式 IOC ,全称 (Inverse Of Control) ,中文意思为:控制反转 什么是控制反转? 控制反转是一种将组件依赖关系的创建和管理置于程序外部的技术. 由容器控制程序之间的关系,而不是由代码直接控制 由于控制权由代码转向了容器,所以称为反转 接口注入 Setter 注入 构造器注入 三种依赖注入方式的比较 : 接口注入: 接口注入模式因为历史较为悠久,在很多容器中都已经得到应用.但由于其在灵活性.易用性上不如其他两种注入模式,因而在 IOC 的专题世界

Spring-----3、Spring的核心机制:依赖注入

纵观所有Java应用(从基于Applet的小应用到多层结构的企业级应用),他们都是一种典型的依赖型应用,也就是由一些互相协作的对象构成的.Spring把这种互相协作的关系称为依赖关系.如A组件调用B组件的方法,可称A组件依赖于B组件依赖注入让Spring的Bean以配置文件组织在一起,而不是以硬编码的方式耦合在一起 一.理解依赖注入 依赖注入(Dependency Injection) = 控制反转(Inversion ofControl,IoC):当某个Java实例(调用者)需另一个Java实

(转)Spring读书笔记-----Spring核心机制:依赖注入

Java应用(从applets的小范围到全套n层服务端企业应用)是一种典型的依赖型应用,它就是由一些互相适当地协作的对象构成的.因此,我们说这些对象间存在依赖关系.加入A组件调用了B组件的方法,我们就可以称A组件依赖于B组件.我们通过使用依赖注入,Java EE应用中的各种组件不需要以硬编码方式耦合在一起,甚至无需使用工厂模式.当某个Java 实例需要其他Java 实例时,系统自动提供所需要的实例,无需程序显示获取,这种自动提供java实例我们谓之为依赖注入,也可以称之为控制反转(Inversi