Spring丨DI

前言

依赖注入(Dependency Injection,DI)和控制反转含义相同,它们是从两个角度描述的同一个概念。
依赖:指的是 Bean 对象的创建依赖于容器,Bean 对象的依赖资源;注入:指的是 Bean 对象依赖的资源由容器来设置以及装配。

Spring 容器在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,这样,调用者通过 Spring 容器获得被调用者实例,这称为依赖注入
依赖注入主要有两种实现方式,分别是属性 setter 注入和构造方法注入。

属性 setter 注入

指 IoC 容器使用 setter 方法注入被依赖的实例。
通过调用无参构造器或无参 static 工厂方法实例化 bean 后,调用该 bean 的 setter 方法,即可实现基于 setter 的 DI。

常量注入

以下演示注入常量。

package com.study.bean;

/**
 * Spring 第一个程序
 */
public class Hello {
    private String name;

    // 无参构造方法:验证 Hello 是否被创建了
    public Hello() {
        System.out.println("Hello 被创建了");
    }

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

    public void show() {
        System.out.println("Hello, " + name);
    }
}

在 XML 配置文件中配置:

<bean name="hello" class="com.study.bean.Hello">
    <!-- 这里的 name是 setName()方法中 set后面的 Name1,首字母要小写  -->
    <property name="name1" value="张三"/>
</bean>

Bean 注入

以下演示 Bean 注入。新建一个地址类 Address,其中私有属性为字符串类型的地址 addr。

package com.study.bean;

/**
 * 演示 bean注入
 */
public class Address {
    private String addr;

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
}

新建一个学生类 Student,其中私有属性包含字符串类型的姓名 name、地址类的地址 addr。

package com.study.bean;

/**
 * 演示 bean注入
 */
public class Student {
    private String name;
    private Address addr;

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

    public void setAddr(Address addr) {
        this.addr = addr;
    }

    public void show() {
        System.out.println("大家好,我是" + name + ",我住在" + addr.getAddr());
    }
}

对应的 XML 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 地址类实例 -->
    <bean id="address" class="com.study.bean.Address">
        <property name="addr" value="广州市"/>
    </bean>

    <bean id="stu" class="com.study.bean.Student">
        <!-- 常量注入 -->
        <property name="name" value="张三"/>
        <!-- bean 注入,其中的 ref 对应 bean的id -->
        <property name="addr" ref="address"/>
    </bean>
</beans>

数组注入

学生类中新增一个私有属性:字符串数组 books,并相对应的增加一个 setBooks()方法。

package com.study.bean;

import java.util.Arrays;

/**
 * 演示 setter注入
 */
public class Student {
    // 常量注入
    private String name;
    // bean 注入
    private Address addr;
    // 数组注入
    private String[] books;

    public void setBooks(String[] books) {
        this.books = books;
    }

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

    public void setAddr(Address addr) {
        this.addr = addr;
    }

    public void show() {
        System.out.println("大家好,我是" + name + ",我住在" + addr.getAddr()
                + ",书本有" + Arrays.toString(books));
    }
}

对应的 XML 配置文件:

<bean id="stu" class="com.study.bean.Student">
    <!-- 常量注入 -->
    <property name="name" value="张三"/>
    <!-- bean 注入,其中的 ref 对应 bean的id -->
    <property name="addr" ref="address"/>
    <!-- 数组注入 -->
    <property name="books">
        <array>
            <!-- 数组元素 -->
            <value>《老人与海》</value>
            <value>《中华上下五千年》</value>
            <value>《活着》</value>
            <value>《傲慢与偏见》</value>
        </array>
    </property>
</bean>

List 注入

在学生类中新增一个 List 类型的爱好列表 hobbies,并增加对应的 setter 方法。

// list注入
private List<String> hobbies;

public void setHobbies(List<String> hobbies) {
    this.hobbies = hobbies;
}

XML 配置文件新增:

<!-- List注入 -->
<property name="hobbies">
    <list>
        <value>羽毛球</value>
        <value>音乐</value>
        <value>美食</value>
        <value>旅游</value>
    </list>
</property>

Map 注入

在学生类中新增一个 Map<String, Integer> 类型的 cards,并增加对应的 setter 方法。

// map注入
private Map<String, Integer> cards;

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

XML 配置文件新增:

<!-- Map注入 -->
<property name="cards">
    <map>
        <entry key="中国建设银行存储卡" value="123456"/>
        <entry key="图书馆借书会员卡" value="2021066"/>
    </map>
</property>

Set 注入

在学生类中新增一个 Set 类型的游戏 games,并增加对应的 setter 方法。

// set注入
private Set<String> games;

public void setGames(Set<String> games) {
    this.games = games;
}

XML 配置文件新增:

<!-- Set注入 -->
<property name="games">
    <set>
        <value>英雄联盟</value>
        <value>王者荣耀</value>
        <value>绝地求生</value>
        <value>守望先锋</value>
    </set>
</property>

Null 注入

在学生类中新增一个字符串类型的女友名字 girlFriendName,并增加对应的 setter 方法。

// null注入
private String girlFriendName;

public void setGirlFriendName(String girlFriendName) {
    this.girlFriendName = girlFriendName;
}

XML 配置文件新增:

<!-- Null注入 -->
<property name="girlFriendName">
    <null/>
</property>

properties 注入

在学生类中新增一个 Properties 类型的信息 info,并增加对应的 setter 方法。

// properties注入
private Properties info;

public void setInfo(Properties info) {
    this.info = info;
}

XML 配置文件新增:

<!-- Properties 注入 -->
<property name="info">
    <props>
        <prop key="学号">201821314404</prop>
        <prop key="性别">男</prop>
        <prop key="年龄">20</prop>
        <prop key="年薪">25W</prop>
    </props>
</property>

P 命名空间注入

为了简化 XML 文件的配置,越来越多的 XML 文件采用属性而非子元素配置信息。
Spring 从 2.5 版本开始引入了一个新的 p 命名空间,可以通过 元素属性的方式配置 Bean 的属性。
使用 p 命名空间之前,需要先声明使用对应的命名空间:在 XML 文件中的根标签 中新增一句:

xmlns:p="http://www.springframework.org/schema/p"

新建一个英雄类 Hero,其中私有属性包含姓名以及年龄。

package com.study.bean;

/**
 * 演示 p命名空间
 */
public class Hero {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Hero{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

使用 p 命名空间后,基于 XML 的配置方式将更进一步简化。但它也是属于 setter 注入的,需要属性含有对应的 set 方法。
XML 文件配置:

<!-- P命名空间,更加简化。P 就是 property 的意思 -->
<bean id="myHero" class="com.study.bean.Hero" p:name="无极大师" p:age="18"/>

构造方法注入

指 IoC 容器使用构造方法注入被依赖的实例。
基于构造器的 DI 通过调用带参数的构造方法实现,每个参数代表一个依赖。

package com.study.bean;

/**
 * Spring 第一个程序
 */
public class Hello {
    private String name;

    // 有参构造
    public Hello(String name) {
        super();
        this.name = name;
    }

    public void show() {
        System.out.println("Hello, " + name);
    }
}

在 XML 配置文件中配置:

<!-- 有参构造方法创建对象 -->
<bean id="hello1" class="com.study.bean.Hello">
    <!-- 参数列表中的第一个参数,下标从 0 开始 -->
    <constructor-arg index="0" value="李四"/>
    <!-- 第二个参数,如果有  -->
    <!--  <constructor-arg index="1" value="王五"/>-->
</bean>

也可以这样写:

<!-- 有参构造方法创建对象 -->
<bean id="hello1" class="com.study.bean.Hello">
    <!-- 参数列表中的对应参数 -->
    <constructor-arg name="name" value="李四"/>
</bean>

其中 name 属性的值等于 name,是对应了有参构造方法中的 String name 的形参 name,如果是 String name1,那么 name="name1"


还可以这样写:

<!-- 有参构造方法创建对象 -->
<bean id="hello1" class="com.study.bean.Hello">
    <!-- 参数列表中的对应类型 -->
    <constructor-arg type="java.lang.String" value="李四"/>
</bean>

关于 标签的属性解释:

属性名解释
index在有参构造方法的参数列表中的索引找到参数列表中对应参数,index从 0 开始
name通过参数名找到参数列表中对应参数
type通过参数数据类型找到参数列表中对应参数

C 命名空间注入

与使用 P 命名空间相似,使用 C 命名空间之前,也需要在 XML 配置文件中的根标签 中进行声明:

xmlns:c="http://www.springframework.org/schema/c"

c命名空间的用法和p命名空间的用法类似。
对于通过构造方法注入原始类型的对象可以把对应的构造参数名称加上c命名空间的前缀作为bean的一个属性进行定义,对应的值即是构造参数的值。所以,使用 C 命名空间要求其需要有对应参数的构造方法。

Hero 类新增构造方法:

// 无参构造
public Hero(){}

// 有参构造
public Hero(String heroName, int heroAge){
    super();
    this.name = heroName;
    this.age = heroAge;
}

XML 配置文件:

<!-- C 命名空间,使用 构造方法注入。C 就是 constructor-arg 的意思 -->
<bean id="hero2" class="com.study.bean.Hero" c:heroName="nikou" c:heroAge="120"/>

参考

Java Properties 类 — 菜鸟教程
spring bean中的id与name的区别 — CSDN
p命名空间和c命名空间 — CSDN
spring:使用标签为Java持久属性集注入值 — 博客园
打赏
评论区
头像
文章目录