Spring的IOC的底层原理

image-20210402132446582

Spring IOC的快速入门案例

  • 新建Maven项目:spring-exmaple

  • maven的pom.xml文件中引入Spring相关依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    <!--Spring相关依赖包-->
    <!--提供了框架的基本组成部分,包括 IoC 和依赖注入功能-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
    </dependency>

    <!--提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>${spring.version}</version>
    </dependency>

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
    </dependency>

    <!--模块提供了强大的表达式语言,用于在运行时查询和操作对象图-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>${spring.version}</version>
    </dependency>

    <!--日志相关-->
    <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
    </dependency>

    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.14.0</version>
    </dependency>
  • 理解IOC控制反转和DI依赖注入

  • 编写Spring核心配置文件

  • 在程序中读取Spring配置文件,通过Spring框架获得Bean,完成相应操作

实现步骤

  1. 实现UserService接口及其实现类

    • UserService.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      package com.example.ioc.demo;

      /**
      * @author jinglv
      * @date 2020/12/20
      */
      public interface UserService {
      /**
      * say hello方法
      */
      void sayHello();
      }

  • UserServiceImpl.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.example.ioc.demo;

    /**
    * @author jinglv
    * @date 2020/12/20
    */
    public class UserServiceImpl implements UserService {
    /**
    * say hello的实现方法
    */
    @Override
    public void sayHello() {
    System.out.println("Hello Spring");
    }
    }
  1. 新建Spring配置文件applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?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">

    <!--UserService的创建权交给了Spring-->
    <bean id="userService" class="com.example.ioc.demo.UserServiceImpl"/>

    </beans>
  1. 编写测试,对比传统方式与Spring IOC的方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    package com.example.ioc.demo;

    import org.junit.jupiter.api.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;

    /**
    * @author jinglv
    * @date 2020/12/20
    */
    class UserServiceImplTest {

    /**
    * 传统方式调用
    */
    @Test
    void sayHello01() {
    // 直接new实现类,接口类与实现类则有耦合关系
    UserService userService = new UserServiceImpl();
    userService.sayHello();
    }

    /**
    * Spring的方式实现
    */
    @Test
    void sayHello02() {
    // 创建Spring的工厂
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 通过工厂获得类
    UserService userService = (UserService) applicationContext.getBean("userService");
    // 调用方法
    userService.sayHello();
    }
    }

IOC与DI

  • IOC Innverse of Control反转控制的概念,就是将原本在程序中手动创建UserService对象控制权,交由Spring框架管理
  • 简单说,就是创建UserService对象控制权被反转到了Spring框架
  • DI Dependency Injection依赖注入的概念,就是在Spring创建这个对象的过程中,将这个对象所依赖的属性注入进入

DI实现的案例

  1. 以上案例,进行改造,UserServiceImpl.java中添加属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    package com.example.ioc.demo;

    /**
    * @author jinglv
    * @date 2020/12/20
    */
    public class UserServiceImpl implements UserService {

    /**
    * 添加属性
    */
    private String name;

    /**
    * say hello的实现方法
    */
    @Override
    public void sayHello() {
    System.out.println("Hello Spring:" + name);
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }
    }
  1. 修改Spring的配置文件applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?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">

    <!--UserService的创建权交给了Spring-->
    <bean id="userService" class="com.example.ioc.demo.UserServiceImpl">
    <!--设置属性-->
    <property name="name" value="小红"/>
    </bean>
    </beans>
  1. 测试用例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package com.example.ioc.demo;

    import org.junit.jupiter.api.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;

    /**
    * @author jinglv
    * @date 2020/12/20
    */
    class UserServiceImplTest {

    /**
    * 传统方式调用
    */
    @Test
    void sayHello01() {
    // 直接new实现类,接口类与实现类则有耦合关系
    // UserService userService = new UserServiceImpl();
    // 设置name,需要修改代码
    UserServiceImpl userService = new UserServiceImpl();
    // 设置属性
    userService.setName("小红");
    // 调用方法
    userService.sayHello();
    }

    /**
    * Spring的方式实现
    */
    @Test
    void sayHello02() {
    // 创建Spring的工厂
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 通过工厂获得类
    UserService userService = (UserService) applicationContext.getBean("userService");
    // 调用方法
    userService.sayHello();
    }
    }

重新认识IOC

  1. IoC 发展简介
  2. IoC 主要实现策略
  3. IoC 容器的职责
  4. IoC 容器的实现
  5. 传统 IoC 容器实现
  6. 轻量级 IoC 容器
  7. 依赖查找 VS. 依赖注入
  8. 构造器注入 VS. Setter 注入

IoC 发展简介

  • 什么是 IoC ?

In software engineering, inversion of control (IoC) is a programming principle. IoC inverts the flow of control as compared to traditional control flow. In IoC, custom-written portions of a computer program receive the flow of control from a generic framework. A software architecture with this design inverts control as compared to traditional procedural programming: in traditional programming, the custom code that expresses the purpose of the program calls into reusable libraries to take care of generic tasks, but with inversion of control, it is the framework that calls into the custom, or task-specific, code.

来源:https://en.wikipedia.org/wiki/Inversion_of_control

  • IoC 的简史
    • 1983年,Richard E. Sweet 在《The Mesa Programming Environment》中提出“Hollywood Principle”(好莱坞原则)
    • 1988年,Ralph E. Johnson & Brian Foote 在《Designing Reusable Classes》中提出“Inversion of control”(控制反转)
    • 1996年,Michael Mattsson 在《Object-Oriented Frameworks, A survey of methodological issues》中将“Inversion of control”命名为 “Hollywood principle”
    • 2004年,Martin Fowler 在《Inversion of Control Containers and the Dependency Injection pattern》中提出了自己对 IoC 以及 DI 的理解
    • 2005年,Martin Fowler 在 《InversionOfControl》对 IoC 做出进一步的说明

IoC 主要实现策略

Implementation techniques 小节的定义:

“In object-oriented programming, there are several basic techniques to implement inversion of control. These are:

  • Using a service locator pattern
  • Using dependency injection, for example
    • Constructor injection - 构造器注入
    • Parameter injection - 属性注入
    • Setter injection - set注入
    • Interface injection - 接口注入
  • Using a contextualized lookup
  • Using template method design pattern
  • Using strategy design pattern”

《Expert One-on-OneTM J2EETM Development without EJBTM》提到的主要实现策略: “IoC is a broad concept that can be implemented in different ways. There are two main types:

  • Dependency Lookup: The container provides callbacks to components, and a lookup context. This is the EJB and Apache Avalon approach. It leaves the onus on each component to use container APIs to look up resources and collaborators. The Inversion of Control is limited to the container invoking callback methods that application code can use to obtain resources.
  • Dependency Injection: Components do no look up; they provide plain Java methods enabling the container to resolve dependencies. The container is wholly responsible for wiring up components, passing resolved objects in to JavaBean properties or constructors. Use of JavaBean properties is called Setter Injection; use of constructor arguments is called Constructor Injection.”

IoC 容器的职责

维基百科(https://en.wikipedia.org/wiki/Inversion_of_control) 在 Overview 小节中提到:
“Inversion of control serves the following design purposes:

  • To decouple the execution of a task from implementation.
  • To focus a module on the task it is designed for.
  • To free modules from assumptions about how other systems do what they do and instead rely on contracts.
  • To prevent side effects when replacing a module.

Inversion of control is sometimes facetiously referred to as the “Hollywood Principle: Don’t call us, we’ll call you”.”

通用职责

  • 依赖处理
    • 依赖查找
    • 依赖注入
  • 生命周期管理
    • 容器
    • 托管的资源(Java Beans 或其他资源)
  • 配置
    • 容器
    • 外部化配置
    • 托管的资源(Java Beans 或其他资源)