Srping - bean的依赖注入(Dependency injection)

目录



1 概述

2 两种基本的依赖注入方式

2.1 构造函数方式

2.2Setter方式

3 其他依赖注入功能

3.1 <ref/>标签引用不同范围的bean

3.2 内部bean

3.3 集合注入

3.4 集合合并

3.5 强类型集合注入

3.6 null和空字符串

3.7 p-namespace方式配置属性注入

3.8 c-namespace方式配置构造函数参数注入

3.9 嵌套属性注入

1 概述



这篇文章主要就是讲解Spring的bean之间依赖注入的方法,本文不讲原理,只涉及用法。

在实际项目中,我们常常需要在一个bean中引用另外一个bean,例如业务逻辑处理(service)层调用数据处理(dao)层。那么就有了依赖注入(Dependency Injection),简称DI。

2 两种基本的依赖注入方式



Spring提供了2种基本方式让我们实现DI,分别是通过构造函数和setter方法。下面我们来一一介绍。

2.1 构造函数方式

实现方式如下:

java代码:

package x.y;
public class Foo {
    public Foo(Bar bar, Baz baz) {
        // ...
    }
}

bean配置:

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

Spring会检查bean之间的依赖关系,会先加载(初始化)被依赖的bean,在例子中,bar和baz首先被加载,然后通过构造函数参数的形式注入到foo中。相信这个很好理解吧。

除了支持bean的注入之外,Spring还支持常量的注入,如下:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

其中constructor-arg需要根据构造函数的顺序对应好(参数类型要匹配)。

除了默认的顺序排序,我们还可以指定Index属性,来指定constructor-arg对应构造函数参数的位置:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

另外还可以指定参数名称来对应指定构造函数参数,但是这种方式需要给类的构造函数加上 @ConstructorProperties注解:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateanswer" value="42"/>
</bean>
package examples;
public class ExampleBean {
    // Fields omitted
    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

2.2 Setter方式

实现方式如下:

java代码:

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;
    }
}

bean配置:

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

Spring在实例化bean之后,会将配置中property属性通过对应的setter方法将被引用对象设置到bean的属性中。同样的,被引用的bean会先于引用bean被Spring加载实例化。

属性的setter方法需要遵循java的标准,即set + 属性名(首字母改为大写)。

在上面例子中

<property name="beanTwo" ref="yetAnotherBean"/>

还可以写成:

<property name="beanTwo"/>

<idref bean="yetAnotherBean"/>

</property>

或者:

<property name="beanTwo" value="yetAnotherBean"/>

通过<idref/>标签,Spring会去查找容器中ID为指定值的bean。

3 其他依赖注入功能



3.1 <ref/>标签引用不同范围的bean

之前我们所看到的例子都是bean引用了同一个Spring容器中的bean,Spring还支持bean引用同个XML配置文件中的bean,或者是父容器的bean。写法分别是:

引用当前容器中的bean:

<ref bean="someBean"/>

引用父容器中的bean:

<ref parent="someBean"/>

引用当前XML中的bean:

<ref local="someBean"/>

3.2 内部bean

内部bean指的是在某个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属性。

3.3 集合注入

除了注入基础类型和bean之外,我们还可以对bean中的集合属性进行注入,List,Set,Map,Properties分别对应配置中的<list/>,<set/>,<map/>,<props/>标签,配置方式如下:

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

3.4 集合合并

假设我们有一个bean collectionParent,collectionParent里有个Properties类型的属性config:

<bean id="collectionParent" class="cn.com.willchen.test.di.CollectionMergeInject">
    <property name="config">
        <props>
            <prop key="url">localhost</prop>
            <prop key="port">8080</prop>
        </props>
    </property>
</bean>

这时,因为业务需要,我们可能有多种场景,需要用到不同的config,或者内容更多的config,假设port属性改了,而且需要多一个protocol属性,那么我们可以这么做:

<bean id="collectionChild" parent="collectionParent">
    <property name="config">
        <!-- the merge is specified on the *child* collection definition -->
        <props merge="true">
            <prop key="protocol">http</prop>
            <prop key="port">9090</prop>
        </props>
    </property>
</bean>

collectionChild的配置通过parent属性继承collectionParent,再在config的props标签中加入merge="true"的属性,就可以实现将collectionParent中config的配置合并过来。

实例化后collectionParent的config属性是{port=8080, url=localhost}
collectionChild的config属性是{port=9090, url=localhost, protocol=http}

注意,重复的元素会被覆盖。

这里用的是Properties作为例子,同样的List,Map,Set也可以,但是对于List略有不同,List是以链表形式出现的,是有序的,2个List合并成1个List后,parent List的元素肯定在child List元素之前。

不同类型的集合之间不能合并,否则Spring会抛出异常。

3.5 强类型集合注入

假设我们的bean 类中属性定义的是一个强类型的集合如Map<Sring, Fload>,Spring同样支持注入:

public class Foo {
    private Map<String, Float> accounts;
    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<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>

例中的9.99,2.75,3.99在注入属性前Spring会自动做类型转换,例子中转为Float类型。

3.6 null和空字符串

这个比较简单,没什么好说的,看例子:

空字符串:

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

null:

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

3.7 p-namespace方式配置属性注入

p-namespace方式是用于替换<property/>标签的另外一种简洁的写法,通常我们给一个bean注入属性是这么写的:

<bean name="classic" class="com.example.ExampleBean">
    <property name="email" value="[email protected]"/>
</bean>

在Spring 2.0之后,我们可以这么写:

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

如果我们的bean的属性引用了其他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>

p:spouse-ref中spouse是属性的名称,-ref表示引用了其他bean。

但是在实际项目中,我们还是需要保证所有配置使用一种风格,要么<property/>要么p-namespace格式。

3.8 c-namespace方式配置构造函数参数注入

c-namespace方式是用于替换<constructor-arg/>标签的另外一种写法,通常定义一个bean的构造函数的参数是这么写的:

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

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

通过c-namespace我们可以这么写:

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

写法和p-namespace类似,也就把p换成c而已。

3.9 嵌套属性注入

假设bean中有个属性A,属性A里有个属性B,那么我们也可以通过配置给A.B赋值:

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

需要注意的是,父属性不能为空,如例子中 fred不能为null,fred.bob也不能为null,否则在注入的时候会抛出空指针异常。

时间: 2024-08-28 07:56:35

Srping - bean的依赖注入(Dependency injection)的相关文章

控制反转(Inversion of Control)与依赖注入(Dependency Injection)

——摘自Rocky Ren的笔记 1.控制反转(Inversion of Control)与依赖注入(Dependency Injection) 控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理.所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器. IoC是一个很大的概念,可以用不同的方式来实现.其主要实现方式有两种:<1>依赖查找(Dependency Look

Spring点滴七:Spring中依赖注入(Dependency Injection:DI)

Spring机制中主要有两种依赖注入:Constructor-based Dependency Injection(基于构造方法依赖注入) 和 Setter-based Dependency Injection(基于Setter方法依赖注入) 一.Contructor-based Dependency Injection(基于构造方法注入) 在bean标签中通过使用<constructor-arg />标签来实现 spring.xml <?xml version="1.0&qu

理解依赖注入(Dependency Injection)

理解依赖注入 Yii2.0 使用了依赖注入的思想.正是使用这种模式,使得Yii2异常灵活和强大.千万不要以为这是很玄乎的东西,看完下面的两个例子就懂了. class SessionStorage { function __construct($cookieName = 'PHP_SESS_ID') { session_name($cookieName); session_start(); } function set($key, $value) { $_SESSION[$key] = $valu

Spring之对象依赖关系(依赖注入Dependency Injection)

承接上篇: Spring中,如何给对象的属性赋值: 1:通过构造函数,如下所示: <!-- 1:构造函数赋初始值 --><bean id="user1" class="com.bie.po.User"><constructor-arg value="10010" type="int"></constructor-arg>      <constructor-arg valu

采用Spring管理Bean和依赖注入

1.实例化spring容器 和 从容器获取Bean目标 实例化Spring容器常用的两种办法: 办法一: 在类途径下寻觅配置文件来实例化容器 [引荐运用] ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"}); 办法二: 在文件体系途径下寻觅配置文件来实例化容器 [这种办法能够在开发期间运用] ApplicationContext ctx = new FileSyst

Java Spring学习笔记----Bean的依赖注入(1)

Spring常用的两种依赖注入方式:一种是设值注入方式,利用Bean的setter方法设置Bean的属性值:另一种是构造注入,通过给Bean的构造方法传递参数来实现Bean的属性赋值: 1.设值注入方式 直接上代码例子,示例的树结构图如下 Shape.java接口内容 package chapter3; public interface Shape { public double area();//求形状的面积 } Circle.java内容: package chapter3; public

Spring学习3—控制反转(IOC)Spring依赖注入(DI)和控制反转(IOC)

一.思想理解 Spring 能有效地组织J2EE应用各层的对象.不管是控制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的 管理下有机地协调.运行.Spring将各层的对象以松耦合的方式组织在一起,Action对象无须关心Service对象的具体实现,Service对 象无须关心持久层对象的具体实现,各层对象的调用完全面向接口.当系统需要重构时,代码的改写量将大大减少. 上面所说的一切都得宜于Spring的核心机制,依赖注入.依赖注入让bean与

【SSH三大框架】Spring基础第二篇:Spring依赖注入的三种方式

控制反转(Inversion of Control)和依赖注入(Dependency Injection): 应用控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它.也可以说,依赖被注入到对象中.所以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转. 对于依赖注入,有三种方式: 1.使用属性的setter方法注入 2.使用构造器注入 3.使用注解注入 下面我们介绍下这三种方式: 一.使用属性的setter方法注入 首先,我们写一个

Spring控制反转与依赖注入(IOC、DI)

IOC: 反转控制   Inverse Of Control DI:依赖注入 Dependency Injection 目的:完成程序的解耦合 解释:在应用系统的开发过程中,有spring负责对象的创建,对象依赖关系的组装,对象属性的初始化,程序员只需要在程序接收spring创建的对象即可. Object obj= new Object(); IOC :  Object obj;  等着接收spring容器创建好的对象,在程序中将对象的创建的权限交出,反转到spring容器中. DI:  某个对