深入理解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 等框架内核的钥匙。愿本文为你打开一扇窗,在框架设计的星辰大海中,找到属于你的航向。若有疑问或灵感,欢迎共探技术之美!

相关推荐
一 乐2 小时前
订票系统|基于Java+vue的火车票订票系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计·论文·订票系统
一恍过去4 小时前
SpringBoot通过Map实现天然的策略模式
spring boot·后端·策略模式
生产队队长7 小时前
Vue+SpringBoot:整合JasperReport作PDF报表,并解决中文不显示问题
vue.js·spring boot·pdf
程序猿大波7 小时前
基于Java,SpringBoot和Vue高考志愿填报辅助系统设计
java·vue.js·spring boot
橘猫云计算机设计7 小时前
基于Java的班级事务管理系统(源码+lw+部署文档+讲解),源码可白嫖!
java·开发语言·数据库·spring boot·微信小程序·小程序·毕业设计
计算机-秋大田8 小时前
基于Spring Boot的个性化商铺系统的设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·课程设计
小马爱打代码8 小时前
Spring Boot - 动态编译 Java 类并实现热加载
spring boot·后端
天草二十六_简村人10 小时前
Rabbitmq消息被消费时抛异常,进入Unacked 状态,进而导致消费者不断尝试消费(下)
java·spring boot·分布式·后端·rabbitmq
程序员小刚10 小时前
基于SpringBoot + Vue 的汽车租赁管理系统
vue.js·spring boot·汽车