深入理解IoC容器:一级缓存与二级缓存的设计与实现

引言

在现代Java开发中,IoC(控制反转)容器是Spring框架的核心组件之一。它通过依赖注入(DI)机制,帮助我们管理对象的生命周期和依赖关系,极大地提升了代码的可维护性和可扩展性。然而,IoC容器的底层实现并不简单,尤其是当涉及到循环依赖缓存机制时,其设计思路和实现细节往往令人感到困惑。

你是否曾经好奇过:

  • IoC容器是如何管理Bean的实例化和依赖注入的?
  • 为什么需要一级缓存和二级缓存?
  • 二级缓存是如何解决循环依赖问题的?

本文将通过手动实现一个简单的IoC容器 ,逐步揭开这些问题的答案。我们将从一级缓存 的设计开始,逐步引入二级缓存,并通过代码示例详细讲解其工作原理。无论你是初学者还是有一定经验的开发者,相信这篇文章都能帮助你更深入地理解IoC容器的底层逻辑。


IoC容器的核心功能

在开始之前,我们先回顾一下IoC容器的核心功能:

  • Bean的扫描与注册 :通过类加载器扫描指定包下的类,并根据注解(如@Component@Service)识别Bean。
  • Bean的实例化与依赖注入 :通过反射机制实例化Bean,并通过@Autowired注解实现依赖注入。
  • Bean的生命周期管理:通过缓存机制管理单例Bean,确保每个Bean只被实例化一次。

我们需要实现的功能是为了更好地理解这些功能,我们将通过一个具体的示例来逐步实现一个简单的IoC容器。假设我们有一个DemoApplication类,它的功能如下:

java 复制代码
public class DemoApplication {
    public static void main(String[] args) throws Exception {
        // 启动IoC容器
        Application application = Application.start(DemoApplication.class);

        // 通过名称获取Bean
        UserService userService = application.getBean("UserServiceImpl");
        userService.say();

        // 通过类型获取Bean
        application.getBean(UserService.class).say();
    }
}

//用户服务
public interface UserService {
    void say();
}

//用户服务的实现
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private OrderService orderService;

    @Override
    public void say() {
        System.out.println("hello");
        orderService.say();
    }
}

//订单服务
public interface OrderService {
    void say();
}

//订单服务的实现
@Service
public class OrderServiceImpl implements OrderService {


    @Override
    public void say() {
        System.out.println("hello OrderService");
    }
}

为了实现这个功能,我们需要完成以下步骤:

  • 扫描指定包下的类 ,识别带有@Component@Service注解的类。
  • 实例化这些类,并将它们存储在容器中。
  • 实现依赖注入 ,通过@Autowired注解自动注入依赖。
  • 提供获取Bean的方法,支持通过名称和类型获取Bean。

接下来,我们将逐步实现这些功能。

java 复制代码
//首先我们需要几个注解
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired{
}
package org.example.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Component {
}
package org.example.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    String value() default "";
}

一级缓存的设计与实现

一级缓存的作用

一级缓存(singletonObjects)是一个ConcurrentHashMap,用于存储已经完全初始化的单例Bean。它的作用是:

  • 确保每个Bean只被实例化一次。
  • 提供快速访问Bean实例的能力。

一级缓存的实现

java 复制代码
package org.example.framework;

import org.example.annotation.Autowired;
import org.example.annotation.Component;
import org.example.annotation.Service;

import javax.annotation.*;
import java.io.File;
import java.lang.annotation.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 简单的IoC容器实现类,支持Bean的扫描、实例化、依赖注入和缓存管理。
 */
public class Application1 {

    // 存储Bean实例的容器
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    // 存储每个类型对应的Bean名称列表,用于支持按类型获取Bean
    private Map<Class<?>, List<String>> allBeanNamesByType = new ConcurrentHashMap<>();

    /**
     * 启动IoC容器。
     *
     * @param aClass 启动类的Class对象,用于确定扫描的包路径
     * @return Application1实例
     * @throws Exception 如果启动过程中发生异常
     */
    public static Application1 start(Class aClass) throws Exception {
        // 创建Application1实例并调用run方法
        return new Application1().run(aClass);
    }

    /**
     * 运行IoC容器,扫描包路径并初始化Bean。
     *
     * @param aClass 启动类的Class对象
     * @return Application1实例
     * @throws Exception 如果运行过程中发生异常
     */
    private Application1 run(Class aClass) throws Exception {
        // 获取包路径并转换为文件路径
        String packageName = aClass.getPackage().getName().replace(".", "/");
        // 获取当前线程的类加载器
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        // 获取包路径对应的URL资源
        URL resource = contextClassLoader.getResource(packageName);
        // 获取资源的文件路径
        String path = resource.getPath();
        // 截取路径,去掉"/class"部分
        path = path.substring(0, path.indexOf("/class"));

        // 获取包路径下所有类
        List<Class> classList = getClassNameByFile(path);

        // 初始化带有@Service注解的Bean
        List<Object> serviceBean = constructor(classList.stream()
                .filter(e -> e.isAnnotationPresent(Service.class)) // 过滤出带有@Service注解的类
                .collect(Collectors.toList())); // 转换为List
        serviceInit(serviceBean); // 初始化这些Bean

        return this;
    }

    /**
     * 根据Bean名称获取Bean实例。
     *
     * @param beanName Bean的名称
     * @return Bean实例
     */
    public <T> T getBean(String beanName) {
        // 如果一级缓存中没有该Bean,返回null
        if (!singletonObjects.containsKey(beanName)) {
            return null;
        }
        // 从一级缓存中获取Bean实例
        return (T) singletonObjects.get(beanName);
    }

    /**
     * 根据类型获取Bean实例。
     *
     * @param aClass Bean的类型
     * @return Bean实例
     */
    public <T> T getBean(Class<T> aClass) {
        // 根据类型获取Bean名称
        String beanName = transformedBeanName(aClass);
        // 根据名称获取Bean实例
        T bean = getBean(beanName);
        // 如果找到Bean,直接返回
        if (Objects.nonNull(bean)) {
            return bean;
        }
        // 根据类型获取所有Bean名称
        List<String> list = allBeanNamesByType.get(aClass);
        // 如果没有找到对应的Bean名称,返回null
        if (Objects.isNull(list)) {
            return null;
        }
        // 如果找到多个Bean,抛出异常
        if (list.size() != 1) {
            throw new RuntimeException("找到多个bean");
        }
        // 返回第一个Bean实例
        return getBean(allBeanNamesByType.get(aClass).iterator().next());
    }

    /**
     * 初始化带有@Service注解的Bean。
     *
     * @param serviceBeans 需要初始化的Bean列表
     * @throws Exception 如果初始化过程中发生异常
     */
    public void serviceInit(List<Object> serviceBeans) throws Exception {
        // 如果Bean列表为空,直接返回
        if (serviceBeans.isEmpty()) {
            return;
        }
        // 用于存储未完成初始化的Bean
        List<Object> resultBean = new ArrayList<>();
        for (Object serviceBean : serviceBeans) {
            // 如果依赖注入成功,则将Bean放入一级缓存
            if (autowiredInjection(serviceBean)) {
                singletonObjects.put(transformedBeanName(serviceBean.getClass()), serviceBean);
            } else {
                // 如果依赖注入失败,则将Bean加入待处理列表
                resultBean.add(serviceBean);
            }
        }
        // 递归处理未完成的Bean
        if (!resultBean.isEmpty()) {
            serviceInit(resultBean);
        }
    }

    /**
     * 自动注入依赖。
     *
     * @param bean 需要注入依赖的Bean实例
     * @return 是否注入成功
     * @throws Exception 如果注入过程中发生异常
     */
    public boolean autowiredInjection(Object bean) throws Exception {
        // 获取所有带有@Autowired注解的字段
        List<Field> fieldList = Stream.of(Arrays.asList(bean.getClass().getFields()), Arrays.asList(bean.getClass().getDeclaredFields()))
                .flatMap(Collection::stream) // 将多个列表合并为一个流
                .filter(e -> e.isAnnotationPresent(Autowired.class)) // 过滤出带有@Autowired注解的字段
                .distinct() // 去重
                .collect(Collectors.toList()); // 转换为List

        // 获取所有带有@Autowired注解的方法
        List<Method> methodList = Stream.of(Arrays.asList(bean.getClass().getMethods()), Arrays.asList(bean.getClass().getDeclaredMethods()))
                .flatMap(Collection::stream) // 将多个列表合并为一个流
                .filter(e -> e.isAnnotationPresent(Autowired.class)) // 过滤出带有@Autowired注解的方法
                .distinct() // 去重
                .collect(Collectors.toList()); // 转换为List

        // 注入字段依赖
        for (Field field : fieldList) {
            // 获取字段对应的Bean实例
            Object paramBean = getBeanCache(field.getName(), field.getType());
            // 如果找不到对应的Bean,返回false
            if (Objects.isNull(paramBean)) {
                return false;
            }
            // 设置字段可访问
            field.setAccessible(true);
            // 将Bean实例注入字段
            field.set(bean, paramBean);
        }

        // 注入方法依赖
        for (Method method : methodList) {
            // 存储方法参数
            List<Object> params = new ArrayList<>();
            // 获取方法的所有参数
            Parameter[] parameters = method.getParameters();
            for (Parameter parameter : parameters) {
                // 获取参数对应的Bean实例
                Object parameterBean = getBeanCache(parameter.getName(), parameter.getType());
                // 如果找不到对应的Bean,返回false
                if (Objects.isNull(parameterBean)) {
                    return false;
                }
                // 将Bean实例加入参数列表
                params.add(parameterBean);
            }
            // 设置方法可访问
            method.setAccessible(true);
            // 调用方法,注入参数
            method.invoke(bean, params.toArray());
        }
        return true;
    }

    /**
     * 根据类获取Bean名称。
     *
     * @param aclass 类的Class对象
     * @return Bean的名称
     */
    public String transformedBeanName(Class aclass) {
        // 如果类带有@Service注解
        if (aclass.isAnnotationPresent(Service.class)) {
            Service service = (Service) aclass.getAnnotation(Service.class);
            // 如果@Service注解的value属性不为空,则使用value作为Bean名称
            if (Objects.nonNull(service.value()) && !service.value().isEmpty()) {
                return service.value();
            }
        }
        // 否则使用类的简单名称作为Bean名称
        return aclass.getSimpleName();
    }

    /**
     * 根据名称和类型从缓存中获取Bean实例。
     *
     * @param name  Bean的名称
     * @param clazz Bean的类型
     * @return Bean实例
     * @throws Exception 如果获取过程中发生异常
     */
    private Object getBeanCache(String name, Class clazz) throws Exception {
        // 根据名称从缓存中获取Bean实例
        Object bean = getBeanCache(name);
        // 如果找到Bean,直接返回
        if (Objects.nonNull(bean)) {
            return bean;
        }
        // 根据类型从缓存中获取Bean实例
        return getBeanCache(clazz);
    }

    /**
     * 根据类型从缓存中获取Bean实例。
     *
     * @param clazz Bean的类型
     * @return Bean实例
     * @throws Exception 如果获取过程中发生异常
     */
    public Object getBeanCache(Class clazz) throws Exception {
        // 根据类型的简单名称从缓存中获取Bean实例
        Object bean = getBeanCache(clazz.getSimpleName());
        // 如果找到Bean,直接返回
        if (Objects.nonNull(bean)) {
            return bean;
        }
        // 根据类型获取所有Bean名称
        List<String> list = allBeanNamesByType.get(clazz);
        // 如果没有找到对应的Bean名称,返回null
        if (Objects.isNull(list)) {
            return null;
        }
        // 如果找到多个Bean,抛出异常
        if (list.size() != 1) {
            throw new RuntimeException("找到多个Bean:" + clazz.getName());
        }
        // 返回第一个Bean实例
        return getBeanCache(allBeanNamesByType.get(clazz).iterator().next());
    }

    /**
     * 根据名称从缓存中获取Bean实例。
     *
     * @param name Bean的名称
     * @return Bean实例
     */
    private Object getBeanCache(String name) {
        // 如果一级缓存中有该Bean,返回实例
        if (singletonObjects.containsKey(name)) {
            return singletonObjects.get(name);
        }
        // 否则返回null
        return null;
    }

    /**
     * 实例化Bean。
     *
     * @param classList 需要实例化的类列表
     * @return Bean实例列表
     * @throws Exception 如果实例化过程中发生异常
     */
    public List<Object> constructor(List<Class> classList) throws Exception {
        // 存储未完成实例化的类
        List<Class> resultClass = new ArrayList<>();
        // 存储已实例化的Bean
        List<Object> beans = new ArrayList<>();
        for (Class classes : classList) {
            Constructor defaultConstructor = null;
            Constructor autowriteConstructor = null;
            // 获取类的所有构造函数
            Set<Constructor> constructorSet = new HashSet<>();
            constructorSet.addAll(Arrays.asList(classes.getConstructors()));
            constructorSet.addAll(Arrays.asList(classes.getDeclaredConstructors()));

            // 如果只有一个构造函数,则使用默认构造函数
            if (constructorSet.size() == 1) {
                defaultConstructor = constructorSet.iterator().next();
            }

            // 获取带有@Autowired注解的构造函数
            List<Constructor> constructorList = constructorSet.stream()
                    .filter(e -> e.isAnnotationPresent(Autowired.class))
                    .collect(Collectors.toList());
            // 如果有多个带有@Autowired注解的构造函数,抛出异常
            if (constructorList.size() > 1) {
                throw new RuntimeException("存在多个autowrite" + classes.getSimpleName());
            }
            // 如果只有一个带有@Autowired注解的构造函数,使用它
            if (constructorList.size() == 1) {
                autowriteConstructor = constructorList.get(0);
            }

            Constructor constructor = null;

            // 优先使用带有@Autowired注解的构造函数
            if (Objects.nonNull(autowriteConstructor)) {
                constructor = autowriteConstructor;
            } else if (Objects.nonNull(defaultConstructor)) {
                // 否则使用默认构造函数
                constructor = defaultConstructor;
            }

            Object objectBean = null;
            // 如果没有构造函数,则使用默认无参构造函数
            if (Objects.isNull(constructor)) {
                objectBean = classes.newInstance();
            } else {
                // 构造函数的参数注入
                List<Object> params = new ArrayList<>();
                boolean flag = true;
                for (Parameter parameter : constructor.getParameters()) {
                    // 获取参数对应的Bean实例
                    Object bean = getBeanCache(parameter.getName(), parameter.getType());
                    // 如果找不到对应的Bean,标记为失败
                    if (Objects.isNull(bean)) {
                        flag = false;
                        break;
                    }
                    // 将Bean实例加入参数列表
                    params.add(bean);
                }
                // 如果所有参数都找到对应的Bean,则实例化对象
                if (flag) {
                    constructor.setAccessible(true);
                    objectBean = constructor.newInstance(params.toArray());
                }
            }

            // 如果实例化成功,将Bean加入列表
            if (Objects.nonNull(objectBean)) {
                beans.add(objectBean);
            } else {
                // 否则将类加入未完成列表
                resultClass.add(classes);
            }
        }

        // 递归处理未完成的类
        if (!resultClass.isEmpty()) {
            beans.add(constructor(resultClass));
        }
        return beans;
    }

    /**
     * 根据文件路径获取类名列表。
     *
     * @param path 文件路径
     * @return 类名列表
     * @throws ClassNotFoundException 如果类未找到
     */
    public List<Class> getClassNameByFile(String path) throws ClassNotFoundException {
        List<Class> classList = new ArrayList<>();
        File file = new File(path);
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        for (File listFile : file.listFiles()) {
            if (listFile.isDirectory()) {
                // 递归处理子目录
                classList.addAll(getClassNameByFile(listFile.getPath()));
            } else {
                String childFilePath = listFile.getPath();
                if (childFilePath.endsWith(".class")) {
                    // 转换为类名
                    childFilePath = childFilePath.substring(childFilePath.indexOf("\classes") + 9, childFilePath.lastIndexOf("."));
                    childFilePath = childFilePath.replace("\", ".");
                    Class aClass = classLoader.loadClass(childFilePath);
                    // 如果类带有@Component注解且不是注解类,则加入列表
                    if (!getAnnotation(aClass) || aClass.isAnnotation()) {
                        continue;
                    }
                    classList.add(aClass);
                    // 注册接口与实现类的映射关系
                    Class[] interfaces = aClass.getInterfaces();
                    for (Class anInterface : interfaces) {
                        if (!allBeanNamesByType.containsKey(anInterface)) {
                            allBeanNamesByType.put(anInterface, Arrays.asList(aClass.getSimpleName()));
                        } else {
                            allBeanNamesByType.get(anInterface).add(aClass.getSimpleName());
                        }
                    }
                }
            }
        }
        return classList;
    }

    /**
     * 判断类是否带有@Component注解。
     *
     * @param clazz 类的Class对象
     * @return 是否带有@Component注解
     */
    public boolean getAnnotation(Class<?> clazz) {
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            // 忽略一些常见的注解
            if (annotation.annotationType() == Deprecated.class ||
                    annotation.annotationType() == SuppressWarnings.class ||
                    annotation.annotationType() == Override.class ||
                    annotation.annotationType() == PostConstruct.class ||
                    annotation.annotationType() == PreDestroy.class ||
                    annotation.annotationType() == Resource.class ||
                    annotation.annotationType() == Resources.class ||
                    annotation.annotationType() == Generated.class ||
                    annotation.annotationType() == Target.class ||
                    annotation.annotationType() == Retention.class ||
                    annotation.annotationType() == Documented.class ||
                    annotation.annotationType() == Inherited.class) {
                continue;
            }
            // 如果类带有@Component注解,返回true
            if (annotation.annotationType() == Component.class) {
                return true;
            } else {
                // 递归检查注解的注解
                return getAnnotation(annotation.annotationType());
            }
        }
        return false;
    }
}

一级缓存的工作流程

Bean 的注册与缓存:

在容器启动时,扫描指定包下的类,识别带有 @Component@Service 注解的类。

通过反射机制实例化这些类,支持以下依赖注入方式:

  • 字段注入 :通过 @Autowired 注解直接注入字段。
  • 构造器注入 :通过 @Autowired 标记构造器,实例化时自动解析参数依赖。
  • 方法注入 :通过 @Autowired 标记方法,在 Bean 初始化后调用方法注入参数。
java 复制代码
// 构造器注入示例
@Service
public class UserServiceImpl implements UserService {
    private final OrderService orderService;

    @Autowired // 支持构造器注入
    public UserServiceImpl(OrderService orderService) {
        this.orderService = orderService;
    }
}

// 方法注入示例
@Service
public class OrderServiceImpl implements OrderService {
    private UserService userService;

    @Autowired // 方法注入
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

Bean 的获取

通过 getBean 方法,从一级缓存中获取已初始化的 Bean。

如果缓存中不存在该 Bean,则返回 null 或抛出异常。

循环依赖的困境与二级缓存的解决方案

在实际开发中,循环依赖是一个常见的问题。例如,假设我们在 OrderServiceImpl 中注入 UserServiceImpl,而 UserServiceImpl 中又注入了 OrderServiceImpl,这就形成了一个典型的循环依赖场景:

java 复制代码
// UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private OrderService orderService;
    
    @Override
    public void say() {
        System.out.println("hello");
        orderService.say();
    }

    @Override
    public void processUser() {
        System.out.println("Processing user in UserService");
    }
}

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private UserService userService;
    
    @Override
    public void say() {
        System.out.println("hello OrderService");
        userService.processUser();
    }
}

在这种情况下,IoC 容器会陷入一个死循环:

  1. 实例化 UserServiceImpl,发现它依赖 OrderServiceImpl
  2. 开始实例化 OrderServiceImpl,发现它依赖 UserServiceImpl
  3. 又回到 UserServiceImpl,导致无限递归,最终抛出异常。

二级缓存的设计与实现

为了解决循环依赖问题,我们引入了二级缓存。

二级缓存的作用

在手动实现的 IoC 容器中,二级缓存(earlySingletonObjects)是解决循环依赖问题的关键。它的核心作用是:

  • 提前暴露 Bean 的引用:在 Bean 还未完全初始化时,将其引用放入二级缓存。
  • 打破循环依赖:当另一个 Bean 需要依赖当前 Bean 时,可以从二级缓存中获取其引用,而不需要等待当前 Bean 完全初始化。

二级缓存的设计

二级缓存的设计基于以下核心思想:

  • 一级缓存(singletonObjects :存储完全初始化后的单例 Bean,确保全局唯一性和直接可用性。
  • 二级缓存(earlySingletonObjects :存储已实例化但未完成依赖注入的 Bean(半成品 Bean),用于解决循环依赖。

二级缓存的实现

java 复制代码
public class Application {
    // 一级缓存:存储完全初始化的 Bean
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    // 二级缓存:存储已实例化但未完成初始化的 Bean
    private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    // 修改后的 getBeanCache 方法,支持从二级缓存获取 Bean
    private Object getBeanCache(String name) throws Exception {
        // 先检查一级缓存
        if (singletonObjects.containsKey(name)) {
            return singletonObjects.get(name);
        }
        // 再检查二级缓存
        if (earlySingletonObjects.containsKey(name)) {
            return earlySingletonObjects.get(name);
        }
        return null;
    }

    // 修改后的 serviceInit 方法,支持半成品 Bean 的存储
    public void serviceInit(List<Object> serviceBeans) throws Exception {
        if (serviceBeans.isEmpty()) {
            return;
        }
        List<Object> resultBean = new ArrayList<>();
        for (Object serviceBean : serviceBeans) {
            String beanName = transformedBeanName(serviceBean.getClass());
            if (autowiredInjection(serviceBean)) {
                // 依赖注入成功,移入一级缓存并移除二级缓存
                singletonObjects.put(beanName, serviceBean);
                earlySingletonObjects.remove(beanName);
            } else {
                // 依赖注入失败,暂存到二级缓存
                earlySingletonObjects.put(beanName, serviceBean);
                resultBean.add(serviceBean);
            }
        }
        // 递归处理未完成的 Bean
        if (!resultBean.isEmpty()) {
            serviceInit(resultBean);
        }
    }
}

解决循环依赖的流程

1. 初始化 UserServiceImpl

当容器开始初始化 UserServiceImpl 时,首先通过反射创建它的实例。此时的 UserServiceImpl 还未完成依赖注入(即 orderService 字段为空),因此它被标记为"半成品"并存入 二级缓存 earlySingletonObjects。这一步骤的核心目的是 提前暴露 Bean 的引用,使得其他 Bean 在初始化时可以获取到它的引用。

java 复制代码
// UserServiceImpl 实例化完成,但依赖未注入
Object userService = new UserServiceImpl();
earlySingletonObjects.put("UserServiceImpl", userService);

2.触发 OrderServiceImpl 的初始化

接下来,容器尝试为 UserServiceImpl 注入 OrderService 依赖。由于 OrderServiceImpl 尚未初始化,容器开始创建 OrderServiceImpl 的实例。同样的,OrderServiceImpl 在实例化后也被存入二级缓存,但它的 userService 字段此时尚未注入。

java 复制代码
// OrderServiceImpl 实例化完成,但依赖未注入
Object orderService = new OrderServiceImpl();
earlySingletonObjects.put("OrderServiceImpl", orderService);

3.解决 OrderServiceImpl 的依赖

当容器为 OrderServiceImpl 注入 UserService 依赖时,它会首先检查 一级缓存 singletonObjects。由于 UserServiceImpl 尚未完成初始化,容器转而从 二级缓存 earlySingletonObjects 中获取其引用。此时,UserServiceImpl 虽然未完成初始化,但它的引用已经足够让 OrderServiceImpl 完成依赖注入。

java 复制代码
// 从二级缓存获取 UserServiceImpl 的引用
UserServiceImpl userServiceRef = (UserServiceImpl) earlySingletonObjects.get("UserServiceImpl");

// 将引用注入到 OrderServiceImpl
Field userServiceField = orderService.getClass().getDeclaredField("userService");
userServiceField.setAccessible(true);
userServiceField.set(orderService, userServiceRef);

4.完成 OrderServiceImpl 的初始化

OrderServiceImpl 的依赖注入完成后,它会被标记为"完全初始化",并从二级缓存移入 一级缓存 singletonObjects。此时,OrderServiceImpl 已经可以被其他 Bean 正常使用。

java 复制代码
// OrderServiceImpl 移入一级缓存
singletonObjects.put("OrderServiceImpl", orderService);
earlySingletonObjects.remove("OrderServiceImpl");

5.完成 UserServiceImpl 的初始化

OrderServiceImpl 完成初始化后,容器会重新回到 UserServiceImpl 的依赖注入流程。此时,OrderServiceImpl 已经存在于一级缓存中,容器可以直接获取它的引用并注入到 UserServiceImplorderService 字段。最终,UserServiceImpl 完成初始化,从二级缓存移入一级缓存。

java 复制代码
// 从一级缓存获取 OrderServiceImpl 的引用
OrderServiceImpl orderServiceRef = (OrderServiceImpl) singletonObjects.get("OrderServiceImpl");

// 将引用注入到 UserServiceImpl
Field orderServiceField = userService.getClass().getDeclaredField("orderService");
orderServiceField.setAccessible(true);
orderServiceField.set(userService, orderServiceRef);

// UserServiceImpl 移入一级缓存
singletonObjects.put("UserServiceImpl", userService);
earlySingletonObjects.remove("UserServiceImpl");

6.关键点总结

  • 二级缓存的桥梁作用:通过临时存储未完成初始化的 Bean,二级缓存充当了循环依赖中的"中间人",使得相互依赖的 Bean 可以交替完成初始化。
  • 递归处理机制 :容器通过递归调用 serviceInit 方法,逐步处理未完成的 Bean,直到所有依赖都被解析。
  • 线程安全性 :使用 ConcurrentHashMap 确保在多线程环境下缓存的读写安全。

二级缓存的协作边界

二级缓存的本质是通过 提前暴露半成品 Bean 的引用 解决循环依赖,但这一机制高度依赖依赖注入的时机:

  1. 字段注入与方法注入
    依赖注入发生在 Bean 实例化之后,此时半成品 Bean 已存入二级缓存,因此可以解决循环依赖。
  2. 构造器注入的挑战
    构造器注入要求依赖在实例化时完成注入。例如:
java 复制代码
// UserServiceImpl(构造器注入)
@Service
public class UserServiceImpl implements UserService {
    private final OrderService orderService;

    @Autowired
    public UserServiceImpl(OrderService orderService) {
        this.orderService = orderService; // 实例化时必须注入
    }
}
  • 当容器尝试实例化 UserServiceImpl 时,必须立即获得 OrderServiceImpl 的实例。
  • OrderServiceImpl 也通过构造器注入依赖 UserServiceImpl,双方都无法提前暴露半成品引用,导致死锁。

这一限制并非缓存设计缺陷,而是由构造器注入的机制决定。实际开发中,可通过优先使用字段注入或代码重构避免此类问题。

通过手动实现 IoC 容器的核心机制,我们揭开了依赖注入与缓存设计的神秘面纱。一级缓存以简洁高效的方式守护着单例 Bean 的生命周期,而二级缓存则化身"破局者",通过提前暴露半成品 Bean 的引用,巧妙化解了循环依赖的死锁困局。若进一步引入三级缓存 ,通过 ObjectFactory 延迟生成代理对象,便可为 AOP 动态代理铺平道路------当 Bean 需要切面增强时,容器能确保依赖注入的是最终代理而非原始对象,从而解决代理滞后导致的逻辑割裂问题。这种分层缓存的协作,不仅是空间换时间的策略,更是对"依赖管理与功能扩展解耦"的深刻实践。未来,通过整合动态代理与注解驱动的切面编程,容器不仅能管理对象生命周期,还能为 Bean 注入行为增强的能力,让代码在控制反转与功能扩展间游刃有余。理解这套底层逻辑,正是深入 Spring 等框架内核的钥匙。愿本文为你打开一扇窗,在框架设计的星辰大海中,找到属于你的航向。若有疑问或灵感,欢迎共探技术之美!

相关推荐
JH30735 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
qq_12498707538 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
Coder_Boy_8 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
2301_818732069 小时前
前端调用控制层接口,进不去,报错415,类型不匹配
java·spring boot·spring·tomcat·intellij-idea
汤姆yu12 小时前
基于springboot的尿毒症健康管理系统
java·spring boot·后端
暮色妖娆丶12 小时前
Spring 源码分析 单例 Bean 的创建过程
spring boot·后端·spring
biyezuopinvip13 小时前
基于Spring Boot的企业网盘的设计与实现(任务书)
java·spring boot·后端·vue·ssm·任务书·企业网盘的设计与实现
JavaGuide14 小时前
一款悄然崛起的国产规则引擎,让业务编排效率提升 10 倍!
java·spring boot
figo10tf14 小时前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
zhangyi_viva14 小时前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端