通俗易懂,带你从零认识Spring IoC,把你安排得明明白白。

271 阅读7分钟

本来这篇文章差不多有一万七百字左右,从IOC到AOP。算是对自己学习Spring的一个验收,同时也分享出来供大家查漏补缺。但因为内容实在太多,所以就先码了IOC的这篇内容,明天再补发AOP的内容,希望大家多多支持。

同时提供免费的学习资料,学习技术内容包含有:Spring,Dubbo,MyBatis, RPC,源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。

需要的朋友可以点击:点这个!点这个!,暗号:01

在这里插入图片描述

IoC 思想

IOC(控制反转)是一种依赖倒置原则的代码设计的思路,它主要采用(DI)依赖注入的方式来实现。

不使用IoC思想的传统模式

在传统模式中,对象由程序员主动创建,控制权在程序员手中。 程序可以做到正常工作,但仍有一个难以避免的问题。 如果用户需求变更,程序员就要修改对应的代码,代码量不大还好,如果代码量巨大的话 修改一次的成本....... 这个问题就是耦合性过高引起的,修改一次需求,或多或少会造成代码的修改,工作量先不说,维护起来也是极其不便的啊。 在这里插入图片描述

  • 就如上图中这四个齿轮(对象)一样,互相啮合,如果有一方停止或更换 其他的齿轮也就没办法工作,这自然不是我们希望看到的。

为了解决对象间耦合过高的问题,软件专家Michael Mattson提出了IoC理论,用来实现对象之间的“解耦”。

那么应当如何去达到理想的效果呢?

使用IoC思想后的模式

IoC的主要思想是借助一个“第三方”来拆开原本耦合的对象,并将这些对象都与“第三方”建立联系,由第三方来创建、操作 这些对象,进而达到解耦的目的。 在这里插入图片描述

因此IoC容器也就成了整个程序的核心,对象之间没有了联系(但都和IoC容器有联系)。 这里引用一句知乎上看到的话

IoC的思想最核心的地方在于,资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。

什么是控制反转

这里我们引入一个场景, 如果A对象想调用B对象。

传统模式中该如何操作 大家都很熟悉了,在A对象中创建一个B对象实例,就可以满足A对象调用B对象的需求。这是我们在A对象中主动的去创建B对象.

而引入IoC后,A对象如果想调用B对象,IoC容器会创建一个B对象注入到A对象中,这样也可以满足A对象的调用需求。但是过程由我们的主动创建,变成了A对象被动的去接收IoC容器注入的B对象。

A对象依赖B对象的过程,由程序员的主动创建B对象供其依赖,变为了被动的接收IoC容器注入的对象。控制权从程序员手中交到了IoC容器手中。A对象获得依赖的过程也由主动变为被动,这就是所谓的控制反转。

什么是依赖注入(DI)

依赖注入是IoC思想最主要的实现方式,也就是上文提到的A对象如果想调用B对象,IoC容器会创建一个B对象注入到A对象中,这样就可以满足A对象对B对象的依赖需求。这个行为就是依赖注入。

DI ≠ IOC

IoC的概念更宽广一些,而DI是IoC的主要实现方式,但这并不意味着DI就是IoC,将二者混为一谈 这是不对的,很容易误导他人。 就比如你想要阅读,最主要的实现方式自然是“用眼睛长时间的去看”,但你不能把这个“眼睛长时间去看”的行为 理解为阅读。(可能例子有点不恰当)

注入方式

setter方法注入

我们需要在类中生成一个set方法和一个空构造(空构造在没有声明有参构造时会隐式声明,无需再进行多余的操作)

public class Hello {
	
    private String name;
	// 一定要生成set方法
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

在Spring配置文件中注入Bean(对象),在Bean中使用property标签为name属性赋值

写一个简单的测试方法,测试property标签是否成功赋值(是)。

构造器注入

构造器注入我们需要手动的生成一个有参构造

package com.molu.pojo;

public class Hello {

    private String name;
    // 生成有参构造
    public Hello(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

这个时候再切回applicationContext.xml中可以看到已经报错了 在这里插入图片描述

因为显式的定义了有参构造后,无参构造就不存在了,我们需要将property标签改为constructor-arg

constructor-arg标签赋值的方式更为多样化。

  • 通过下标赋值
<bean id="hello" class="com.molu.pojo.Hello">
    <constructor-arg index="0" value="陌路"/>
</bean>
  • 通过参数类型赋值(不推荐,参数类型容易重合)
<bean id="hello" class="com.molu.pojo.Hello">
    <constructor-arg type="java.lang.String" value="陌路"/>
</bean>
  • 通过参数名赋值(推荐)
<bean id="hello" class="com.molu.pojo.Hello">
    <constructor-arg name="name" value="陌路"/>
</bean>

结果都是一样的,这里就不再展示测试结果了

拓展注入

P(Property)命名空间注入

  • 在使用P命名空间之前要在引入它的约束
xmlns:p="http://www.springframework.org/schema/p"
  • P命名空间的使用必须要有一个空构造。(类似于setter方法注入)但前面也说了,没有声明有参构造时 空构造会隐式声明。
  • P命名空间注入 可以直接在Bean标签中进行注入

首先创建一个Me类

package com.molu.pojo;

public class Me {
    
    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; }

在applicationContext.xml中用P命名空间进行注入

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--在Bean标签中,使用P命名空间进行简单的属性注入-->
    <bean id="me" class="com.molu.pojo.Me" p:name="陌路" p:age="18"/>
</beans>

测试

略......

C(constructor-arg)命名标签注入

  • 使用C命名空间之前也需要引入约束
  • xmlns:c="www.springframework.org/schema/c"
  • C命名空间不同于P命名空间,他必须要有一个构造方法(类似构造器注入)
  • P命名空间也可以直接在Bean标签中进行简单的注入操作

在Me类中添加构造器

package com.molu.pojo;

public class Me {
    private String name;
    
    private int age;
    // 生成有参构造 供C命名空间调用
    public Me(String name, int age) {
        this.name = name;
        this.age = 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; }
}

在applicationContext.xml中用C命名空间进行注入

<!--C命名空间注入,通过构造器注入-->
<bean id="me" class="com.molu.pojo.Me" c:name="陌路" c:age="18" />

测试

略......

关于其他的注入方式这里就不再一一列举了,官网上写的很详细,有能力的朋友可以移步官网,在官网上寻求答案

补充

我们再写一个HelloTwo类,里面和Hello类一样,唯一不同是显式的定义了无参构造而不是有参构造。

package com.molu.pojo;

public class HelloTwo {
    private String name;
    // 显式的定义了无参构造
    public HelloTwo() {
        // 简单写一个测试输出语句
        System.out.println("HelloTwo的无参构造被调用了");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

在Application Context.xml中注册bean,不对其进行其他任何操作。

<bean id="helloTwo" class="com.molu.pojo.HelloTwo"></bean>

使用刚刚用过的Hello类测试方法 原封不动进行测试

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.getName());
    }
}

控制台输出了我们在HelloTwo无参构造中写的输出语句,奇怪的是我们并没有在测试类中写任何关于HelloTwo的代码。。 在这里插入图片描述

由此能够得到一些信息:“注册进applicationContext.xml中的bean,无论你调用与否,他都会被初始化”

ioc到这里就总结得差不多了,下一篇文章就讲讲AOP。

最后还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2020年最新大厂面试题。

需要的朋友可以点击:点这个!点这个!,暗号:01

在这里插入图片描述 在这里插入图片描述