1.依赖注入概述

Spring在创建某个bean对象的时候,该对象所对应的类有时也需要一些其他的依赖,才能完成对象的创建,比如说如果该类是service层的实现类,那么它一定是需要依赖dao层的实现类对象。如果这个类是dao层的实现类,那么它一定是需要一些进行数据库连接时的参数,这些参数的数据类型有的是基本数据类型,比如说Int、也有可能是引用类型,比如说String,更有可能是一个对象,比如说数据源对象。

我们把Spring为这些bean对象提供数据依赖的过程就叫做依赖注入(Dependency Injection),关于依赖注入,我们要讲两个大方面的内容,一个是依赖注入的数据类型,一个是依赖注入的方式:

依赖注入的数据类型主要有三类:

  • 基本数据类型和String
  • 其他的bean类型(在配置文件中或注解中配置过的bean)
  • 复杂类型/集合类型

注入的方法有三种:

  • 使用构造函数提供
  • 使用Set方法提供
  • 使用注解提供

2.依赖注入的方式

2.1 使用构造函数注入

首先,我们在类中写一些可被注入的变量,然后在写好该类的带参构造函数,在构造函数中为变量赋值:

import com.eastnotes.service.AccountService;
import java.util.Date;

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements AccountService {

    private String name;    //String数据类型
    private Integer age;    //基本类型的包装类
    private Date birthday;  //其他bean类型

    public AccountServiceImpl(String name, Integer age, Date birthday){
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public void saveAccount(){
        System.out.println("Dao的saveAccount方法被调用了"+name+","+age+","+birthday);
    }

}

然后我们就可以去bean.xml配置文件中去配置了,既然是这个类所依赖的数据,那么我们就需要在这个类的bean标签内部使用constructor-arg这个标签来注入,该标签有如下几个属性:

  • type:用于指定要注入数据的数据类型,该数据类型也是构造函数中某个或者某些数据类型,不能单独使用
  • index:用于指定参数的索引位置,相对于括号中的位置而言,从0开始,使用起来挺麻烦
  • name:用于给指定名称的参数赋值,常用

以上三个用于指定给构造函数中的那个参数赋值

  • value:用于提供基本数据类型和String类型的数据
  • ref:用于提供其他bean类型数据,它指的就是在Spring IOC核心容器中出现过的bean对象。

下面是全部的配置代码:

<bean id="accountService" class="com.eastnotes.service.impl.AccountServiceImpl" >
    <constructor-arg name="name" value="reborn东东"></constructor-arg>
    <constructor-arg name="age" value="18"></constructor-arg>
    <constructor-arg name="birthday" ref="birthday"></constructor-arg>
</bean>

<bean id="birthday" class="java.util.Date"></bean>

运行结果如下:

Dao的saveAccount方法被调用了reborn东东,18,Wed Sep 25 21:55:17 CST 2019

可以发现,Spring自动将字符串类型的18转换成了Integer类型的18。而且显示中文也没有任何乱码,这就是Spring的强大之处。

这种通过构造函数的方式进行依赖出入的方法的优点是:在获取bean对象的时候,注入数据是必须的操作,否则对象无法创建成功。因为我们只在类中提供了带参构造,而没有提供无参构造。

但是也有一个缺点,改变了这个bean对象的实例化方式,使我们在创建对象时,如果当时用不到这些数据,也必须提供。所以,实际开发中我们一般不用这种方式进行DI。而使用下面的Set方法注入。

2.2 使用Set方法注入

我们给那三个变量,写上Setter方法:

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements AccountService {

    private String name;    //String数据类型
    private Integer age;    //基本类型的包装类
    private Date birthday;  //其他bean类型

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

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

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public void saveAccount(){
        System.out.println("Dao的saveAccount方法被调用了"+name+","+age+","+birthday);
    }

}

然后去配置文件中进行依赖注入的配置,这种方式涉及到的标签是property,它出现的位置也是bean标签的内部。标签的属性如下:

  • name:指定注入参数的setXXX方法名称的XXX,而不是参数的名称。
  • value:用于提供基本数据类型和String类型的数据。
  • ref:用于提供其他bean类型数据,它指的就是在Spring IOC核心容器中出现过的bean对象。

配置实例如下:

<bean id="accountService" class="com.eastnotes.service.impl.AccountServiceImpl" >
    <property name="name" value="reborn东"></property>
    <property name="age" value="22"></property>
    <property name="birthday" ref="birthday"></property>
</bean>

<bean id="birthday" class="java.util.Date"></bean>

运行结果同上。

总结来说,这种方式的优点是:创建对象没有明显的限制,可以直接使用默认构造函数,缺点是:如果某个成员必须有值,则获取对象时有可能set方法没有执行。但我们仍更常用set方法的方式。

3. 复杂类型(集合)的注入

前两种数据类型,如基本数据类型和String引用类型,以及其他bean类型的注入,在上一节已经讲过了,这里新开一节说一下复杂集合类型的注入,常见的集合类型有数组、List、Map、Set、Properties。首先在类中定义这几个变量:

public class AccountServiceImplComplex implements AccountService {

    private String[] myStr;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String, String> myMap;
    private Properties myProp;

    public void setMyStr(String[] myStr) {
        this.myStr = myStr;
    }

    public void setMyList(List<String> myList) {
        this.myList = myList;
    }

    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public void setMyProp(Properties myProp) {
        this.myProp = myProp;
    }

    public void saveAccount(){
        System.out.println("AccountServiceImplComplex{" +
                "myStr=" + Arrays.toString(myStr) +
                ", myList=" + myList +
                ", mySet=" + mySet +
                ", myMap=" + myMap +
                ", myProp=" + myProp +
                '}');
    }

}

然后去配置文件中进行配置,这时我们就不能在beans标签下的property标签里通过配置属性的方式给变量赋值了,我们需要用另外的标签,这些标签需要放在property标签里。具体配置详情如下:

<bean id="accountService" class="com.eastnotes.service.impl.AccountServiceImplComplex" >
    <property name="myList">
        <list>
            <value>AAA</value>
            <value>BBB</value>
            <value>CCC</value>
        </list>
    </property>

    <property name="myStr">
        <array>
            <value>AAA</value>
            <value>BBB</value>
            <value>CCC</value>
        </array>
    </property>

    <property name="mySet">
        <set>
            <value>AAA</value>
            <value>BBB</value>
            <value>CCC</value>
        </set>
    </property>

    <property name="myMap">
        <map>
            <entry key="test1" value="AAA"></entry>
            <entry key="test2" value="BBB"></entry>
        </map>
    </property>

    <property name="myProp">
        <props>
            <prop key="1">1</prop>
            <prop key="2">2</prop>
        </props>
    </property>
</bean>

额外说的一点就是结构相同的标签可以互换,比如说我们使用标签给List进行数据注入也是可以的,使用标签给Map类型注入也是可以的。总结来说:

  • 用于给List结构注入的标签可以是:array、list、set
  • 用于给Map结构注入的标签可以是:map、props