Spring框架3:spring的依赖注入

Posted 别再闹了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring框架3:spring的依赖注入相关的知识,希望对你有一定的参考价值。

本系列笔记均是对b站教程https://www.bilibili.com/video/av47952931 的学习笔记,非本人原创

spring的依赖注入(DI)

什么是依赖注入:

作者:Angry Bugs
链接:https://www.zhihu.com/question/32108444/answer/581948457
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

看了几个高赞答案,感觉说得还是太啰嗦了。依赖注入听起来好像很复杂,但是实际上炒鸡简单,一句话说就是:本来我接受各种参数来构造一个对象,现在只接受一个参数——已经实例化的对象。也就是说我对对象的『依赖』是注入进来的,而和它的构造方式解耦了。构造它这个『控制』操作也交给了第三方,也就是控制反转。不举抽象的什么造汽车或者小明玩儿手机的例子了。一个很实际的例子,比如我们要用 redis 实现一个远程列表。耦合成一坨的代码可以是这样写,其中我们需要自己构造需要用的组件:class RedisList:
    def __init__(self, host, port, password):
        self._client = redis.Redis(host, port, password)

    def push(self, key, val):
        self._client.lpush(key, val)

l = RedisList(host, port, password)依赖翻转之后是这样的:class RedisList:
    def __init__(self, redis_client)
        self._client = redis_client

    def push(self, key, val):
        self._client.lpush(key, val)

redis_client = get_redis_client(...)
l = RedisList(redis_client)看起来好像也没什么区别,但是考虑下面这些因素:线下线上环境可能不一样,get_redis_client 函数在线上可能要做不少操作来读取到对应的配置,可能并不是不是一个简单的函数。redis 这个类是一个基础组件,可能好多类都需要用到,每个类都去自己实例化吗?如果需要修改的话,每个类都要改。我们想依赖的是 redis 的 lpush 方法,而不是他的构造函数。所以把 redis 这个类的实例化由一个单一的函数来做,而其他函数只调用对应的接口是有意义的。就这么简单啊。。
构造函数注入

AccountServiceImpl :

package com.jiading.service.impl;

import com.jiading.service.IAccountService;

import java.util.Date;

/*
账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService {
    //如果是经常变化的数据,并不适用于注入的方式
    private String name;
    private Integer age;//基本类型的包装类
    private Date birthday;

    public AccountServiceImpl(String name,Integer age,Date birthday){
        this.name=name;
        this.age=age;
        this.birthday=birthday;
    }
    public void saveAccount() {
        System.out.println("service中的saveAccount方法执行了+"+name+","+age+","+birthday);
    }
}
<?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">
    <!-- spring的依赖注入
      依赖注入:Dependency Injection
      IOC的作用:
          降低程序间的耦合(依赖关系)
      依赖关系的管理交给了spring来维护
      依赖关系:
        在当前类需要用到其他类的对象,由spring为我们提供,我们在配置文件中说明
      依赖关系的维护:
        称之为依赖注入
      依赖注入:
        能注入的数据类型:
            1. 基本类型和String
            2. 其他的bean类型(在配置文件中或者注解配置过的bean)
            3. 复杂类型/集合类型
        注入方式:
            1. 使用构造函数提供
            2. 使用set方法提供
            3. 使用注解提供
    -->
    <!-- 构造函数注入:
        使用标签constructor-arg
        标签出现的位置:bean标签内部
        标签的属性:
            type:要注入的数据的数据类型,该类型也是构造函数中某个或者某些参数的类型
            index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值
            name:用于指定要注入的数据给构造函数中指定名称的参数赋值
            以上三个用于指定给构造函数中哪个参数赋值
            value:用于提供基本类型和String类型的数据
            ref:用于指定其他bean类型数据,它指的就是在spring的IOC核心容器中出现过的bean对象
    -->
    <bean id="accountService" class="com.jiading.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="test"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <!-- 那个18会由框架自动解析为Integer类型-->
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>
    <!-- 配置一个日期对象-->
    <bean id="now" class="java.util.Date"></bean>
    <!-- 构造函数注入
    优势:
        在获取bean对象时,注入数据是必须的操作,否则对象无法实例化成功,不容易被错误调用
    弊端:
        改变了bean对象的实例化方式,使我们在创建对象时如果用不到这些数据,也必须提供
    实际开发中,由于其不太灵活,一般不采用这种方式
    -->
</beans>
set方法注入
package com.jiading.service.impl;

import com.jiading.service.IAccountService;

import java.util.Date;

/*
账户的业务层实现类
 */
public class AccountServiceImpl2 implements IAccountService {
    //如果是经常变化的数据,并不适用于注入的方式
    private String name;
    private Integer age;//基本类型的包装类
    private Date birthday;


    public void saveAccount() {
        System.out.println("service中的saveAccount方法执行了+"+name+","+age+","+birthday);
    }

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

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

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}
    <bean id="accountService2" class="com.jiading.service.impl.AccountServiceImpl2">
        <!-- set方法注入
            涉及到标签:property
            出现的1位置:bean内部
            标签的属性:
                name:用于指定注入时所调用的set方法名称(不包括set本身,只包括属性名,例如方法叫setName,这里写name就好)
                value:用于提供基本类型和String类型的数据
                ref:用于指定其他bean类型数据,它指的就是在spring的IOC核心容器中出现过的bean对象
            优势:创建对象时没有明确的界限,可以直接使用默认构造函数
            弊端:如果有某个成员必须有值,则获取对象时有可能set方法没有被执行(因为和构造函数方式相比,set方法更多的是调用者主动加property,缺少强制性
            相比构造函数注入,是更常用的方式
        -->
        <property name="name" value="Test"></property>
        <property name="age" value="21"></property>
        <property name="birthday" ref="now"></property>
    </bean>
    ```
    ##### 复杂类型的注入
    ```java
    package com.jiading.service.impl;

import com.jiading.service.IAccountService;

import java.util.*;

/*
账户的业务层实现类
 */
public class AccountServiceImpl3 implements IAccountService {
    private String[] myStrs;
    private List<String> myList;

    public void setMyStrs(String[] myStrs) {
        this.myStrs = myStrs;
    }

    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 setMyProps(Properties myProps) {
        this.myProps = myProps;
    }

    private Set<String> mySet;
    private Map<String, String> myMap;
    private Properties myProps;

    public void saveAccount() {
        System.out.println(Arrays.toString(myStrs));
        System.out.println(myList);
        System.out.println(mySet);
        System.out.println(myMap);
        System.out.println(myProps);
    }
}
    <!-- 复杂类型的注入/集合类型的注入-->
    <bean id="accountService3" class="com.jiading.service.impl.AccountServiceImpl3">
        <property name="MyStrs">
            <array>
                <!-- 用于给List结构集合注入的标签有listarrayset。因为结构一样,所以可以互换-->
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>
        <property name="MyList">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>
        <property name="MySet">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>
        <property name="myMap">
            <map>
                <!-- 用于给map结构集合注入的标签有mapprops。因为结构一样,所以可以互换-->
                <entry key="testA" value="aaa"></entry>
                <entry key="testB">
                    <value>bbb</value>
                </entry>
            </map>
        </property>
        <property name="myProps">
            <props>
                <prop key="testC">ccc</prop>
                <prop key="testD">ddd</prop>
            </props>
        </property>
    </bean>

可以看出,通过依赖注入,我们可以直接传入实例化对象,参数可以在配置文件中配置完成,省去了每次传入的麻烦

以上是关于Spring框架3:spring的依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

Spring框架--依赖注入

框架----Spring中依赖注入

Spring 框架学习---- 依赖注入

JAVA框架 Spring 依赖注入

Spring框架学习笔记 ---[spring框架概念 , 初步上手使用Spring , 控制反转 & 依赖注入初步理解 ]

spring中依赖注入的原理