【大白话说Java面试题 第142题】【06_Spring篇】第2题:如何实现一个 IOC 容器?

第2题:如何实现一个 IOC 容器?

📚 回答:

  • 核心考点 : 手写 IOC 容器是面试中检验候选人是否真正理解 Spring 原理的"试金石"。面试官不会满足于"扫描包、反射创建对象、字段注入"这种玩具级实现,而是期望你展现 BeanDefinition 元数据抽象单例池与作用域管理构造器注入的递归解析三级缓存解决循环依赖 、以及 BeanPostProcessor 扩展点设计 等工程级设计能力。面试官真正想判断的是:你是否理解 Spring 从"配置"到"对象"的完整映射链路,以及如何在设计中预留扩展性。
1. IOC 容器的核心架构设计

一个工程级的 IOC 容器至少包含 5 个核心模块:citation:3citation:6

模块 职责 对应 Spring 组件
配置解析模块 解析 XML/注解/JavaConfig,提取 Bean 元数据 BeanDefinitionReader
Bean 定义注册模块 存储 Bean 的类名、作用域、依赖关系等元数据 BeanDefinitionRegistry
Bean 实例化模块 根据 BeanDefinition 反射创建对象 AbstractAutowireCapableBeanFactory
依赖注入模块 解析并注入构造器/字段/Setter 依赖 AutowiredAnnotationBeanPostProcessor
生命周期扩展模块 提供初始化前后、销毁时的扩展点 BeanPostProcessor
2. 第一步:定义核心注解

首先定义容器所需的自定义注解,模拟 Spring 的 @Component@Autowired@Qualifier@Scope

java 复制代码
// 标记类为 Bean,由容器管理
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
    String value() default "";  // Bean 名称,默认类名首字母小写
}

// 依赖注入标记
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.CONSTRUCTOR})
public @interface Autowired {
    boolean required() default true;
}

// 指定注入的 Bean 名称(处理同类型多实例)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface Qualifier {
    String value();
}

// 作用域:singleton(默认)或 prototype
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
    String value() default "singleton";
}
3. 第二步:BeanDefinition------Bean 的"元数据描述"

BeanDefinition 是 IOC 容器的核心数据结构,存储 Bean 的所有元信息,而非 Bean 实例本身:citation:3citation:6

java 复制代码
public class BeanDefinition {
    private Class<?> beanClass;           // Bean 的 Class 对象
    private String beanName;              // Bean 唯一标识
    private String scope = "singleton";   // 作用域:singleton/prototype
    private boolean lazyInit = false;     // 是否延迟初始化
    private Constructor<?> constructor;   // 构造器(用于构造器注入)
    private List<PropertyValue> propertyValues = new ArrayList<>();  // 属性依赖
    private List<ConstructorArg> constructorArgs = new ArrayList<>(); // 构造器参数

    // Getter/Setter...
}

// 属性值封装
public class PropertyValue {
    private String name;    // 属性名
    private Object value;   // 属性值(可能是引用其他 Bean 的 RuntimeBeanReference)
    // ...
}

// 构造器参数封装
public class ConstructorArg {
    private Class<?> type;   // 参数类型
    private Object value;    // 参数值
    private String ref;      // 引用的 Bean 名称
    // ...
}

关键设计BeanDefinition 解耦了"配置信息"和"实例对象",容器启动时先加载所有 BeanDefinition,再根据依赖关系按需或预加载实例。citation:6

4. 第三步:配置解析与 BeanDefinition 注册
  • 4.1 包扫描解析(注解驱动) 扫描指定包路径下的所有类,识别 @Component 注解,生成 BeanDefinition

    java 复制代码
    public class ClassPathBeanDefinitionScanner {
        private BeanDefinitionRegistry registry;
    
        public void scan(String... basePackages) {
            for (String basePackage : basePackages) {
                Set<Class<?>> classes = scanPackage(basePackage);
                for (Class<?> clazz : classes) {
                    if (clazz.isAnnotationPresent(Component.class)) {
                        BeanDefinition bd = parseBeanDefinition(clazz);
                        registry.registerBeanDefinition(bd.getBeanName(), bd);
                    }
                }
            }
        }
    
        private BeanDefinition parseBeanDefinition(Class<?> clazz) {
            BeanDefinition bd = new BeanDefinition();
            bd.setBeanClass(clazz);
    
            // 解析 @Component 的 value 作为 Bean 名称
            Component component = clazz.getAnnotation(Component.class);
            String beanName = component.value();
            if (beanName.isEmpty()) {
                beanName = Introspector.decapitalize(clazz.getSimpleName());  // 首字母小写
            }
            bd.setBeanName(beanName);
    
            // 解析 @Scope
            if (clazz.isAnnotationPresent(Scope.class)) {
                bd.setScope(clazz.getAnnotation(Scope.class).value());
            }
    
            // 解析构造器(优先找 @Autowired 构造器,否则找无参构造器)
            Constructor<?> autowiredCtor = findAutowiredConstructor(clazz);
            bd.setConstructor(autowiredCtor != null ? autowiredCtor : clazz.getDeclaredConstructor());
    
            // 解析 @Autowired 字段作为 propertyValues
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    PropertyValue pv = new PropertyValue();
                    pv.setName(field.getName());
                    // 如果有 @Qualifier,记录引用名
                    if (field.isAnnotationPresent(Qualifier.class)) {
                        pv.setValue(new RuntimeBeanReference(
                            field.getAnnotation(Qualifier.class).value()));
                    } else {
                        pv.setValue(new RuntimeBeanReference(field.getType()));
                    }
                    bd.getPropertyValues().add(pv);
                }
            }
    
            return bd;
        }
    }
  • 4.2 BeanDefinitionRegistry------注册表实现 BeanDefinitionRegistry 本质是一个 Map<String, BeanDefinition>,负责 Bean 定义的增删查:citation:3

    java 复制代码
    public class DefaultBeanDefinitionRegistry implements BeanDefinitionRegistry {
        // 核心存储:Bean 名称 → BeanDefinition
        private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
        // 按注册顺序存储 Bean 名称
        private final List<String> beanDefinitionNames = new ArrayList<>();
    
        @Override
        public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
            // 去重检查
            if (beanDefinitionMap.containsKey(beanName)) {
                throw new BeanDefinitionStoreException("Duplicate bean name: " + beanName);
            }
            beanDefinitionMap.put(beanName, beanDefinition);
            beanDefinitionNames.add(beanName);
        }
    
        @Override
        public BeanDefinition getBeanDefinition(String beanName) {
            BeanDefinition bd = beanDefinitionMap.get(beanName);
            if (bd == null) {
                throw new NoSuchBeanDefinitionException(beanName);
            }
            return bd;
        }
    
        @Override
        public boolean containsBeanDefinition(String beanName) {
            return beanDefinitionMap.containsKey(beanName);
        }
    
        @Override
        public String[] getBeanDefinitionNames() {
            return beanDefinitionNames.toArray(new String[0]);
        }
    }
5. 第四步:Bean 实例化与依赖注入
  • 5.1 单例池与三级缓存设计 工程级 IOC 容器必须支持单例管理和循环依赖解决:citation:1

    java 复制代码
    public abstract class AbstractBeanFactory implements BeanFactory {
        // 一级缓存:成品单例池(完全初始化的 Bean)
        protected final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
        // 二级缓存:早期引用(已实例化但未注入属性的 Bean)
        protected final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
        // 三级缓存:Bean 工厂(用于生成早期引用,支持 AOP 代理)
        protected final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
        // 正在创建中的 Bean(循环依赖检测)
        protected final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>());
    
        @Override
        public Object getBean(String beanName) {
            // 1. 先从一级缓存获取
            Object singleton = singletonObjects.get(beanName);
            if (singleton != null) return singleton;
    
            // 2. 再从二级缓存获取
            singleton = earlySingletonObjects.get(beanName);
            if (singleton != null) return singleton;
    
            // 3. 从三级缓存获取工厂并创建
            ObjectFactory<?> factory = singletonFactories.get(beanName);
            if (factory != null) {
                singleton = factory.getObject();
                earlySingletonObjects.put(beanName, singleton);
                singletonFactories.remove(beanName);
                return singleton;
            }
    
            // 4. 创建 Bean
            return createBean(beanName, getBeanDefinition(beanName));
        }
    }
  • 5.2 构造器注入的递归解析 构造器注入需要递归解析所有参数依赖,是 IOC 容器最复杂的部分:

    java 复制代码
    public class DefaultBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory, BeanDefinitionRegistry {
    
        @Override
        protected Object createBean(String beanName, BeanDefinition bd) {
            // 标记为"正在创建"(循环依赖检测)
            singletonsCurrentlyInCreation.add(beanName);
    
            try {
                // 1. 实例化(构造器注入在此阶段完成)
                Object bean = instantiateBean(bd);
    
                // 2. 放入三级缓存(暴露早期引用,解决循环依赖)
                singletonFactories.put(beanName, () -> bean);
    
                // 3. 属性填充(字段注入)
                populateBean(beanName, bean, bd);
    
                // 4. 初始化(执行初始化方法、BPP 前后处理)
                bean = initializeBean(beanName, bean, bd);
    
                // 5. 移入一级缓存
                singletonObjects.put(beanName, bean);
                earlySingletonObjects.remove(beanName);
                singletonFactories.remove(beanName);
    
                return bean;
            } finally {
                singletonsCurrentlyInCreation.remove(beanName);
            }
        }
    
        private Object instantiateBean(BeanDefinition bd) {
            Constructor<?> ctor = bd.getConstructor();
            Class<?>[] paramTypes = ctor.getParameterTypes();
    
            if (paramTypes.length == 0) {
                // 无参构造器:直接反射创建
                return ctor.newInstance();
            }
    
            // 有参构造器:递归解析每个参数的依赖
            Object[] args = new Object[paramTypes.length];
            for (int i = 0; i < paramTypes.length; i++) {
                // 优先按类型查找,再按名称查找
                String depBeanName = resolveDependency(paramTypes[i], bd.getConstructorArgs().get(i));
                args[i] = getBean(depBeanName);  // 递归调用 getBean,可能触发循环依赖
            }
            return ctor.newInstance(args);
        }
    
        private void populateBean(String beanName, Object bean, BeanDefinition bd) {
            for (PropertyValue pv : bd.getPropertyValues()) {
                Field field = bean.getClass().getDeclaredField(pv.getName());
                field.setAccessible(true);
    
                Object value = pv.getValue();
                if (value instanceof RuntimeBeanReference) {
                    // 引用类型:从容器获取依赖 Bean
                    RuntimeBeanReference ref = (RuntimeBeanReference) value;
                    String depBeanName = ref.getBeanName();
                    if (depBeanName == null) {
                        // 按类型查找
                        depBeanName = resolveBeanNameByType(ref.getBeanType());
                    }
                    value = getBean(depBeanName);  // 递归获取依赖
                }
                field.set(bean, value);
            }
        }
    }
  • 5.3 循环依赖检测 在构造器注入中,如果检测到循环依赖,需要抛出异常或采取特殊处理:

    java 复制代码
    private Object getBean(String beanName) {
        // 循环依赖检测:如果当前 Bean 正在创建中,说明存在循环依赖
        if (singletonsCurrentlyInCreation.contains(beanName)) {
            // 尝试从三级缓存获取早期引用
            ObjectFactory<?> factory = singletonFactories.get(beanName);
            if (factory != null) {
                return factory.getObject();  // 返回早期引用(半成品)
            }
            // 构造器循环依赖无法解决(早期引用还未放入缓存)
            throw new BeanCurrentlyInCreationException(
                "Circular dependency detected for bean '" + beanName + "'");
        }
        // ... 正常创建流程
    }
6. 第五步:BeanPostProcessor 扩展点设计

为了让容器具备 AOP、事务等高级功能的扩展能力,必须设计 BeanPostProcessor 接口:citation:6citation:7

java 复制代码
public interface BeanPostProcessor {
    // 初始化前处理(如 @PostConstruct 执行)
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }
    // 初始化后处理(如 AOP 代理创建)
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}

// 容器中的 BPP 注册与执行
public class DefaultBeanFactory extends AbstractBeanFactory {
    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public void addBeanPostProcessor(BeanPostProcessor bpp) {
        beanPostProcessors.add(bpp);
    }

    private Object initializeBean(String beanName, Object bean, BeanDefinition bd) {
        // 1. 执行 Aware 接口回调
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }

        // 2. BPP 前置处理
        Object wrappedBean = bean;
        for (BeanPostProcessor bpp : beanPostProcessors) {
            wrappedBean = bpp.postProcessBeforeInitialization(wrappedBean, beanName);
        }

        // 3. 执行初始化方法(@PostConstruct、InitializingBean、init-method)
        invokeInitMethods(wrappedBean, bd);

        // 4. BPP 后置处理(AOP 代理在此创建!)
        for (BeanPostProcessor bpp : beanPostProcessors) {
            wrappedBean = bpp.postProcessAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }
}
7. 第六步:容器启动入口
java 复制代码
public class AnnotationConfigApplicationContext extends DefaultBeanFactory {
    private ClassPathBeanDefinitionScanner scanner;

    public AnnotationConfigApplicationContext(String... basePackages) {
        // 1. 创建扫描器
        this.scanner = new ClassPathBeanDefinitionScanner(this);
        // 2. 扫描并注册 BeanDefinition
        scanner.scan(basePackages);
        // 3. 预实例化所有非懒加载的单例 Bean
        preInstantiateSingletons();
    }

    private void preInstantiateSingletons() {
        for (String beanName : getBeanDefinitionNames()) {
            BeanDefinition bd = getBeanDefinition(beanName);
            if (bd.isSingleton() && !bd.isLazyInit()) {
                getBean(beanName);  // 触发创建
            }
        }
    }
}
8. 完整使用示例
java 复制代码
// 定义 Bean
@Component
public class UserDao {
    public String findById(Long id) {
        return "User-" + id;
    }
}

@Component
public class UserService {
    private final UserDao userDao;

    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public String getUser(Long id) {
        return userDao.findById(id);
    }
}

// 启动容器
public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext("com.example");
        UserService userService = ctx.getBean(UserService.class);
        System.out.println(userService.getUser(1L));  // User-1
    }
}
9. 手写 IOC 与 Spring 的对比
功能维度 手写简易 IOC Spring Framework
配置方式 注解扫描 XML、注解、JavaConfig、Groovy
BeanDefinition 简化版 RootBeanDefinitionGenericBeanDefinition 支持父子合并
作用域 singleton、prototype singleton、prototype、request、session、application、自定义
依赖注入 构造器 + 字段 构造器、Setter、字段、方法参数
循环依赖 三级缓存基础版 完整三级缓存 + @Lazy 支持
AOP 支持 需手动扩展 BPP AbstractAutoProxyCreator 原生支持
事件机制 ApplicationEventPublisher + 观察者模式
国际化 MessageSource
资源加载 ResourcePatternResolver
类型转换 PropertyEditorConverter
环境抽象 EnvironmentProfile
10. 生产环境避坑指南
  • 10.1 构造器注入的循环依赖无法自动解决 手写容器时,如果 A 的构造器依赖 B,B 的构造器依赖 A,三级缓存机制在构造器注入阶段无法生效(因为对象尚未实例化完成,无法放入缓存)。解决方案:

    1. 改用字段注入(实例化后即可放入缓存);
    2. 引入 @Lazy 注解,延迟注入其中一个依赖。
  • 10.2 单例 Bean 的线程安全 singletonObjects 必须使用 ConcurrentHashMap,且 getBean() 的创建流程需要加锁(或使用 synchronized 块),防止并发环境下重复创建 Bean。

  • 10.3 原型(Prototype)作用域的内存泄漏 Prototype Bean 不由容器管理生命周期,如果持有单例 Bean 的引用,且单例 Bean 持有 Prototype Bean 的引用,会导致 Prototype Bean 无法被 GC。应使用 ObjectFactory@Lookup 方法每次获取新实例。

  • 10.4 包扫描的性能问题 大规模项目中,包扫描可能耗时较长。优化方案:

    1. 缩小扫描范围(精确指定包路径,不要用通配符);
    2. 引入索引文件(Spring 5+ 的 spring.components 索引);
    3. 延迟初始化非核心 Bean。
11. 面试官追问与高分回答模板
  • 追问 1:"如何实现一个 IOC 容器?"

    低分回答:"扫描包路径,用反射创建对象,然后字段注入。"(玩具级实现,没有工程思维)

    高分回答

    "实现一个工程级 IOC 容器需要 5 个核心模块:

    1. 配置解析模块 :扫描指定包路径,识别 @Component 等注解,将类信息解析为 BeanDefinition(包含类名、作用域、依赖关系等元数据);
    2. BeanDefinition 注册模块 :使用 Map<String, BeanDefinition> 存储所有 Bean 定义,提供注册、查询、去重能力;
    3. Bean 实例化模块 :根据 BeanDefinition 通过反射调用构造器创建对象。支持构造器注入时,递归解析参数依赖;
    4. 依赖注入模块 :实例化后,遍历 @Autowired 字段,从容器中递归获取依赖对象并注入。使用三级缓存(singletonObjects、earlySingletonObjects、singletonFactories)解决循环依赖;
    5. 生命周期扩展模块 :定义 BeanPostProcessor 接口,在 Bean 初始化前后提供扩展点,支持 AOP 代理创建、初始化方法执行等。
      关键设计决策:单例池用 ConcurrentHashMap、构造器注入优先、循环依赖检测用 Set<String> 标记创建中状态。"
  • 追问 2:"为什么要设计 BeanDefinition?直接存 Class 对象不行吗?"

    高分回答

    "BeanDefinition 是 IOC 容器的核心抽象,直接存 Class 对象无法满足以下需求:

    1. 元数据丰富性:Class 对象只包含类信息,而 BeanDefinition 还存储作用域(singleton/prototype)、是否懒加载、初始化/销毁方法名、依赖关系、构造器参数等。这些信息无法从 Class 对象直接获取。
    2. 配置与实例解耦:容器启动时先加载所有 BeanDefinition(轻量级),再根据依赖关系按需或预加载实例(重量级)。如果直接存 Class,每次 getBean 都要重新解析注解,性能差且无法支持预加载。
    3. 父子 Bean 合并 :Spring 支持 <bean parent="parentBean"> 的继承关系,BeanDefinition 可以合并父定义的属性,Class 对象无法做到。
    4. 动态修改BeanFactoryPostProcessor 可以在 Bean 实例化前修改 BeanDefinition 的属性(如占位符替换),这是 Spring 扩展性的基础。
      简单来说:Class 是 JVM 的类描述,BeanDefinition 是 Spring 的 Bean 配置描述,两者职责不同。"
  • 追问 3:"三级缓存解决循环依赖的原理是什么?二级缓存够吗?"

    高分回答

    "三级缓存的设计是为了同时解决循环依赖和 AOP 代理问题:

    • 一级缓存 singletonObjects:存储完全初始化的 Bean,是成品池;
    • 二级缓存 earlySingletonObjects:存储提前暴露的半成品 Bean(已实例化但未注入属性);
    • 三级缓存 singletonFactories :存储 ObjectFactory 工厂,用于生成早期引用。
      为什么二级缓存不够?
      如果只有两级缓存,循环依赖时注入的早期引用和最终成品可能是不同的对象(比如 AOP 代理对象)。三级缓存中的 ObjectFactory 可以在需要时生成代理对象,确保早期引用和最终引用是同一个代理对象。
      解决流程(A→B→A)
    1. 创建 A,实例化后将 ObjectFactory(包含 A 的原始对象)放入三级缓存;
    2. A 注入 B,开始创建 B;
    3. B 实例化后需要注入 A,从三级缓存获取 ObjectFactory,生成 A 的早期引用(如果需要代理,这里生成代理对象);
    4. B 完成初始化,放入一级缓存;
    5. A 继续注入 B(已完成),完成初始化,从三级缓存移入一级缓存。
      如果只有两级缓存,步骤 3 中直接暴露原始对象,后续 A 初始化后的代理对象与早期引用不是同一个,导致 B 中的 A 和最终 A 不一致。"
  • 追问 4:"构造器注入和字段注入在实现上有什么区别?"

    高分回答

    "两者的核心区别在于依赖解析的时机

    • 构造器注入 :依赖在实例化阶段 就解析并注入。创建 Bean 时,通过反射获取构造器参数类型,递归调用 getBean() 获取每个参数的依赖对象,然后调用 Constructor.newInstance(args) 创建对象。如果参数依赖存在循环依赖,此时对象尚未创建完成,无法放入缓存,导致构造器循环依赖无法自动解决。
    • 字段注入 :先通过无参构造器(或默认构造器)创建对象,对象创建后立即放入三级缓存,然后再遍历 @Autowired 字段,递归调用 getBean() 获取依赖并注入。因为对象已经创建并放入缓存,循环依赖时可以从缓存获取早期引用。
      实现上的差异:构造器注入需要在 instantiateBean() 中解析参数,字段注入在 populateBean() 中解析字段。构造器注入更复杂,因为需要处理参数类型匹配、@Qualifier 指定、可变参数等问题。"
  • 追问 5:"如果容器中有两个同类型的 Bean,怎么决定注入哪一个?"

    高分回答

    "处理同类型多实例的注入,需要实现类型匹配 + 名称匹配的两级策略:

    1. 按类型查找 :先根据字段类型从 beanDefinitionMap 中筛选出所有匹配的 BeanDefinition;
    2. 唯一性判断:如果只有一个匹配,直接注入;
    3. 按名称匹配:如果有多个匹配,检查字段名是否与某个 Bean 的名称一致,一致则注入该 Bean;
    4. @Qualifier 指定 :如果字段上有 @Qualifier("beanName"),直接按指定名称注入;
    5. @Primary 优先 :检查匹配的 Bean 中是否有标记 @Primary 的,优先注入;
    6. 报错 :如果以上都无法确定唯一 Bean,抛出 NoUniqueBeanDefinitionException
      在手写容器中,至少需要实现前 4 步。Spring 还额外支持 @Priority(JSR-250)、@Order 等更复杂的优先级规则。"
  • 追问 6:"BeanPostProcessor 的执行时机是什么?AOP 代理是在哪个阶段创建的?"

    高分回答

    "BeanPostProcessor 在 Bean 生命周期的初始化阶段前后执行:

    1. postProcessBeforeInitialization:在 Bean 的初始化方法(@PostConstructInitializingBean.afterPropertiesSet()init-method之前 执行。典型应用是 @PostConstruct 注解的解析和执行。
    2. postProcessAfterInitialization:在 Bean 的初始化方法之后 执行。AOP 代理就是在这个阶段创建的!
      AOP 代理创建时机 :Spring 的 AbstractAutoProxyCreator(BPP 的实现类)在 postProcessAfterInitialization 中检查 Bean 是否需要代理(是否有切面匹配),如果需要,则创建 JDK 动态代理或 CGLIB 代理,将原始 Bean 包装为代理对象返回。
      这意味着:AOP 代理是在 Bean 完全初始化之后创建的 ,所以 @PostConstruct 方法中调用同类方法不会触发 AOP(因为此时代理还未创建)。"
12. 方案选型速查表
场景 手写容器策略 Spring 对应实现
单例管理 ConcurrentHashMap 单例池 DefaultSingletonBeanRegistry
循环依赖(字段注入) 三级缓存 DefaultSingletonBeanRegistry 三级缓存
循环依赖(构造器注入) 无法自动解决,需 @Lazy @Lazy + ObjectFactory
AOP 代理 BPP postProcessAfterInitialization AbstractAutoProxyCreator
事务管理 自定义 BPP + 动态代理 TransactionProxyFactoryBean
配置解析 注解扫描 + 反射 ClassPathBeanDefinitionScanner
类型转换 手动 Converter ConversionService

💡 面试官想要的满分总结

手写 IOC 容器不是写玩具代码,而是检验对 Spring 设计思想的理解深度。

核心架构是 5 个模块 :配置解析 → BeanDefinition 注册 → Bean 实例化 → 依赖注入 → 生命周期扩展。BeanDefinition 是连接"配置"和"实例"的桥梁,解耦了元数据和对象,是 Spring 扩展性的基石。

依赖注入的实现要区分构造器注入 (实例化时解析,循环依赖难解决)和字段注入 (实例化后注入,三级缓存可解决循环依赖)。三级缓存的设计精妙之处在于通过 ObjectFactory 工厂延迟生成早期引用,确保 AOP 代理对象的一致性------二级缓存直接暴露原始对象会导致代理不一致。

BeanPostProcessor 是容器扩展性的核心,AOP 代理在 postProcessAfterInitialization 阶段创建,这解释了为什么 @PostConstruct 中自调用不走 AOP。

最后,手写容器时要关注线程安全ConcurrentHashMap)、循环依赖检测singletonsCurrentlyInCreation Set)、同类型多实例的注入策略(类型 + 名称 + @Qualifier)。理解这些,才算真正理解了 Spring IOC 的精髓。


觉得对您有帮助,麻烦 点点关注啦 ,您的关注是我创作的最大动力~ 🎯