秋雨De blog

  • 首页
  • 留言板
  • 关于
  • rss
秋雨De blog
一个技术小白的个人博客
  1. 首页
  2. spring boot
  3. 正文

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

2025年3月23日 475点热度 0人点赞 0条评论

引言

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

你是否曾经好奇过:

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

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


IoC容器的核心功能

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

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

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


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。

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

//首先我们需要几个注解
@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实例的能力。

一级缓存的实现

package org.example.framework;

import org.example.annotation.*;
//import org.example.annotation.Configuration;

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

public class Application1 {
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();


    private Map<Class<?>, List<String>> allBeanNamesByType = new ConcurrentHashMap<>();


    public static Application1 start(Class aClass) throws Exception {
        return new Application1().run(aClass);
    }

    private Application1 run(Class aClass) throws Exception {
        String packageName = aClass.getPackage().getName().replace(".", "/");
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        URL resource = contextClassLoader.getResource(packageName);
        String path = resource.getPath();
        path = path.substring(0, path.indexOf("/class"));
        List<Class> classList = getClassNameByFile(path);
        constructor(classList.stream().filter(e -> e.isAnnotationPresent(Service.class)).collect(Collectors.toList()));
        serviceInit(singletonObjects.values().stream().collect(Collectors.toList()));
        return this;
    }


    public <T> T getBean(String beanName) throws Exception {
        return (T) getBeanCache(beanName, null);
    }

    public <T> T getBean(Class<T> aClass) throws Exception {
        String beanName = transformedBeanName(aClass);
        T bean = getBean(beanName);
        if (Objects.nonNull(bean)) {
            return bean;
        }
        List<String> list = allBeanNamesByType.get(aClass);
        if (Objects.isNull(list)) {
            return null;
        }
        if (list.size() != 1) {
            throw new RuntimeException("找到多个bean");
        }
        return getBean(allBeanNamesByType.get(aClass).iterator().next());
    }

    private Object getBeanCache(String name, Class clazz) throws Exception {
        Object bean = getBeanCache(name);
        if (Objects.nonNull(bean)) {
            return bean;
        }
        return getBeanCache(clazz);
    }

    private Object getBeanCache(Class clazz) throws Exception {
        Object bean = getBeanCache(clazz.getSimpleName());
        if (Objects.nonNull(bean)) {
            return bean;
        }
        List<String> list = allBeanNamesByType.get(clazz);
        if (Objects.isNull(list)) {
            return null;
        }
        if (list.size() != 1) {
            throw new RuntimeException("找到多个Bean:" + clazz.getName());
        }
        return getBeanCache(allBeanNamesByType.get(clazz).iterator().next());
    }

    private Object getBeanCache(String name) throws Exception {
        if (singletonObjects.containsKey(name)) {
            return singletonObjects.get(name);
        }
        return null;
    }


    public void serviceInit(List<Object> serviceBeans) throws Exception {
        if (serviceBeans.isEmpty()) {
            return;
        }
        List<Object> resultBean = new ArrayList<>();
        for (Object serviceBean : serviceBeans) {
            if (!autowirteInjection(serviceBean)) {
                resultBean.add(serviceBean);
            }
        }
        if (!resultBean.isEmpty()) {
            serviceInit(resultBean);
        }
    }

    public boolean autowirteInjection(Object bean) throws Exception {
        List<Field> fieldList = Stream.of(Arrays.asList(bean.getClass().getFields()), Arrays.asList(bean.getClass().getDeclaredFields()))
                .flatMap(Collection::stream)
                .filter(e -> e.isAnnotationPresent(Autowirte.class))
                .distinct()
                .collect(Collectors.toList());
        List<Method> methodList = Stream.of(Arrays.asList(bean.getClass().getMethods()), Arrays.asList(bean.getClass().getDeclaredMethods()))
                .flatMap(Collection::stream)
                .filter(e -> e.isAnnotationPresent(Autowirte.class))
                .distinct()
                .collect(Collectors.toList());
        for (Field field : fieldList) {
            Object paramBean = getBeanCache(field.getName(), field.getType());
            if (Objects.isNull(paramBean)) {
                return false;
            }
            field.setAccessible(true);
            field.set(bean, paramBean);
        }
        for (Method method : methodList) {
            List<Object> params = new ArrayList<>();
            Parameter[] parameters = method.getParameters();
            for (Parameter parameter : parameters) {
                Object parameterBean = getBeanCache(parameter.getName(), parameter.getType());
                if (Objects.isNull(parameterBean)) {
                    return false;
                }
                params.add(parameterBean);
            }
            method.setAccessible(true);
            method.invoke(bean, params.toArray());
        }
        return true;
    }

    public String transformedBeanName(Class aclass) {
        if (aclass.isAnnotationPresent(Service.class)) {
            Service service = (Service) aclass.getAnnotation(Service.class);
            if (Objects.nonNull(service.value()) && !service.value().isEmpty()) {
                return service.value();
            }
        }
        return aclass.getSimpleName();
    }


    public void constructor(List<Class> classList) throws Exception {
        List<Class> resultClass = new ArrayList<>();
//        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();
            }
            List<Constructor> constructorList = constructorSet.stream().filter(e -> e.isAnnotationPresent(Autowirte.class)).collect(Collectors.toList());
            if (constructorList.size() > 1) {
                throw new RuntimeException("存在多个autowrite" + classes.getSimpleName());
            }
            if (constructorList.size() == 1) {
                autowriteConstructor = constructorList.get(0);
            }
            Constructor constructor = null;

            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()) {
                    Object bean = getBeanCache(transformedBeanName(parameter.getType()), parameter.getType());
                    if (Objects.isNull(bean)) {
                        flag = false;
                        break;
                    }
                    params.add(bean);
                }
                if (flag) {
                    constructor.setAccessible(true);
                    objectBean = constructor.newInstance(params.toArray());
                }
            }
            if (Objects.nonNull(objectBean)) {
//                objectBean = createProxyIfNecessary(objectBean);
                singletonObjects.put(transformedBeanName(classes), objectBean);
                Class[] interfaces = classes.getInterfaces();
                for (Class anInterface : interfaces) {
                    if (!allBeanNamesByType.containsKey(anInterface.getName())) {
                        allBeanNamesByType.put(anInterface, Arrays.asList(classes.getSimpleName()));
                    } else {
                        allBeanNamesByType.get(anInterface).add(classes.getSimpleName());
                    }
                }
            } else {
                resultClass.add(classes);
            }
        }
        if (!resultClass.isEmpty()) {
            constructor(resultClass);
        }
    }


    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);
                    if (!getAnnotation(aClass) || aClass.isAnnotation()) {
                        continue;
                    }
                    classList.add(aClass);
                }
            }
        }
        return classList;
    }

    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;
            }
            if (annotation.annotationType() == Component.class) {
                return true;
            } else {
                return getAnnotation(annotation.annotationType());
            }
        }
        return false;
    }
}

一级缓存的工作流程

Bean 的注册与缓存:

在容器启动时,扫描指定包下的类,识别带有 @Component 或 @Service 注解的类。
通过反射机制实例化这些类,支持以下依赖注入方式:

  • 字段注入:通过 @Autowired 注解直接注入字段。
  • 构造器注入:通过 @Autowired 标记构造器,实例化时自动解析参数依赖。
  • 方法注入:通过 @Autowired 标记方法,在 Bean 初始化后调用方法注入参数。
// 构造器注入示例
@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 或抛出异常。

二级缓存的必要性

在IoC容器的设计中,一级缓存看似已经能够管理Bean的完整生命周期,那么为什么还需要引入二级缓存?这个问题触及到了容器设计中关于对象状态管理和依赖解析时机的核心考量。

一级缓存的局限性

让我们先看一个典型的一级缓存实现如何应对循环依赖:

// 一级缓存实现的核心片段
public class Application1 {
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    
    public void constructor(List<Class> classList) throws Exception {
        // 实例化逻辑...
        if (Objects.nonNull(objectBean)) {
            singletonObjects.put(transformedBeanName(classes), objectBean);
            // 立即尝试依赖注入
            autowirteInjection(objectBean);
        }
    }
}

在这种实现下,当遇到循环依赖时会出现以下问题:

  • 实例化顺序僵局:
    • 创建UserServiceImpl实例后立即放入一级缓存
    • 尝试注入OrderService时发现需要创建OrderServiceImpl
    • 创建OrderServiceImpl实例后立即放入一级缓存
    • 尝试注入UserService时从一级缓存获取UserServiceImpl
    • 但此时UserServiceImpl的orderService字段尚未注入完成,处于"半成品"状态
  • 状态不一致风险:
    • 其他线程可能在UserServiceImpl未完全初始化时就通过getBean获取到它
    • 使用这个不完整的Bean可能导致NPE或其他异常

二级缓存的设计与实现

二级缓存的作用

二级缓存通过对象状态分层管理解决了这些问题:

 二级缓存的设计

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

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

二级缓存的实现

public class Application {
    // 一级缓存:存储完全初始化的 Bean
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    // 二级缓存:存储已实例化但未完成初始化的 Bean
    private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
    //修改后的constructor方法将半成品放到二级缓存中
    public void constructor(List<Class> classList) throws Exception {
        List<Class> resultClass = new ArrayList<>();
//        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();
            }
            List<Constructor> constructorList = constructorSet.stream().filter(e -> e.isAnnotationPresent(Autowirte.class)).collect(Collectors.toList());
            if (constructorList.size() > 1) {
                throw new RuntimeException("存在多个autowrite" + classes.getSimpleName());
            }
            if (constructorList.size() == 1) {
                autowriteConstructor = constructorList.get(0);
            }
            Constructor constructor = null;

            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()) {
                    Object bean = getBeanCache(transformedBeanName(parameter.getType()), parameter.getType());
                    if (Objects.isNull(bean)) {
                        flag = false;
                        break;
                    }
                    params.add(bean);
                }
                if (flag) {
                    constructor.setAccessible(true);
                    objectBean = constructor.newInstance(params.toArray());
                }
            }
            if (Objects.nonNull(objectBean)) {
                earlySingletonObjects.put(transformedBeanName(classes), objectBean);
                Class[] interfaces = classes.getInterfaces();
                for (Class anInterface : interfaces) {
                    if (!allBeanNamesByType.containsKey(anInterface.getName())) {
                        allBeanNamesByType.put(anInterface, Arrays.asList(classes.getSimpleName()));
                    } else {
                        allBeanNamesByType.get(anInterface).add(classes.getSimpleName());
                    }
                }
            } else {
                resultClass.add(classes);
            }
        }
        if (!resultClass.isEmpty()) {
            constructor(resultClass);
        }
    }
    // 修改后的 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() throws Exception {
        if (earlySingletonObjects.isEmpty()) {
            return;
        }
        for (Object serviceBean : earlySingletonObjects.values()) {
            if (autowirteInjection(serviceBean)) {
                singletonObjects.put(transformedBeanName(serviceBean.getClass()), serviceBean);
                earlySingletonObjects.remove(transformedBeanName(serviceBean.getClass()));
            }
        }
        if (!earlySingletonObjects.isEmpty()) {
            serviceInit();
        }
    }
}

二级缓存的工作流程

通过手动实现IoC容器,我们深入理解了Spring解决循环依赖的核心逻辑:一级缓存存储完整Bean,二级缓存暂存未完成依赖注入的“半成品”,通过分层状态隔离,既保证了线程安全,又破解了循环依赖的僵局。当前实现虽解决了基础问题,但相比Spring仍缺少三级缓存对代理对象的支持及更精细的生命周期管理。未来可通过引入ObjectFactory扩展代理场景、结合AOP实现动态增强,并支持@PostConstruct等注解完善生命周期。这种“造轮子”的过程不仅揭示了框架设计的精妙,更启发我们:理解底层逻辑,才能跳出框架束缚,写出真正健壮的代码。建议结合Spring源码中的DefaultSingletonBeanRegistry,进一步探索工业级容器在并发、扩展性上的深层设计思想。

项目源码:https://github.com/Fall-Rain/ioc_demo

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2025年3月27日

fallrain

种一棵树最好的时间是十年前,其次是现在。

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

fallrain

种一棵树最好的时间是十年前,其次是现在。

文章目录
  • 引言
  • IoC容器的核心功能
  • 一级缓存的设计与实现
  • 二级缓存的必要性
  • 二级缓存的设计与实现
  • 二级缓存的工作流程
友情连接
猫饭范文泉博客迎風别葉CODING手艺人ScarSu
归档
  • 2025 年 5 月
  • 2025 年 4 月
  • 2025 年 3 月
  • 2024 年 12 月
  • 2024 年 10 月
  • 2024 年 5 月
  • 2023 年 2 月
  • 2022 年 11 月
  • 2022 年 3 月
  • 2021 年 12 月
  • 2021 年 8 月
  • 2021 年 5 月
  • 2021 年 4 月
  • 2021 年 3 月
  • 2020 年 12 月
  • 2020 年 11 月
  • 2020 年 8 月
  • 2020 年 5 月
  • 2019 年 12 月
  • 2019 年 3 月

吉ICP备18007356号

吉公网安备22020302000184号

Theme Kratos Made By Seaton Jiang

COPYRIGHT © 2025 秋雨De blog ALL RIGHTS RESERVED