Spring核心之IOC与DI:手写工厂到Spring容器演进

一、Spring框架简介

Spring是一个轻量级的、开源的JavaEE全栈式应用框架。它的核心价值在于简化企业级应用开发,通过提供一系列模块化解决方案,让开发者能够更专注于业务逻辑。

Spring的核心优势:

  1. IOC(控制反转):将对象的创建、管理与依赖关系的控制权从程序代码"反转"到Spring容器,实现解耦。

  2. AOP(面向切面编程):允许将遍布应用的公共行为(如日志、事务)从核心业务逻辑中分离,增强模块化。

  3. 粘合剂:它就像一个"框架的框架",能够非常方便地整合MyBatis、Hibernate、Struts等其他优秀的第三方技术与框架。

二、IOC(控制反转):从工厂模式到Spring容器

2.1 问题起源:耦合

在传统编码中,对象间的依赖关系通常是"硬编码"的。例如,在Service层中直接new一个Dao层的实现类。这种强耦合导致代码僵化,一旦需要更换实现,就必须修改源代码。

2.2 IOC思想

  • 正转 :程序员在代码中主动new对象,控制权在程序员手中。

  • 反转(IOC):对象的创建不再由程序员控制,而是交给一个"工厂"或"容器"来负责。程序员从主动创建变为被动接收对象。

核心控制对象的创建权发生反转

2.3 手工实现IOC:工厂模式

在接触Spring前,我们可以用"工厂+反射+配置文件"模拟IOC思想,解耦Service和Dao。

实现步骤:

  1. 定义配置文件 ​ (bean.properties):将类名以键值对形式存储。

    复制代码
    userDao=com.hg.dao.UserDaoImpl
    userService=com.hg.service.UserServiceImpl
  2. 创建工厂类:在静态代码块中读取配置文件。

  3. 反射创建对象 :通过Class.forName(className).newInstance()创建对象实例,并存入一个Map容器。

  4. 提供获取方法 :对外提供getBean(String id)方法,从Map中返回所需对象。

此时,Service中不再出现new UserDaoImpl()的代码,而是从工厂获取,初步实现解耦。

2.4 Spring的IOC容器

Spring将上述工厂模式的思想发挥到极致,提供了一个功能强大的IOC容器。

Spring IOC 入门步骤:

  1. 引入依赖 ​ (pom.xml)

    复制代码
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.23</version> <!-- 请使用合适版本 -->
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.36</version>
    </dependency>
  2. 准备日志配置 ​ (log4j.properties)

  3. 编写Spring配置文件 ​ (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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- bean标签:将一个类的对象交给Spring IOC容器管理 -->
        <!-- id: 对象在容器中的唯一标识 (Map的key) -->
        <!-- class: 对象的全限定类名 -->
        <bean id="userDao" class="com.hg.dao.UserDaoImpl"></bean>
        <bean id="userService" class="com.hg.service.UserServiceImpl"></bean>
    </beans>
  4. 从容器中获取Bean

    复制代码
    public class Test {
        public static void main(String[] args) {
            // 1. 加载Spring配置文件,初始化IOC容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 2. 从容器中获取Bean对象
            UserService userService = ac.getBean("userService", UserService.class);
            // 3. 使用对象
            userService.saveUser();
        }
    }

三、DI(依赖注入)

IOC解决了对象创建的问题。**DI(依赖注入)**​ 则专门解决对象间的依赖关系赋值问题,它是IOC思想的具体实现。

3.1 构造方法注入

通过类的构造方法来完成依赖的注入。

  1. 目标类提供带参构造方法

    复制代码
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
        private String msg;
        // 构造方法
        public UserServiceImpl(UserDao userDao, String msg) {
            this.userDao = userDao;
            this.msg = msg;
        }
    }
  2. 在配置文件中使用<constructor-arg>标签注入

    复制代码
    <bean id="userService" class="com.hg.service.UserServiceImpl">
        <!-- name: 通过构造方法参数名匹配 -->
        <constructor-arg name="userDao" ref="userDao"/>
        <!-- index: 通过构造方法参数索引位置匹配 (从0开始) -->
        <constructor-arg index="1" value="示例文本"/>
    </bean>
    <bean id="userDao" class="com.hg.dao.UserDaoImpl"/>
    • ref:引用IOC容器中其他Bean的id。

    • value:注入基本数据类型或String类型的值。

3.2 Setter方法注入(最常用)

通过类的Setter方法来完成依赖注入。

  1. 目标类提供Setter方法

    复制代码
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
        private String msg;
        // Setter 方法
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
        public void setMsg(String msg) {
            this.msg = msg;
        }
    }
  2. 在配置文件中使用<property>标签注入

    复制代码
    <bean id="userService" class="com.hg.service.UserServiceImpl">
        <!-- name: 取set方法名去掉"set"后首字母小写的部分,例如setUserDao -> userDao -->
        <property name="userDao" ref="userDao"/>
        <property name="msg" value="示例文本"/>
    </bean>
    <bean id="userDao" class="com.hg.dao.UserDaoImpl"/>

3.3 自动注入(Autowire)

让Spring容器自动寻找匹配的Bean并完成注入,无需在<property><constructor-arg>中显式配置。

byType方式 :根据属性的类型 ,在IOC容器中查找匹配的Bean。要求容器中同类型的Bean必须唯一

复制代码
<bean id="userService" class="com.hg.service.UserServiceImpl" autowire="byType"/>
<bean id="userDao" class="com.hg.dao.UserDaoImpl"/>
<!-- 注意:不能再有另一个类型为UserDao的bean,否则会报错 -->

原理 :Spring会遍历IOC容器中所有的Bean,检查其类型是否与UserServiceImpl中需要注入的属性(如UserDao)类型匹配,如果找到且唯一,则自动注入。

总结

  • IOC​ 是一种设计思想,将对象的控制权交给容器,实现解耦。

  • DI​ 是IOC的一种实现方式,由容器在运行时动态地将依赖关系注入到组件中。

  • **在Spring中,IOC容器通过读取配置(XML或注解)来管理Bean及其依赖关系,DI则负责建立Bean之间的关联。**​ 从手写工厂到使用Spring容器,我们看到了这一思想从概念到强大工业级实现的演进过程。

相关推荐
xiaodaidai丶3 小时前
Spring Web MVC的异步请求解读
spring boot·spring·mvc
PPPPickup3 小时前
easymall---人工客服(SpringAI版)无敌复用框架!
spring·ai编程
Thomas.Sir3 小时前
SpringMVC 工作原理深入解析
spring·设计模式·mvc·spring mvc
sanggou3 小时前
Spring Cloud负载均衡组件到底是哪一个?
spring·spring cloud·负载均衡
xiaoye37083 小时前
Spring Bean 生命周期自定义扩展示例
java·spring boot·spring
弹简特3 小时前
【JavaEE17-后端部分】 MyBatis 入门第一篇:准备工作与第一个查询
spring boot·spring·mybatis
Voyager_44 小时前
吃透设计模式:从原理到落地(如何选型),Java/Spring开发场景
java·spring·设计模式
6+h5 小时前
【Spring】深度剖析AOP
java·后端·spring
计算机学姐6 小时前
基于SpringBoot的宠物诊所管理系统
java·vue.js·spring boot·后端·spring·elementui·宠物