Spring 手写简易IOC容器

IOC 容器原理

IOC 基本概念

IOC(Inversion of Control,控制反转) 是一种设计思想,指将对象的创建、管理和依赖关系的维护从程序本身转移到外部容器中。传统开发中,对象之间的依赖关系由程序自身控制,而 IOC 容器通过配置信息(如 XML、注解)接管对象的生命周期,实现对象间的松耦合。

核心本质:将 "程序主动创建对象" 转变为 "容器为程序提供对象",控制权发生反转。

IOC 与依赖注入(DI)的关系

  • DI(Dependency Injection,依赖注入) 是 IOC 的具体实现方式,指容器在创建对象时,将其依赖的对象通过参数、属性等方式注入,而非对象自己创建依赖。
  • 三者关系:IOC 是思想,DI 是实现方式,IOC 容器是 DI 的载体。

传统方式与 IOC 方式的完整流程对比

传统三层架构(手动控制)的完整执行流程

java 复制代码
// 1. 定义接口与实现(数据库层)
interface Database {
    Connection getConnection();
}
class MySQLDatabase implements Database {
    @Override
    public Connection getConnection() {
        System.out.println("[MySQLDatabase] 获取数据库连接");
        return null; // 简化示例
    }
}

// 2. 数据访问层(Repository)
class UserRepository {
    private Database db;
    public UserRepository(Database db) {
        this.db = db;
        System.out.println("[UserRepository] 初始化,依赖 Database");
    }
    
    public void saveUser(String username) {
        System.out.println("[UserRepository] 执行 SQL: INSERT INTO user (name) VALUES ('" + username + "')");
        Connection conn = db.getConnection();
        // 实际会执行 SQL 操作
    }
}

// 3. 业务逻辑层(Service)
class UserService {
    private UserRepository repo;
    public UserService(UserRepository repo) {
        this.repo = repo;
        System.out.println("[UserService] 初始化,依赖 UserRepository");
    }
    
    public void createUser(String username) {
        System.out.println("[UserService] 校验用户合法性: " + username);
        // 业务逻辑(如参数校验、权限检查)
        repo.saveUser(username);
        System.out.println("[UserService] 用户创建成功");
    }
}

// 4. 控制层(Controller)
class UserController {
    private UserService service;
    public UserController(UserService service) {
        this.service = service;
        System.out.println("[UserController] 初始化,依赖 UserService");
    }
    
    public void handleRequest(String action, String username) {
        System.out.println("[UserController] 接收到请求: " + action);
        if ("createUser".equals(action)) {
            service.createUser(username);
        }
    }
}

// 5. 传统方式主程序
public class TraditionalApp {
    public static void main(String[] args) {
        System.out.println("=== 传统方式:手动创建对象链 ===");
        
        // 手动创建对象,层级依赖关系:Controller -> Service -> Repository -> Database
        Database db = new MySQLDatabase();
        UserRepository repo = new UserRepository(db);
        UserService service = new UserService(repo);
        UserController controller = new UserController(service);
        
        // 调用方法,触发完整流程
        controller.handleRequest("createUser", "张三");
        
        System.out.println("=== 传统方式执行完毕 ===");
    }
}
执行结果与流程解析
java 复制代码
=== 传统方式:手动创建对象链 ===
[MySQLDatabase] 获取数据库连接  // Database 构造函数执行
[UserRepository] 初始化,依赖 Database  // Repository 初始化
[UserService] 初始化,依赖 UserRepository  // Service 初始化
[UserController] 初始化,依赖 UserService  // Controller 初始化
[UserController] 接收到请求: createUser  // 控制器接收请求
[UserService] 校验用户合法性: 张三  // 服务层业务逻辑
[UserRepository] 执行 SQL: INSERT INTO user (name) VALUES ('张三')  // 数据层操作
[MySQLDatabase] 获取数据库连接  // Repository 调用 Database 方法
[UserService] 用户创建成功  // 服务层完成
=== 传统方式执行完毕 ===
核心特点
  • 对象创建顺序:从底层(Database)到顶层(Controller)依次创建,耦合紧密。
  • 方法调用链:Controller → Service → Repository → Database,层级清晰但无法解耦。
  • 问题暴露 :若更换为 Oracle 数据库,需修改 UserRepository 的构造函数(传入 OracleDatabase),并修改 TraditionalAppdb 的创建逻辑。

IOC 方式(容器管理)的完整执行流程

java 复制代码
// 1. 接口与实现类(同上,不变)
// (Database/MySQLDatabase/UserRepository/UserService/UserController 代码不变)

// 2. 配置类(告知容器如何创建 Bean)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean
    public Database database() {
        System.out.println("[AppConfig] 配置 Database Bean: MySQLDatabase");
        return new MySQLDatabase();
    }
    
    @Bean
    public UserRepository userRepository(Database db) {
        System.out.println("[AppConfig] 配置 UserRepository Bean");
        return new UserRepository(db);
    }
    
    @Bean
    public UserService userService(UserRepository repo) {
        System.out.println("[AppConfig] 配置 UserService Bean");
        return new UserService(repo);
    }
    
    @Bean
    public UserController userController(UserService service) {
        System.out.println("[AppConfig] 配置 UserController Bean");
        return new UserController(service);
    }
}

// 3. IOC 方式主程序
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class IOCApp {
    public static void main(String[] args) {
        System.out.println("=== IOC 方式:容器自动创建对象链 ===");
        
        // 1. 创建容器并加载配置
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println("[IOCApp] 容器初始化完成");
        
        // 2. 从容器获取 Controller
        UserController controller = context.getBean(UserController.class);
        
        // 3. 调用方法,触发完整流程
        controller.handleRequest("createUser", "张三");
        
        System.out.println("=== IOC 方式执行完毕 ===");
    }
}
执行结果与流程解析
java 复制代码
=== IOC 方式:容器自动创建对象链 ===
[AppConfig] 配置 Database Bean: MySQLDatabase  // 容器解析配置,先创建 Database
[AppConfig] 配置 UserRepository Bean  // 再创建 Repository(依赖已注入)
[AppConfig] 配置 UserService Bean  // 创建 Service
[AppConfig] 配置 UserController Bean  // 创建 Controller
[IOCApp] 容器初始化完成  // 容器初始化完毕
[UserController] 接收到请求: createUser  // 控制器接收请求
[UserService] 校验用户合法性: 张三  // 服务层业务逻辑
[UserRepository] 执行 SQL: INSERT INTO user (name) VALUES ('张三')  // 数据层操作
[MySQLDatabase] 获取数据库连接  // Repository 调用 Database 方法
[UserService] 用户创建成功  // 服务层完成
=== IOC 方式执行完毕 ===
核心特点
  • 对象创建顺序:容器根据依赖关系自动推导创建顺序(与传统方式相同,但由容器控制)。
  • 配置驱动AppConfig 中的 @Bean 方法定义了 Bean 的创建逻辑,业务代码与配置解耦。
  • 动态替换能力 :若更换为 Oracle 数据库,只需修改 AppConfig 中的 database() 方法。
java 复制代码
@Bean
public Database database() {
    return new OracleDatabase(); // 替换为 Oracle 实现
}

业务代码(UserRepository/UserService/Controller)无需任何修改。

两种方式的核心差异对比表

对比维度 传统方式(手动控制) IOC 方式(容器管理)
对象创建位置 主程序(main方法)中手动创建 容器根据配置自动创建
依赖关系维护 代码中硬编码依赖(如 new MySQLDatabase() 配置中声明依赖(如 @Bean public UserService(UserRepository repo)
耦合度 高(修改底层实现需层层修改上层代码) 低(仅修改配置即可替换实现)
方法调用透明度 直接调用,流程清晰但耦合紧密 容器隐藏对象创建细节,调用流程相同但底层解耦
扩展性(如更换数据库) 修改 main方法和依赖类构造函数 仅修改配置类中的 @Bean方法
测试成本 需手动创建完整依赖链,难以隔离测试 可通过配置注入 Mock 对象,轻松隔离依赖

IOC 方式的动态性演示(更换数据库实现)

新增 Oracle 数据库实现
java 复制代码
class OracleDatabase implements Database {
    @Override
    public Connection getConnection() {
        System.out.println("[OracleDatabase] 获取 Oracle 数据库连接");
        return null;
    }
}
修改配置类(仅改一行代码)
java 复制代码
@Bean
public Database database() {
    return new OracleDatabase(); // 改为 Oracle 实现
    // 原代码:return new MySQLDatabase();
}
执行结果(IOCApp 不变)
plain 复制代码
=== IOC 方式:容器自动创建对象链 ===
[AppConfig] 配置 Database Bean: OracleDatabase  // 注意此处变为 Oracle
[AppConfig] 配置 UserRepository Bean  
[AppConfig] 配置 UserService Bean  
[AppConfig] 配置 UserController Bean  
[IOCApp] 容器初始化完成  
[UserController] 接收到请求: createUser  
[UserService] 校验用户合法性: 张三  
[UserRepository] 执行 SQL: INSERT INTO user (name) VALUES ('张三')  
[OracleDatabase] 获取 Oracle 数据库连接  // 此处变为 Oracle 实现
[UserService] 用户创建成功  
=== IOC 方式执行完毕 ===
关键结论
  • IOC 容器通过 "配置抽象" 隔离了底层实现,业务代码对数据库变更无感知。
  • 传统方式需要修改 2 处代码(main 方法和 UserRepository 构造函数),IOC 方式仅需修改配置类 1 处。

总结:从代码执行流程看 IOC 的本质

  • 传统方式:开发者像 "手工组装机器",每个零件(对象)的创建和连接都需亲自处理,一旦某个零件更换,整台机器都需拆解重组。
  • IOC 方式:开发者像 "设计机器图纸",告诉容器 "需要什么零件" 和 "如何连接",容器自动组装机器,更换零件时只需修改图纸,机器运行逻辑不变。

通过完整的方法调用追踪可以看到,IOC 容器并未改变程序的业务执行流程(Controller → Service → Repository),而是将 "对象如何创建" 和 "依赖如何连接" 的控制权从代码转移到配置,从而实现了松耦合和可扩展性。这正是 "控制反转" 的核心价值 ------ 让程序逻辑专注于 "做什么",而不是 "如何做"。

简易代码实现

定义注解

java 复制代码
/**
 * 依赖注入注解
 *
 * @author song
 * @date 2025/06/20
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.CONSTRUCTOR})
public @interface MyAutowired {

}

/**
 * 组件扫描注解
 *
 * @author song
 * @date 2025/06/20
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyComponent {

    String value() default "";
}

/**
 * 包扫描注解
 *
 * @author song
 * @date 2025/06/20
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyComponentScan {

    String value();
}

/**
 * 包扫描注解
 *
 * @author song
 * @date 2025/06/20
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyComponentScan {

    String value();
}

/**
 * 初始化方法注解
 *
 * @author song
 * @date 2025/06/20
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyPostConstruct {

}

容器生命周期

java 复制代码
package com.ssh.wlb.modules.spring.context;

// 1. 注解导入

import com.ssh.wlb.modules.spring.annotation.MyAutowired;
import com.ssh.wlb.modules.spring.annotation.MyComponent;
import com.ssh.wlb.modules.spring.annotation.MyComponentScan;
import com.ssh.wlb.modules.spring.annotation.MyConfiguration;
import com.ssh.wlb.modules.spring.annotation.MyPostConstruct;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import lombok.Data;

/**
 * 自定义Spring式IoC容器实现 核心功能:
 * 1. 依赖注入(@MyAutowired)
 * 2. 循环依赖解决(二级缓存)
 * 3. 生命周期回调(@MyPostConstruct)
 * 4. 组件扫描(@MyComponentScan)
 */
public class MyApplicationContext {

    // ========================= 核心存储结构 =========================
    // [作用] 存储Bean元数据(类型/作用域)
    private final Map<String, BeanDefinition> beanDefinitions = new HashMap<>();

    // [作用] 一级缓存(完整Bean)
    // [特性] 存储已完成初始化的单例Bean
    private final Map<String, Object> singletonPool = new HashMap<>();

    // [作用] 二级缓存(半成品Bean)
    // [特性] 解决循环依赖的关键组件
    private final Map<String, Object> earlySingletonPool = new HashMap<>();

    // ========================= 容器初始化 =========================

    /**
     * 构造函数
     *
     * @param configClass 配置类(需带@MyConfiguration注解) 执行流程:
     *                    1. 解析配置类 → 2. 扫描Bean定义 → 3. 预实例化单例
     */
    public MyApplicationContext(Class<?> configClass) {
        scanConfiguration(configClass);    // 步骤1:配置解析
        preInstantiateSingletons();        // 步骤2:单例预加载
    }

    // ========================= Bean定义模型 =========================

    /**
     * Bean元数据容器 存储信息:
     * - type: Bean的Class对象
     * - singleton: 是否单例作用域
     */
    @Data
    static class BeanDefinition {

        private Class<?> type;
        private boolean singleton;
    }

    // ========================= 配置解析阶段 =========================

    /**
     * 解析配置类 关键步骤:
     * 1. 验证@MyConfiguration注解
     * 2. 获取@ComponentScan路径
     * 3. 启动包扫描
     */
    private void scanConfiguration(Class<?> configClass) {
        // 1. 配置类注解校验
        if (!configClass.isAnnotationPresent(MyConfiguration.class)) {
            throw new RuntimeException("配置类缺少@MyConfiguration注解: " + configClass.getName());
        }

        // 2. 扫描路径提取
        String scanPath = "";
        if (configClass.isAnnotationPresent(MyComponentScan.class)) {
            scanPath = configClass.getAnnotation(MyComponentScan.class).value();
        }

        // 3. 启动递归扫描
        scanPackage(scanPath);
    }

    // ========================= 组件扫描阶段 =========================

    /**
     * 包扫描入口 执行流程:
     * 1. 包路径转文件路径 → 2. 获取类加载器资源 → 3. 启动目录扫描
     */
    private void scanPackage(String basePackage) {
        // 路径格式转换 (com.example → com/example)
        String path = basePackage.replace(".", "/");

        try {
            // 获取类路径资源
            Enumeration<URL> resources = Thread.currentThread()
                .getContextClassLoader()
                .getResources(path);

            // 遍历所有资源目录
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                scanDirectory(basePackage, new File(resource.getFile()));
            }
        } catch (IOException e) {
            throw new RuntimeException("包扫描失败: " + basePackage, e);
        }
    }

    /**
     * 递归扫描目录 处理逻辑:
     * - 目录 → 递归扫描子目录
     * - .class文件 → 注册Bean定义
     */
    private void scanDirectory(String currentPackage, File dir) {
        for (File file : Objects.requireNonNull(dir.listFiles())) {
            if (file.isDirectory()) {
                // 递归处理子目录: 包名追加目录名
                scanDirectory(currentPackage + "." + file.getName(), file);
            } else if (file.getName().endsWith(".class")) {
                // 类文件处理: 去除.class后缀
                String className = currentPackage + "." +
                    file.getName().replace(".class", "");
                registerBeanDefinition(className);
            }
        }
    }

    // ========================= Bean注册阶段 =========================

    /**
     * 注册Bean定义 核心逻辑:
     * 1. 类加载 → 2. @MyComponent注解过滤 → 3. Bean命名规则
     */
    private void registerBeanDefinition(String className) {
        try {
            Class<?> clazz = Class.forName(className);

            // 仅处理带@MyComponent注解的类
            if (clazz.isAnnotationPresent(MyComponent.class)) {
                BeanDefinition bd = new BeanDefinition();
                bd.setType(clazz);
                bd.setSingleton(true); // 默认单例作用域

                // Bean命名规则:
                // 1. 优先使用注解指定名称
                // 2. 默认首字母小写类名
                String beanName = clazz.getAnnotation(MyComponent.class).value();
                if (beanName.isEmpty()) {
                    beanName = clazz.getSimpleName().substring(0, 1).toLowerCase() +
                        clazz.getSimpleName().substring(1);
                }

                // 防重复注册
                if (!beanDefinitions.containsKey(beanName)) {
                    beanDefinitions.put(beanName, bd);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("注册Bean定义失败: " + className, e);
        }
    }

    // ========================= Bean实例化阶段 =========================

    /**
     * 预实例化单例Bean 执行策略:遍历所有Bean定义,初始化单例作用域Bean
     */
    private void preInstantiateSingletons() {
        beanDefinitions.keySet().forEach(beanName -> {
            BeanDefinition bd = beanDefinitions.get(beanName);
            if (bd != null && bd.isSingleton()) {
                getBean(beanName); // 触发初始化
            }
        });
    }

    /**
     * 获取Bean实例(核心入口) 三级缓存查询顺序:
     * 1. 一级缓存(完整Bean) → 2. 二级缓存(半成品) → 3. 新建实例
     */
    public Object getBean(String beanName) {
        // 1. 一级缓存查询
        if (singletonPool.containsKey(beanName)) {
            return singletonPool.get(beanName);
        }

        // 2. 二级缓存查询(解决循环依赖)
        if (earlySingletonPool.containsKey(beanName)) {
            return earlySingletonPool.get(beanName);
        }

        // 3. 安全校验
        if (!beanDefinitions.containsKey(beanName)) {
            throw new RuntimeException("未找到Bean定义: " + beanName);
        }

        // 4. 创建新实例
        Object instance = createBean(beanName);

        // 5. 单例Bean注册
        BeanDefinition bd = beanDefinitions.get(beanName);
        if (bd != null && bd.isSingleton()) {
            singletonPool.put(beanName, instance);
            earlySingletonPool.remove(beanName); // 关键:移出二级缓存
        }

        return instance;
    }

    /**
     * 创建Bean实例 四步生命周期:
     * 1. 实例化 → 2. 提前暴露 → 3. 依赖注入 → 4. 初始化回调
     */
    private Object createBean(String beanName) {
        BeanDefinition bd = beanDefinitions.get(beanName);

        // 防御性检查
        if (bd == null) {
            throw new RuntimeException("Bean定义不存在: " + beanName);
        }

        try {
            // [阶段1] 反射实例化
            Object instance = bd.getType().getDeclaredConstructor().newInstance();

            // [阶段2] 提前暴露半成品(解决循环依赖)
            earlySingletonPool.put(beanName, instance);

            // [阶段3] 依赖注入
            for (Field field : bd.getType().getDeclaredFields()) {
                if (field.isAnnotationPresent(MyAutowired.class)) {
                    // 突破私有访问限制
                    field.setAccessible(true);
                    // 递归依赖解析
                    Object fieldBean = getBean(field.getName());
                    field.set(instance, fieldBean);
                }
            }

            // [阶段4] 初始化回调
            invokeInitMethods(instance, bd.getType());

            return instance;
        } catch (Exception e) {
            // 异常时清理二级缓存
            earlySingletonPool.remove(beanName);
            throw new RuntimeException("创建Bean失败: " + beanName, e);
        }
    }

    // ========================= 生命周期回调 =========================

    /**
     * 执行初始化方法 过滤条件:带@MyPostConstruct注解的方法
     */
    private void invokeInitMethods(Object bean, Class<?> beanClass) {
        for (Method method : beanClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(MyPostConstruct.class)) {
                try {
                    // 突破私有访问限制
                    method.setAccessible(true);
                    // 执行初始化逻辑
                    method.invoke(bean);
                } catch (Exception e) {
                    throw new RuntimeException("初始化方法执行失败: " + method.getName(), e);
                }
            }
        }
    }
}

容器初始化流程图

循环依赖解决序列图

Bean生命周期状态图

stateDiagram-v2 [*] --> 未实例化 未实例化 --> 实例化中: getBean调用 实例化中 --> 半成品: 加入二级缓存 半成品 --> 依赖注入中: 开始注入 依赖注入中 --> 初始化中: 开始初始化 初始化中 --> 完整Bean: 加入一级缓存 完整Bean --> [*]: 可被获取 note right of 半成品 循环依赖解决方案: 当BeanB需要BeanA时, 直接返回半成品BeanA end note

缓存机制图解

graph TD A[1. 获取Bean定义] --> B[2. 实例化] B --> C[3. 加入二级缓存] C --> D[4. 依赖注入] D --> E{需要其他Bean?} E -->|是| F[递归getBean] E -->|否| G[5. 执行初始化] F -->|返回半成品/完整Bean| D G --> H[6. 移入一级缓存] H --> I[7. 清理二级缓存] I --> J[8. 返回完整Bean]

容器使用

java 复制代码
@MyConfiguration
@MyComponentScan("com.ssh.wlb.modules.spring")
public class AppConfig {
    // 配置类:指定组件扫描路径
}

@MyComponent
public class UserService {
    @MyAutowired
    private UserRepository userRepository;  // 需要依赖注入的字段

    @MyPostConstruct
    public void init() {
        System.out.println("UserService初始化完成");  // 初始化回调
    }

    public void showUser() {
        userRepository.showUser();  // 业务方法
    }
}

@MyComponent
public class UserRepository {
    @MyPostConstruct
    public void init() {
        System.out.println("UserRepository初始化完成");  // 初始化回调
    }

    public void showUser() {
        System.out.println("UserRepository查询用户");  // 业务方法
    }
}

public class Test {
    public static void main(String[] args) {
        // 12. 创建容器 - 触发整个初始化流程
        MyApplicationContext ctx = new MyApplicationContext(AppConfig.class);
        /* 
          容器创建流程:
          1. 解析配置类,获取扫描路径
          2. 扫描包路径,识别@MyComponent类
          3. 创建Bean定义(UserService, UserRepository)
          4. 初始化一级/二级缓存
        */

        // 12.1 按名称获取Bean - 触发UserService初始化
        UserService service = (UserService) ctx.getBean("userService");
        /*
          getBean("userService")流程:
          1. 检查一级缓存 → 未找到
          2. 创建UserService实例(步骤2:实例化)
          3. 将半成品UserService放入二级缓存(步骤3)
          4. 发现@MyAutowired字段 → 触发UserRepository创建
        */

        // 13. 调用业务方法
        service.showUser();  // 输出:"UserRepository查询用户"
    }
}

完整执行流程图解

缓存状态变化图解

plain 复制代码
时间线         | 触发操作                  | 一级缓存内容          | 二级缓存内容                | 关键状态说明
--------------|---------------------------|----------------------|----------------------------|-------------------------------------------
T0 (容器启动) | 扫描注册Bean定义           | ∅                    | ∅                          | 加载UserService/UserRepository定义
              |                           |                      |                            |
T1 (获取Bean) | getBean("userService")     | ∅                    | ∅                          | 开始创建UserService
              | ↓                          |                      |                            |
T2            | UserService实例化          | ∅                    | userService(半成品)        | 对象已创建(userRepository=null)
              | ↓                          |                      |                            |
T3            | 发现@MyAutowired依赖       | ∅                    | userService(半成品)        | 需要注入userRepository
              | ↓                          |                      |                            |
T4            | getBean("userRepository")  | ∅                    | userService(半成品)        | 开始创建UserRepository
              | ↓                          |                      |                            |
T5            | UserRepository实例化       | ∅                    | userService(半成品)        | 
              |                           |                      | userRepository(半成品)     | UserRepository对象创建完成
              | ↓                          |                      |                            |
T6            | UserRepository初始化       | userRepository(成品) | userService(半成品)        | 执行@MyPostConstruct
              | (移入一级缓存)             |                      |                            | 控制台输出:"UserRepository初始化完成"
              | ↓                          |                      |                            |
T7            | 注入UserService           | userRepository(成品) | userService(半成品)        | userService.userRepository已赋值
              | ↓                          |                      |                            |
T8            | UserService初始化         | userRepository(成品) | ∅                          | 执行@MyPostConstruct
              | (移入一级缓存)             | userService(成品)    |                            | 控制台输出:"UserService初始化完成"
              | ↓                          |                      |                            |
T9 (业务调用) | service.showUser()        | userService(成品)    | ∅                          | 调用链:userService → userRepository
              |                           | userRepository(成品) |                            | 控制台输出:"UserRepository查询用户"

关键变化节点图解

graph LR A[容器启动] --> B[实例化UserService] B --> C[加入二级缓存] C --> D[发现userRepository依赖] D --> E[实例化UserRepository] E --> F[加入二级缓存] F --> G[初始化UserRepository] G --> H[移入一级缓存] H --> I[从二级缓存移除] I --> J[注入UserService] J --> K[初始化UserService] K --> L[移入一级缓存] L --> M[从二级缓存移除] M --> N[返回完整Bean]

关键点说明

  1. 二级缓存的核心作用
    • T2-T8 期间保存userService半成品
    • T5-T6 期间保存userRepository半成品
    • 解决循环依赖问题:当其他Bean需要注入userService时,可以从二级缓存获取半成品
  2. 状态转换点
    • 实例化 → 半成品:对象被创建但未初始化(构造器执行完成)
    • 依赖注入 → 半成品增强:对象依赖已满足,但未执行初始化方法
    • 初始化 → 成品 :执行完@PostConstruct方法,进入可用状态
  3. 缓存清理时机
    • UserRepository在 T6 初始化后立即移出二级缓存
    • UserService在 T8 初始化后移出二级缓存
    • 最终二级缓存保持为空(单例模式下)
相关推荐
寻月隐君4 分钟前
想用 Rust 开发游戏?这份超详细的入门教程请收好!
后端·rust·github
晴空月明17 分钟前
分布式系统高可用性设计 - 缓存策略与数据同步机制
后端
Real_man1 小时前
新物种与新法则:AI重塑开发与产品未来
前端·后端·面试
小马爱打代码2 小时前
Spring Boot:将应用部署到Kubernetes的完整指南
spring boot·后端·kubernetes
卜锦元2 小时前
Go中使用wire进行统一依赖注入管理
开发语言·后端·golang
SoniaChen333 小时前
Rust基础-part3-函数
开发语言·后端·rust
全干engineer3 小时前
Flask 入门教程:用 Python 快速搭建你的第一个 Web 应用
后端·python·flask·web
William一直在路上4 小时前
SpringBoot 拦截器和过滤器的区别
hive·spring boot·后端
小马爱打代码5 小时前
Spring Boot 3.4 :@Fallback 注解 - 让微服务容错更简单
spring boot·后端·微服务
曾曜5 小时前
PostgreSQL逻辑复制的原理和实践
后端