Spring_IOC/DI

1、问题引入

在说Spring之前,首先来回顾一下我们之前学习的javaSE部分的知识,假如我们现在有一个需求:现在有两个类(A类和B类),我们需要在第二个类(B)中调用第一个类(A)的某些方法,用我们以前学过的知识,肯定是在第二个类中实例化一个A类的对象,然后用对象调用对应的方法,具体操作如下:

但是这么做并不是一个好的方法,因为这样做我们可以理解为是A类 ‘污染’ 了B类,或者说是A类 ‘ 侵入 ’ 了B类。我们可以这么理解,假如现在一个项目中有很多个类都用这种方式调用了A类的方法,但是随着技术的更新有一天我们发现A类现在已经毫无存在的价值了,我们有了更好的方式去代替它,但是如果这个时候要删掉A类,那么每一个调用了A类这个方法的类都需要手动去修改源代码。这样就会给我们带来很大的麻烦。因此,我们应该用另一种更好的方法区代替上面的操作。

这个时候我们可能会想到使用接口,让A、B两个类实现同样的一个接口,然后覆写接口里面的方法,这样看起来似乎也没有多大的问题:

这样看起来是和A类没有多大的关系,但是使用这种还是摆脱不了new一个对象的命运。和这个比起来,我们更加希望:假如现在有这样一个 ‘接口’ ,当我们需要哪一个类的对象的时候,由这个特殊的接口直接给我们返回这个对象的实例,而不用让我们自己手动去new一个对象,增加代码的耦合度。比如:

其实上面这种思想就是Spring中的IOC/DI思想。

2、IOC 和 DI

2.1 什么是IOC和DI

IOC的全称为 Inversion of Control ,顾名思义就是控制反转

在传统方法中,当一个类中需要另一个类的实例化对象,通常都是自己手动去new一个,而有了IOC之后,IOC中有一个容器,这个容器就相当于上面提到的“特殊的接口”(注意:容器不等于接口,我这里说它是一个特殊的接口,只是举个例子方便描述它的功能而已)。这个容器负责创建实例化对象,然后再在适当的时候主动将对象给需要依赖这个对象的类。

所谓的控制反转:

  • 控制的就是上面说的创建对象的过程(也就是new的过程);谁控制谁 —— IOC容器控制对象的创建
  • 反转:有反转就有正转,像我们的传统方法,自己需要什么就自己动手去创建什么,这个就是正转;而通过容器控制对象的创建以及依赖对象的注入,这个过程就是反转。因为是容器帮我们查找并注入依赖对象,所以对象只是被动的接收依赖对象,所以叫做反转。反转的就是依赖对象获取的过程。

DI的全称为Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

依赖注入和控制反转之间的关系:

依赖注入和控制反转其实描述的是同一个东西,只是从不同的角度来描述而已。控制反转就是为一个对象动态的提供它所需要的另一个对象,而这个过程是通过依赖注入来实现的。比如:B类需要A类的一个对象,然后容器new一个A的对象,在适当的时候,就像打针一样,把A的对象注射到B里面,这样就实现了依赖注入。

IOC不是什么新技术,而是一种思想。下面在通过一个简单的例子去理解IOC和传统方法的区别;

就拿找女朋友来说,刚开始,我们是在茫茫人海中自己去寻找,我们自己知道我们需要的是怎么样一个人,当我们发现一个目标之后,开始自己私下去打听她的qq,微型,手机号......制造巧合去认识她,然后投其所好......这一系列的过程都需要我们自己去操作,然而,现在有了IOC,就相当于是一个婚介所,他们那里存了整个村子的男男女女的资料,这个时候我们只需要将我们的要求告诉他们,然后让他们找到合适的对象之后再主动给我,如果她给我的这个对象合适了,我们就谈谈恋爱、然后结婚......如果不合适,就抛出异常。这整个过程不用我们自己去操作,而是交给了婚介所这样的一个容器去控制整个过程。Spring也就是这个思想:所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

3、在代码中简单体验IOC和DI

继续使用上面的A,B两个类:

A类:

在配置文件中配置A类

在B类中获取A类的实例对象

运行结果:

如果是新手,看来这个可能还是一脸茫然,不知道是怎么回事,下面会有完整的Spring项目创建以及IOC和DI的实例;

4、Spring属性实例化案例

使用工具:IDEA+maven

首先,创建一个maven项目(前提是你电脑中已经安装好了maven)

早maven项目的pom文件中导入依赖的jar包:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
</dependencies>

在resource文件下创建一个配置文件(.xml) (如 applicationContext.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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

<!--在这里配置类、属性信息-->

</beans>

这是下面例子的目录结构:

几个类结构:


import java.util.*;

/**
 * 测试各种数据类型的属性注入
 */
public class MyTest {

    private Integer intVal ; // 整形数据
    private String strVal ; // String 数据
    private String[] strArr ; // 数组类型
    private List list ; // List集合
    private Set set ;  // Set集合
    private Map map; // Map集合
    private User user; //自定义类型
    private Date date; // 日期类型

    // 为每一个属性添加一个get和set方法 通过set方法实现属性注入,所以必须要有set方法
    public void setIntVal(Integer intVal) {
        this.intVal = intVal;
    }

    public void setStrVal(String strVal) {
        this.strVal = strVal;
    }

    public void setStrArr(String[] strArr) {
        this.strArr = strArr;
    }

    public void setList(List list) {
        this.list = list;
    }

    public void setSet(Set set) {
        this.set = set;
    }

    public void setMap(Map map) {
        this.map = map;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public void setDate(Date date) {
        this.date = date;
    }

//    覆写toString方法用于打印属性信息
    @Override
    public String toString() {
        return " intVal = " + intVal +
                "\r\n strVal = '" + strVal + '\'' +
                "\r\n strArr = " + Arrays.toString(strArr) +
                "\r\n list = " + list +
                "\r\n set = " + set +
                "\r\n map = " + map +
                "\r\n date = " + date +
                "\r\n user = " + user ;
    }
}
/**
 * 用于测试自定义属性注入的User类
 */
public class User {
    private Integer age ;
    private String name ;

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

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

    // 覆写toString方法用于打印信息
    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

4.1 通过set方法实例化:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

<!--在这里配置类、属性信息-->
    <bean id="test" class="per.fei._3SpringProjectTest.MyTest">

        <!--整形的注入-->
        <property name="intVal">
            <value>18</value>
        </property>
        <!--整形也可以这么弄-->
        <!--这里写的是String ("18"),而类中的类型是Integer ( = 18),spring内部实现了从String到Integer的转换-->
        <!--<property name="intVal" value="18"/>-->

        <!--String类型-->
        <property name="strVal" value="hahaha"/>

        <!--数组类型-->
        <property name="strArr">
           <list>
               <value>strArr_1</value>
               <value>strArr_2</value>
               <value>strArr_3</value>
           </list>
        </property>

        <!--list就是一个动态数组,所以和数组基本一样-->
        <property name="list">
            <list>
                <value>list_1</value>
                <value>list_2</value>
                <value>list_3</value>
            </list>
        </property>

        <!--set类型的数据-->
        <property name="set">
            <set>
                <value>set_01</value>
                <value>set_02</value>
                <value>set_03</value>
            </set>
        </property>

        <!--map类型-->
        <property name="map" >
            <map>
                <entry key="1" value="no_1"/>
                <entry key="2" value="no_2"/>
                <entry key="3" value="no_3"/>
            </map>
        </property>

        <!--Date类型不能直接像下面这么弄,要先写一个类型转换器(MyConvert.java),
因为Spring虽然在上面说实现了从String到Integer的类型转换,-->
        <!--但是没有实现从String到Date的类型转换,需要我们自己实现一个转换器-->
        <property name="date" value="2019-5-21"/>

        <!--自定义类型-可以先配置对应的自定义类型的bean,然后通过ref(反射)直接获取对应的自定义类型-->
        <property name="user" ref="user"/>


    </bean>

    <!--配置User类-->
    <bean id="user" class="per.fei._3SpringProjectTest.User">
        <property name="name" value="张三"/>
        <property name="age" value="22"/>
    </bean>

    <!--自定义属性编辑器到Spring中-->
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map >
                <entry key="java.util.Date" value="per.fei._3SpringProjectTest.MyConvert"/>
            </map>
        </property>
    </bean>

</beans>

测试结果:

4.2 通过构造方法实例化:

(这里需要把上面的MyTest类中的set方法换成构造方法,其余类结构的不变)

MyTest.java

import java.util.*;
/**
 * 测试各种数据类型的属性注入
 */
public class MyTest {

    private Integer intVal ; // 整形数据
    private String strVal ; // String 数据
    private String[] strArr ; // 数组类型
    private List list ; // List集合
    private Set set ;  // Set集合
    private Map map; // Map集合
    private User user; //自定义类型
    private Date date; // 日期类型

//   添加构造方法
    public MyTest(Integer intVal, String strVal, String[] strArr, List list, Set set, Map map, User user, Date date) {
        this.intVal = intVal;
        this.strVal = strVal;
        this.strArr = strArr;
        this.list = list;
        this.set = set;
        this.map = map;
        this.user = user;
        this.date = date;
    }

    //    覆写toString方法用于打印属性信息
    @Override
    public String toString() {
        return " intVal = " + intVal +
                "\r\n strVal = '" + strVal + '\'' +
                "\r\n strArr = " + Arrays.toString(strArr) +
                "\r\n list = " + list +
                "\r\n set = " + set +
                "\r\n map = " + map +
                "\r\n date = " + date +
                "\r\n user = " + user ;
    }
}

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

<!--在这里配置类、属性信息-->
    <bean id="test" class="per.fei._3SpringProjectTest.MyTest">
        <!--int型-->
        <constructor-arg name="intVal" value="18"/>
        <!--String类型-->
        <constructor-arg name="strVal" value="hahaha"/>
        <!--数组类型-->
        <constructor-arg name="strArr">
            <list>
                <value>strArr_01</value>
                <value>strArr_02</value>
                <value>strArr_03</value>
            </list>
        </constructor-arg>
        <!--list类-->
        <constructor-arg name="list">
            <list>
                <value>list_01</value>
                <value>list_02</value>
                <value>list_03</value>
            </list>
        </constructor-arg>
        <!--set类型-->
        <constructor-arg name="set">
            <set>
                <value>set_01</value>
                <value>set_02</value>
                <value>set_03</value>
            </set>
        </constructor-arg>
        <!--map类型-->
        <constructor-arg name="map">
            <map>
                <entry key="mapKey_01" value="mapVal_01"/>
                <entry key="mapKey_02" value="mapVal_02"/>
                <entry key="mapKey_03" value="mapVal_03"/>
            </map>
        </constructor-arg>
        <!--自定义类User-->
        <constructor-arg name="user" ref="user"/>
        <!--Date类-->
        <constructor-arg name="date" value="2019-5-21"/>
    </bean>

    <!--配置User类-->
    <bean id="user" class="per.fei._3SpringProjectTest.User">
        <property name="name" value="张三"/>
        <property name="age" value="22"/>
    </bean>
    <!--自定义属性编辑器到Spring中-->
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map >
                <entry key="java.util.Date" value="per.fei._3SpringProjectTest.MyConvert"/>
            </map>
        </property>
    </bean>

</beans>

Test类运行结果:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值