• 【Spring框架】——2. Spring IOC


    IOC(控制反转)是一种设计思想或者说是理论,下面通过一个简单的示例进行推导。

    1. IOC 理论推导

    1.1 新建一个 java 项目

    开发环境

    • idea
    • jdk 1.8
    • maven 3.6.3

    新建项目

    1、使用 idea 和 maven 创建项目,目录结构如下

    在这里插入图片描述
    2、在父 pom.xml 文件中,引入以下两个依赖包

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.yuhuofei</groupId>
        <artifactId>spring-study</artifactId>
        <packaging>pom</packaging>
        <version>1.0-SNAPSHOT</version>
        <modules>
            <module>01-spring-ioc</module>
        </modules>
        <dependencies>
            <!--引入spring框架依赖包-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.3.20</version>
                <type>pom</type>
            </dependency>
    
            <!--引入jdbc依赖包-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.3.20</version>
            </dependency>
    
        </dependencies>
    </project>
    
    • 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

    3、在 dao 目录下新建以下接口及实现类

    • UserDao.java
    package com.yuhuofei.dao;
    
    /**
     * @Description
     * @InterfaceName UserDao
     * @Author yuhuofei
     * @Date 2022/6/14 22:00
     * @Version 1.0
     */
    public interface UserDao {
    
        public void selectUser();
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • UserDaoImpl.java
    package com.yuhuofei.dao;
    
    /**
     * @Description
     * @ClassName UserDaoImpl
     * @Author yuhuofei
     * @Date 2022/6/14 22:01
     * @Version 1.0
     */
    public class UserDaoImpl implements UserDao {
    
        @Override
        public void selectUser() {
            System.out.println("获取当前的用户信息");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4、在 service 目录下,新建以下接口及实现类

    • UserService.java
    package com.yuhuofei.service;
    
    /**
     * @Description
     * @InterfaceName UserService
     * @Author yuhuofei
     * @Date 2022/6/14 22:02
     * @Version 1.0
     */
    public interface UserService {
        public void getUser();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • UserServiceImpl.java
    package com.yuhuofei.service;
    
    import com.yuhuofei.dao.UserDao;
    import com.yuhuofei.dao.UserDaoImpl;
    
    /**
     * @Description
     * @ClassName UserServiceImpl
     * @Author yuhuofei
     * @Date 2022/6/14 22:02
     * @Version 1.0
     */
    public class UserServiceImpl implements UserService {
    
        private UserDao userDao = new UserDaoImpl();
    
        @Override
        public void getUser() {
            userDao.selectUser();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    至此,一个简单的 java 小项目就搭建完了。

    5、新建一个测试类

    在 test/java 目录下,新建一个测试类 TestIoc.java ,内容如下:

    import com.yuhuofei.service.UserService;
    import com.yuhuofei.service.UserServiceImpl;
    
    /**
     * @Description
     * @ClassName TestIoc
     * @Author yuhuofei
     * @Date 2022/6/16 23:46
     * @Version 1.0
     */
    public class TestIoc {
    
        public static void main(String[] args) {
            UserService userService = new UserServiceImpl();
            userService.getUser();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    运行里面的 main 方法,控制台输出下面的结果

    在这里插入图片描述
    以上是比较原始的 java 实现方式,当没有需求变更时,没有任何问题,但是,需求总是在变化的。

    1.2 由需求变更推导 IOC 设计思想

    在前面的实现例子中可以发现,客户端调用的实际是 service 层的接口,与 dao 层并不直接接触。

    这时候,如果有一个新的需求过来,要查询小明这个用户的信息,该怎么做?

    按以往的开发方式,我们肯定是在 dao 层新增一个实现类(如 XiaoMingUserDaoImpl.java),实现小明这个用户的信息查询,然后更改 service 层的原有实现类,将 userDao 指向 小明这个实现类 new 出来的对象。(即:UserDao userDao = new XiaoMingUserDaoImpl(); )

    最后再由客户端调用,得到结果。

    上面的这种方式,会有这样的问题,需求每变更一次,都要同时更改 dao 层、service 层的代码,而且调用具体实现方法的控制权在程序或者说是程序员手上,不在调用方手里。这样的方式来应对频繁的需求变更,对程序员来说是非常痛苦非常累的。

    例如,要依次查询小红、小李、小姚……等50个人的用户信息,那代码得改得多幸苦!

    那有没有什么方法改进,减少工作量的?当然有!

    在 java 的类中,对于类的成员变量,一般会有与之对应的 get 或者 set 方法来获取或者修改该成员变量的值。利用这一点,可以对上面的实现方式进行改进。

    实现方法改进

    1. 对于 dao 层的内容,可以先准备好所有的接口实现类,例如 XiaoHongUserDaoImpl.java、XiaoLiUserDaoImpl.java、XiaoYaoUserDaoImpl.java……查询各自对应的结果

    2. 对于 service 层的实现类,利用 set 方法可以改成以下的实现方式

      package com.yuhuofei.service;
      
      import com.yuhuofei.dao.UserDao;
      
      /**
       * @Description
       * @ClassName UserServiceImpl
       * @Author yuhuofei
       * @Date 2022/6/14 22:02
       * @Version 1.0
       */
      public class UserServiceImpl implements UserService {
      
          private UserDao userDao;
      
          //利用 set 方法,按需变更userDao的值,实现值的动态注入
          public void setUserDao(UserDao userDao) {
              this.userDao = userDao;
          }
      
          @Override
          public void getUser() {
              userDao.selectUser();
          }
      }
      
      
      • 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
    3. 测试类的变更

      import com.yuhuofei.dao.XiaoMingUserDaoImpl;
      import com.yuhuofei.service.UserService;
      import com.yuhuofei.service.UserServiceImpl;
      
      /**
      * @Description
      * @ClassName TestIoc
      * @Author yuhuofei
      * @Date 2022/6/16 23:46
      * @Version 1.0
      */
      public class TestIoc {
      
         public static void main(String[] args) {
             UserService userService = new UserServiceImpl();
             //新建小明这个对象
             XiaoMingUserDaoImpl xiaoMingUserDao = new XiaoMingUserDaoImpl();
             //设置UserServiceImpl类中的成员变量userDao的值
             ((UserServiceImpl) userService).setUserDao(xiaoMingUserDao);
             //调用查询方法
             userService.getUser();
         }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24

    执行测试类后,得到的结果如下

    在这里插入图片描述
    如果要继续查询其他人的用户信息,在 dao 层的实现类已经准备好的前提下,只需要改变测试类中 setUserDao() 方法的传参即可,查哪个人的,就传哪个人进去,将调用具体实现方法的控制权转移给了调用方。

    IOC 设计思想说明

    像上面这种,将调用具体实现方法的控制权由程序或者程序员手上转移给调用方的设计思想就是 IOC(控制反转)。对象的创建不再是由程序通过硬编码主动创建,而是由调用方进行传递,程序接收后,执行指定对象下的方法,获取结果。

    这种设计思想,使得程序员不再需要去管理对象的创建,而程序要使用对象,接收调用方传递过来的就好,系统的耦合性进一步降低。

    2. IOC 的本质

    IOC (控制反转) 是一种设计思想,而DI (依赖注入) 只是它的一种实现方式。

    Spring 过程:

    • Spring 容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从 IOC 容器中取出需要的对象。
    • XML 配置Bean时,Bean的定义信息和实现是分离的,而是采用注解的方式将两者合为一体,最新版的Spring已经可以零配置实现IOC,主要是因为Bean的定义信息以注解的形式定义在实现类中。

    控制反转也可以理解为一种方式,是一种通过描述( XML 或注解)并通过第三方去生产或获取特定对象的方式,Spring 中实现控制反转的是 IOC 容器,其实现方法是依赖注入(DI)。

    IOC 的实现方式:

    • DI (依赖注入)
    • XML 配置
    • 注解
  • 相关阅读:
    基于装饰器对通用表格的封装
    【回顾】“双11”首个元宇宙日 中国移动通信联合会元宇宙产业委员会揭牌 《元宇宙产业宣言》发布
    unity 判断平台
    多线程概述(线程创建,方法(等待,通知,加入,睡眠,礼让,中断),上下文切换,死锁,守护线程与用户线程)
    About FreeType slant(italic,oblique) principle and algorithm
    MYSQL——毫秒值和日期类型数据的转换,DATE_SUB的用法
    全开源无加密跨境电商购物网站系统源码(无货源模式+多语言+多货币)
    C语言学生宿舍水电费信息管理系统
    plsql导入dmp文件:使用PL/SQL导入DMP文件,实现数据库的快速迁移
    php反序列化逃逸
  • 原文地址:https://blog.csdn.net/Crezfikbd/article/details/125287582