⚙️ Spring 核心架构概览

在深入具体的 Spring原理框架面试 细节之前,我们需要建立全局视角。Spring 框架并非单一的工具库,而是一个分层的应用程序框架。其核心模块包括 Spring Core(核心容器)、Spring Context(上下文抽象)、Spring AOP(面向切面编程)、Spring DAO(数据访问)以及 Spring Web(Web应用支持)。

面试官在考察 Spring原理框架面试 相关知识点时,通常希望候选人能够清晰地阐述这些模块之间的依赖关系,以及如何通过 Spring 的依赖注入(DI)机制将各个模块有机地结合起来,形成松耦合的企业级应用架构。

模块名称 核心功能 面试考察频率
Spring Core 提供 IOC 容器和依赖注入机制 ⭐⭐⭐⭐⭐
Spring AOP 实现事务管理、日志记录等横切关注点 ⭐⭐⭐⭐⭐
Spring MVC Web层框架,处理请求映射与视图解析 ⭐⭐⭐⭐
Spring Data 简化数据库访问,支持多种持久化框架 ⭐⭐⭐
Spring Boot 自动配置、起步依赖,简化Spring应用搭建 ⭐⭐⭐⭐⭐

⚡ IOC 容器与 DI 机制源码解析

控制反转(Inversion of Control, IOC) 和 依赖注入(Dependency Injection, DI) 是 Spring 框架的基石。在 Spring原理框架面试 中,面试官常问:“请描述一下 Spring IOC 容器的启动过程?”

1. IOC 容器启动流程

当 Spring 应用启动时,ApplicationContext 的实现类(如 AnnotationConfigApplicationContext)会执行以下步骤:

  1. 资源定位:通过 ResourceLoader 加载配置文件或扫描注解类。
  2. Bean 定义注册:将解析后的 BeanDefinition 注册到 BeanDefinitionRegistry 中。
  3. Bean 实例化:调用 BeanFactoryPostProcessor 对 Bean 定义进行后处理(如解析占位符)。
  4. 依赖注入:实例化单例 Bean,填充属性,执行 BeanPostProcessor 的前置和后置处理。

2. 循环依赖解决方案

在 Spring原理框架面试 中,循环依赖是一个高频考点。Spring 通过“三级缓存”机制解决了单例 Bean 的循环依赖问题:

  • 一级缓存(singletonObjects):存放完全初始化好的 Bean。
  • 二级缓存(earlySingletonObjects):存放早期的 Bean 对象(已实例化但未属性填充)。
  • 三级缓存(singletonFactories):存放 Bean 工厂,用于生成早期引用,主要解决 AOP 代理导致的循环依赖问题。

当 Bean A 依赖 Bean B,而 Bean B 又依赖 Bean A 时,Spring 会先实例化 A,将其工厂放入三级缓存,然后在属性填充时发现 B,实例化 B。B 填充属性时发现 A,从三级缓存获取 A 的早期引用(如果 A 需要 AOP 代理,则通过工厂生成代理对象放入二级缓存),完成 B 的初始化。最后 A 从二级缓存获取 B,完成自身初始化。

⚙️ AOP 动态代理原理

面向切面编程(AOP) 使得开发者能够将横切关注点(如日志、事务、安全)与业务逻辑分离。在 Spring原理框架面试 中,理解 AOP 的底层实现至关重要。

JDK 动态代理
CGLIB 动态代理
Spring 的选择

JDK 动态代理

JDK 动态代理是基于接口的。它要求目标类必须实现至少一个接口。底层通过 java.lang.reflect.Proxy 类和 InvocationHandler 接口实现。

// 示例代码片段
Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new InvocationHandler() {
        public Object invoke(Object proxy, Method method, Object[] args) {
            // 前置处理
            Object result = method.invoke(target, args);
            // 后置处理
            return result;
        }
    }
);

优点:无需额外依赖,JDK 原生支持。
缺点:只能代理接口方法,无法代理类方法。

CGLIB 动态代理

CGLIB(Code Generation Library)是基于继承的。它通过字节码技术生成目标类的子类,并重写父类方法来拦截调用。

// 示例:CGLIB 增强器
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class);
enhancer.setCallback(new MethodInterceptor() {
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
        // 前置处理
        Object result = proxy.invokeSuper(obj, args);
        // 后置处理
        return result;
    }
});
return enhancer.create();

优点:可以代理没有接口的类。
缺点:需要引入 CGLIB 依赖,性能略低于 JDK 代理(但在现代 JVM 中差距已很小),无法代理 final 类或 final 方法。

Spring 的代理策略

在 Spring 中,默认情况下:

  • 如果目标类实现了接口,Spring 默认使用 JDK 动态代理。
  • 如果目标类没有实现接口,Spring 自动切换到 CGLIB 动态代理。

你可以通过 @EnableAspectJAutoProxy(proxyTargetClass = true) 强制使用 CGLIB,或者在 Spring Boot 2.x 之后,默认倾向于使用 CGLIB 以提供更一致的行为。

⚡ Bean 生命周期全流程

Bean 的生命周期 是 Spring原理框架面试 中的经典问题。理解这一流程有助于深入掌握 Spring 的扩展点。

1. 实例化(Instantiation)

Spring 容器通过反射调用构造函数或工厂方法创建 Bean 的原始实例。此时,Bean 的属性尚未填充。

2. 属性赋值(Populate Bean)

Spring 将 Bean 定义中的属性值填充到实例中。这是依赖注入发生的地方。

3. 初始化(Initialization)

如果 Bean 实现了 BeanNameAware、BeanFactoryAware 等接口,会调用相应回调方法。接着执行 BeanPostProcessor.postProcessBeforeInitialization。如果定义了 init-method 或 @PostConstruct,则执行初始化逻辑。最后执行 BeanPostProcessor.postProcessAfterInitialization(AOP 代理通常在此阶段生成)。

4. 销毁(Destruction)

当容器关闭时,如果 Bean 实现了 DisposableBean 或定义了 destroy-method,则执行销毁逻辑。

⚙️ Spring Boot 自动配置原理

Spring Boot 的核心在于“约定优于配置”,其自动配置机制是 Spring原理框架面试 中现代 Java 开发部分的重点。

核心注解:@EnableAutoConfiguration

该注解通过 @Import(AutoConfigurationImportSelector.class) 导入自动配置类。它的工作原理如下:

  1. 读取配置:从 META-INF/spring.factories(Spring Boot 2.7 之前)或 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Spring Boot 2.7+)文件中读取所有候选的自动配置类。
  2. 条件判断:使用 @ConditionalOnClass、@ConditionalOnMissingBean 等条件注解进行筛选。只有当类路径下存在特定类,且用户未手动定义 Bean 时,自动配置才会生效。
  3. 注册 Bean:符合条件的配置类被加载到 Spring 容器中,从而自动装配相关的 Bean。

这种机制极大地简化了应用配置,使得开发者能够“开箱即用”。

⚡ 高频面试问答(Q&A)

问题 简要解答
Spring 中的 Bean 是线程安全的吗? 默认情况下,Spring 的 Bean 是单例的,且无状态,因此是线程安全的。如果有状态(如成员变量),则需要通过 ThreadLocal 或原型作用域来保证线程安全。
如何解释 Spring 中的 @Transactional 失效场景? 1. 方法非 public;2. 类内部自调用;3. 数据库引擎不支持事务(如 MyISAM);4. 异常被 catch 吞掉未抛出。
Spring MVC 的工作流程是什么? 用户发送请求 → DispatcherServlet 接收 → HandlerMapping 找到处理器 → HandlerAdapter 执行 Controller → Controller 返回 ModelView → ViewResolver 解析视图 → 渲染响应。
Spring 中如何避免死锁? 1. 固定锁获取顺序;2. 使用超时机制(Lock.tryLock);3. 减少锁粒度;4. 避免嵌套锁。