Spring学习手册(4)—— Spring 依赖注入

上文Spring学习手册(3)—— bean实例化配置主要使用xml配置方式介绍bean配置,并介绍了四种bean实例化方式:默认构造器、带参数的构造器、静态工厂方法、实例化的工厂方法。上文的代码演示中我们也注意到了依赖类的一些注入。本文将主要了解学习Spring的依赖注入。
源代码下载地址

一、Spring依赖注入

企业级应用都不是由单个对象构成的,甚至一个简单的应用也是由多个对象相互协作完成工作的。Spring使用依赖注入(DI)的技术使得一个对象所依赖(协作)的对象只能由构造器参数、工厂方法参数或set方法设置属性的方式关联。Spring支持两种依赖注入的方式:构造器注入、set方法注入。其中工厂方法使用与构造器注入相似的方式注入依赖。

  • 构造器参数注入
  • set方法注入

二、构造器参数注入

当我们定义bean时,若需向构造器注入参数需使用<constructor-arg>标签将依赖参数传递给构造器。该标签有以下属性可以进行设置:

属性 说明
ref 指向对象的引用
value 值类型,一般为java基本类型
type 表明注入对象的类型
index 参数列表序列,从0开始
name 使用形参名标示(3.0版本以后支持)

为了更好的解释这些属性的用法,我们构建一个简单的例子来更好的理解构造器注入。

package example;
//构造器注入例子类
public class ConstructorDIExample {

    private String name;
    private int age;
    private Address address;

    public ConstructorDIExample(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public String toString(){

        return name+"  "+age+"  "+address.toString();
    }
}
package example;
//辅助地址类,无具体意义。为了演示对象引用注入
public class Address{

   public String toString(){
        return "I'm the address class";
    }
}

配置文件如下:

<bean id="address" class="example.Address"/>

<bean id="constructorDIExample" class="example.ConstructorDIExample">
     <constructor-arg type="String" value="Json"/>
     <constructor-arg index="1" value="20"/>
     <constructor-arg name="address" ref="address">
</bean>

我们可以看到ConstructorDIExample除构造方法外,无别的方式设置内部属性,因此在定义bean时,我们将<constructor-arg>标签包含在<bean>标签内设置构造期参数信息。

根据传入参数值类型的不同需要使用不同的属性:

  • 当传入值为另一个定义的bean时,使用ref属性,值为该bean的id名或name值;
  • 当传入值为基本数据类型或String时,使用value属性

为了指明每个参数值所对应的形参,提供有typeindexname三个属性,简单说明如下:

  • type用来指明参数具体类型,主要用于设置基本数据类型或String类型参数时,如value="true"设置,需要type来告诉Spring将该值转换成String还是boolean类型;
  • index 按照形参顺序设置值信息,以0开始计算。Spring将根据具体形参类型将值转换成制定类型;
  • name 使用形参名设置参数值信息。
  • 当无参数设置无奇异时,可省略type或index或name的使用。如构造器只包含Food和Time两个类型参数,而Food和Time为非基本数据类型,此时可直接食用ref设置。

静态工厂方法和实例工厂方法这两种实例化方法的参数注入方式与构造器实例化方式的注入方式相同。

三、Setter方法参数注入

当我们完成bean定义,需要通过setter方法设置对象内部属性,此时我们使用<property>标签告诉Spring如何设置内部属性。该标签有以下属性可以进行设置:

属性 说明
ref 指向对象的引用
value 值类型,一般为java基本类型
name 需要设置的属性名

以上属性的用法与构造器上的属性设置方式相似,这里我们也简单构建一个例子来帮助我们理解下Setter注入方式。

package example;

public class SetterDIExample {

    private int age;
    private String  name;
    private Address address;

    public SetterDIExample() {

    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString(){

        return name+"  "+age+"  "+address.toString();
    }
}
<bean id="address" class="com.liangwei.learnspring.Address"/>

<bean id="setterExample" class="com.liangwei.learnspring.SetterDIExample">
     <property name="age" value="20"/>
     <property name="name" value="Json"/>
     <property name="address" ref="address"/>
</bean>

Setter注入方式中的namevalueref属性的用法和构造器注入方式中的使用和意义相同,这里就不再重复说明。

⚠️:name的值第一个字母大写加上“set”前缀必须和类提供的seter方法一致。例如:前面例子设置age属性转变后为:setAge,则SetterDIExample类里面必须有setAge方法。

四、配置信息详解

使用<constructor-arg/><property/>元素标签可以为类设置协同类对象或某具体类型值。

内置类型注入

Java内置类型包括intlongbooleanString等,该小节我们以Setter方法属性注入的方式展示内置类型的用法

测试代码如下:

public class BuildInTypeExample {

    /* 年龄*/
    private int age;

    /* 体重 */
    private float weight;

    /* 姓名 */
    private String name;

    /* 是否已婚 */
    private boolean married;

    public void setAge(int age) {
        this.age = age;
    }

    public void setWeight(float weight) {
        this.weight = weight;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setMarried(boolean married) {
        this.married = married;
    }

    public String toString(){
        return "age: "+ age +" weight: "+weight+" name: "+name+" isMarride: "+married;
    }
}

配置信息如下:

<bean id="buildInTypeExample" class="com.liangwei.learnspring.BuildInTypeExample">
        <property name="age" value="20"/>
        <property name="weight" value="50.23"/>
        <property name="name" value="snow"/>
        <property name="married" value="false"/>
</bean>

内置类型注入使用value属性,Spring会将String类型的value转换成相应的类型。

引用其他bean

该配置模式可参考Setter方法参数注入的配置方式

内部bean

  • 内部bean不需要id或name定义,即使定义了IOC容器也会忽略;
  • IOC容器也会忽略scope标记(该属性会在后面讲解);

Person类定义

public class Person {

    /* 年龄 */
    private  int age;

    /* 姓名 */
    private String name;

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String  toString(){

        return "age: "+ age+"  name: "+ name;
    }
}

InnerClassExample类定义:

public class InnerClassExample {

    private Person person;

    public void setPerson(Person person) {
        this.person = person;
    }

    public String toString(){
        return person.toString();
    }
}

配置信息:

 <!-- 内部类注入 -->
<bean id="innerClassExample" class="com.liangwei.learnspring.InnerClassExample">
    <property name="person">
        <bean class="com.liangwei.learnspring.Person">
            <property name="age" value="21"/>
            <property name="name" value="Json"/>
        </bean>
    </property>
</bean>

容器

使用<list/><set/><map/><props/>可以提供Java容器类型ListSetMapProperties

1. 强类型容器

容器类注入方式语法方式相对简单,可参考代码示例进行学习。

public class CollectionDIExample {

    private List<String>  names;

    private Map<String,Integer>  scores;

    private Set<String> className;

    private Properties properties;


    public void setNames(List<String> names) {
        this.names = names;
    }

    public void setScores(Map<String, Integer> scores) {
        this.scores = scores;
    }

    public void setClassName(Set<String> className) {
        this.className = className;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public List<String> getNames() {
        return names;
    }

    public Map<String, Integer> getScores() {
        return scores;
    }

    public Set<String> getClassName() {
        return className;
    }

    public Properties getProperties() {
        return properties;
    }
}

配置文件:

 <!-- 容器类注入 -->
    <bean id="collectionDIExample" class="com.liangwei.learnspring.CollectionDIExample">
        <property name="names">
            <list>
                <value>Json</value>
                <value>Snow</value>
                <value>Joe</value>
            </list>
        </property>
        <property name="scores">
            <map>
                <entry key="Json" value="87"/>
                <entry key="Snow" value="90"/>
                <entry key="Joe" value="89"/>
            </map>
        </property>
        <property name="className">
            <set>
                <value>Class One</value>
                <value>Class Two</value>
                <value>Class Three</value>
            </set>
        </property>
        <property name="properties">
            <props>
                <prop key="baidu">www.baidu.com</prop>
                <prop key="google">www.google.com</prop>
                <prop key="bing">cn.bing.com</prop>
            </props>
        </property>
    </bean>
2.容器合并

为演示容器合并注入方式,我们以Properties类进行举例说明,其他如Map、List等以此类推。

public class CollectionMergeExample {
    private Properties properties;

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public Properties getProperties() {
        return properties;
    }
}

配置数据:

<!-- 容器合并注入 -->
    <bean id="parentCollection" class="com.liangwei.learnspring.CollectionMergeExample">
        <property name="properties">
            <props>
                <prop key="baidu">www.baidu.com</prop>
                <prop key="google">www.google.com</prop>
            </props>
        </property>
    </bean>
    <bean id="childCollection" parent="parentCollection">
        <property name="properties">
            <props merge="true">
                <prop key="google">g.cn</prop>
                <prop key="bing">cn.bing.com</prop>
            </props>
        </property>
    </bean>

运行以下代码你将看到如下输出:

{google=www.google.com, baidu=www.baidu.com}
{google=g.cn, baidu=www.baidu.com, bing=cn.bing.com}

当使用容器合并注入时,我们注意到以下几点:

  • 定义一个bean,其parent属性指向需要合并的bean的名字;
  • 需要合并的容器设置merge属性为true;

当我们如上设置时,Spring的IOC容器将会合并Java容器的数据设置,如例子中所体现的那样google的值被覆盖,并在原有的基础上增加了bing的数据。若merge属性不尽兴设置或设置为false,则不会进行数据合并,此时childCollection的数据输出将不包含baidu的数据键值对。有兴趣的可以更改代码试试。

Null注入

当我们想为一个类型赋值为null时,我们需要使用<null/>标签。而使用“”往往是将空字符串为该参数赋值,如下代码演示所示:

public class NullExample {

    private String nullString;

    private String emptyString;

    public String getNullString() {
        return nullString;
    }

    public void setNullString(String nullString) {
        this.nullString = nullString;
    }

    public String getEmptyString() {
        return emptyString;
    }

    public void setEmptyString(String emptyString) {
        this.emptyString = emptyString;
    }
}

配置文件:

    <bean id="nullExample" class="com.liangwei.learnspring.NullExample">
        <property name="nullString">
            <null/>
        </property>
        <property name="emptyString" value=""/>
    </bean>

五、总结

本文详细介绍了IOC注入的配置方式:首先先概括性介绍IOC容器的两种主要注入方式:构造器注入和Setter方法注入,然后就各种常见数据类型和容器注入配置方式进行了解释和代码演示。至此我们通过XML文件配置方式可以实现bean定义、依赖对象注入和基本数据注入等工作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容