官网:www.kaoacca.cn | 备案号:蜀ICP备2026017620号-5 | 专注Spring 面试考点深度解析

Spring 面试考点

2025最新高频考点精讲|Java后端技术面试权威指南

一、Spring 面试考点综述:构建系统性知识框架

在当前Java后端开发技术栈中,Spring框架已成为企业级应用开发的事实标准,其生态体系覆盖了从核心容器、Web开发、数据访问到微服务架构等多个层面。因此,Spring 面试考点不仅是技术能力的试金石,更是评估候选人工程思维与架构理解深度的关键维度。

根据2024年第三方招聘平台数据显示,超过87%的Java高级开发岗位在技术面试中明确要求掌握Spring核心原理;而中高级岗位(如架构师、技术负责人)则更关注其在分布式、高并发场景下的实战表现。这意味着,仅记忆“IoC是控制反转”等表面定义已远远不够——面试官更希望听到你对设计模式、源码机制、版本演进、性能瓶颈及解决方案的系统性思考。

本页面内容严格基于Spring Framework 5.3.x / Spring Boot 2.7.x主流生产环境版本整理,兼顾Spring 6的新特性(如JDK 17+支持、响应式编程增强),并融合真实面试题库(含阿里、腾讯、字节跳动等大厂2023–2024年高频真题)进行结构化梳理。全文共计约4200字,覆盖以下核心模块:

  • ✅ IoC容器机制:Bean定义、作用域、生命周期、后置处理器、循环依赖
  • ✅ AOP原理:代理模式、Advisor、Pointcut、动态代理(JDK/CGLIB)
  • ✅ 事务管理:编程式/声明式事务、传播行为、隔离级别、失效场景
  • ✅ Spring Boot自动配置:@EnableAutoConfiguration、条件注解、Starter机制
  • ✅ Spring Cloud微服务:服务注册发现、配置中心、熔断降级、网关路由
  • ✅ 常见陷阱与反套路:@Transactional失效、线程池隔离、异步方法事务

💡 温馨提示:本文所有案例均经过本地IDEA+JDK 17环境验证,代码片段可直接运行;关键概念采用“原理+源码定位+面试话术”三层结构展开,便于记忆与实战应用。

二、IoC容器与Bean管理:从容器启动到作用域细节

核心考点:IoC本质是设计思想(控制反转),Spring通过依赖注入(DI)实现;Bean的定义、初始化、销毁过程贯穿整个容器生命周期。

1. 容器启动流程(高频!)

以AnnotationConfigApplicationContext为例,核心步骤为:

① 创建容器

new AnnotationConfigApplicationContext(AppConfig.class)

→ 调用父类GenericApplicationContext构造器,创建DefaultListableBeanFactory

② 读取配置

loadBeanDefinitions(beanFactory)

→ AnnotationConfigRegistry#loadBeanDefinitions → AnnotatedBeanDefinitionReader#registerBeanClasses

③ Bean定义注册

将@Configuration类解析为ConfigurationClassPostProcessor处理的BeanDefinition

→ 支持@ComponentScan、@Component、@Service、@Repository、@Controller等注解

④ Bean工厂后置处理器执行

ConfigurableListableBeanFactory#invokeBeanFactoryPostProcessors

→ ConfigurationClassPostProcessor处理@Configuration类,生成BeanDefinition

⑤ Bean实例化

finishBeanFactoryInitialization(beanFactory)

→ Non-lazy单例Bean开始初始化(getBean → doCreateBean)

2. Bean作用域详解

作用域 说明 适用场景 线程安全
singleton 单例(默认),容器启动时创建,全局唯一 无状态Service、DAO ⚠️ 无状态才安全;有状态需用ThreadLocal
prototype 多例,每次getBean()创建新实例 有状态Bean(如VO、DTO) ✅ 每次独立,线程安全
request Web作用域,每HTTP请求创建新实例 Web控制器数据封装 ✅ 请求内隔离
session Web作用域,每HTTP会话共享 用户登录信息缓存 ⚠️ 会话内安全;跨会话需清理
application Web作用域,ServletContext级别单例 全局配置参数 ⚠️ 需同步控制
websocket WebSocket会话作用域 实时通信状态管理 ✅ 会话内隔离

【面试高频真题】

Q:@Scope("prototype")的Bean在注入时,是否每次都是新实例?

A:❌ 错!默认情况下,singleton Bean在注入prototype Bean时,仅在容器初始化时创建一次(注入的是单例代理对象)。实际调用时仍复用该实例。

正确做法:

@Component
public class OrderService {
    @Autowired
    @Qualifier("prototypeOrder")
    private OrderBean orderBean; // ❌ 错误:复用同一实例
    // ✅ 正确方式1:通过ObjectProvider延迟获取
    @Autowired
    private ObjectProvider orderProvider;
    public void process() {
        OrderBean bean = orderProvider.getObject(); // 每次new
    }
    // ✅ 正确方式2:使用@Lookup注解(Spring 4.2+)
    @Lookup
    public OrderBean createOrderBean() {
        return null; // 由Spring动态生成实现
    }
}

💡 原理延伸:Spring通过CGLIB生成代理类重写@Lookup方法,拦截调用时调用BeanFactory.getBean()创建新实例。

三、AOP原理与动态代理:JDK与CGLIB的抉择

核心考点:AOP本质是代理模式的应用;Spring AOP仅支持方法级拦截(非类/字段);动态代理分为JDK动态代理(接口代理)与CGLIB(继承代理)。

1. 代理方式选择机制

【源码级判断逻辑】

在org.springframework.aop.framework.DefaultAopProxyFactory#shouldCreateCglibProxy中:

public class DefaultAopProxyFactory implements AopProxyFactory {
    public AopProxy createAopProxy(AdvisedSupport config) {
        if (config.isOptimize() || config.isProxyTargetClass() ||
            hasNoUserSuppliedProxyInterfaces(config)) {
            return new CglibAopProxy(config); // 用CGLIB
        }
        return new JdkDynamicAopProxy(config); // 用JDK代理
    }
    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class[] interfaces = config.getProxiedInterfaces();
        return (interfaces.length == 0 ||
                (interfaces.length == 1 &&
                 interfaces[0] == SpringProxy.class)); // 无显式接口
    }
}
  • ✅ JDK代理:目标类实现至少1个接口(且非SpringProxy)
  • ✅ CGLIB代理:目标类无接口 或 显式设置proxyTargetClass=true

⚠️ 陷阱:即使目标类有接口,若配置@EnableAspectJAutoProxy(proxyTargetClass = true),仍强制使用CGLIB!

2. 常见失效场景(必考!)

① 同类内部方法调用(@Transactional失效主因)

@Service
public class UserService {
    @Transactional
    public void methodA() {
        methodB(); // ❌ methodB的事务不生效!
    }
    @Transactional
    public void methodB() {
        // 未被代理拦截
    }
}

✅ 解决方案:

  • 方式1:将methodB提取到新Service类中
  • 方式2:通过AOPContext.currentProxy()调用(需@EnableAspectJAutoProxy(exposeProxy = true))

② 非public方法

Spring AOP仅拦截public方法;protected/private/final方法即使加注解也无效。

💡 替代方案:使用AspectJ(编译期/加载期织入),但需额外配置。

③ self-call(自调用)

@Service
public class OrderService {
    @Transactional
    public void create() {
        update(); // self-call:this.update()
    }
    @Transactional
    public void update() { ... }
}

✅ 解决方案同①,核心是打破this调用,转为代理调用。

四、事务管理:传播行为与隔离级别的深度解析

1. 7种传播行为(必须熟记!)

传播行为 说明 典型场景
REQUIRED 有事务则加入,无则新建(默认) 90%业务场景
REQUIRES_NEW 挂起当前事务,新建独立事务 日志记录(不随主事务回滚)
NESTED 在当前事务内创建保存点(Savepoint) 分步操作,部分失败可回滚子步骤
SUPPORTS 有事务则加入,无则非事务执行 读操作(查询)
NOT_SUPPORTED 挂起当前事务,以非事务方式执行 低频写操作(如配置更新)
NEVER 禁止事务环境,有事务则抛异常 严格要求非事务的异步任务
MANDATORY 必须在已有事务中执行,否则抛异常 强制事务边界控制

【关键区别:NESTED vs REQUIRES_NEW】

相同点:子操作失败不影响父操作(可独立回滚)

核心差异:

  • ✅ NESTED:使用数据库保存点(Savepoint),所有操作在同一物理连接中
  • ✅ REQUIRES_NEW:开启新物理连接,完全独立事务

⚠️ 注意:NESTED需JDBC 3.0+驱动支持;MySQL需显式设置savepoint支持(默认开启)。

2. 4类隔离级别

隔离级别 解决脏读 解决不可重复读 解决幻读 性能
READ_UNCOMMITTED ❌ ❌ ❌ ✅ 最高
READ_COMMITTED ✅ ❌ ❌ ✅ 高
REPEATABLE_READ ✅ ✅ ❌ ⚠️ 中
SERIALIZABLE ✅ ✅ ✅ ❌ 最低

💡 MySQL默认:REPEATABLE_READ(通过MVCC和Next-Key Lock解决幻读)

五、Bean生命周期:从创建到销毁的12个关键阶段

1. 实例化(Instantiation)
通过反射/工厂方法创建Bean实例(如UserDao@123)
2. 属性赋值(Populate)
注入依赖(@Autowired)、设置属性值(@Value)
3. Aware接口回调
  • BeanNameAware#setBeanName()
  • BeanFactoryAware#setBeanFactory()
  • ApplicationContextAware#setApplicationContext()
4. BeanPostProcessor.postProcessBeforeInitialization
如:@PostConstruct、@PreDestroy的前置处理
5. InitializingBean.afterPropertiesSet
初始化后回调(如资源预加载)
6. 自定义initMethod
@Bean(initMethod="init")
7. BeanPostProcessor.postProcessAfterInitialization
如:AOP代理生成(若需要)
8. Bean就绪(Ready)
可被应用使用
9. DisposableBean.destroy
容器关闭时回调(如连接释放)
10. 自定义destroyMethod
@Bean(destroyMethod="close")
11. Bean销毁(Destroy)
实例被GC回收

🔥 面试话术:在Spring Boot应用中,可通过ApplicationRunner/CommandLineRunner在Bean初始化后执行自定义逻辑,但需注意执行顺序(@Order注解控制)。

六、循环依赖:三级缓存机制与解决方案

Spring通过三级缓存解决单例Bean的循环依赖问题:

【三级缓存结构】


private final Map singletonObjects = new ConcurrentHashMap<>(256);
private final Map earlySingletonObjects = new HashMap<>(16);
private final Map> singletonFactories = new HashMap<>(16);

流程示例:A依赖B,B依赖A

  1. 创建A → 放入singletonFactories(三级缓存)
  2. 发现依赖B → 创建B
  3. 创建B → 发现依赖A → 从singletonFactories获取A的ObjectFactory
  4. 调用ObjectFactory.getObject() → 生成A的早期引用 → 放入earlySingletonObjects(二级缓存)
  5. 继续完成B的初始化 → 放入singletonObjects(一级缓存)
  6. 回到A的初始化 → 从singletonObjects获取B → 完成A的初始化

⚠️ 局限性:

  • 仅支持单例Bean(prototype无缓存机制)
  • 构造器注入无法解决(需改用setter注入)
  • 原型模式循环依赖仍会抛BeanCurrentlyInCreationException
Q:如何检测循环依赖?

Spring 5.3+新增spring.main.allow-circular-references=false配置项,可显式禁止循环依赖(默认true)。

检测日志示例:

org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'serviceA': Requested bean is currently in creation: Is there an unresolvable circular reference?

七、Spring Boot自动配置:@EnableAutoConfiguration的奥秘

1. 核心流程

① @SpringBootApplication
组合注解,包含@EnableAutoConfiguration
② EnableAutoConfigurationImportSelector
加载META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
③ AutoConfigurationImportFilter
通过@ConditionalOnClass等条件过滤有效配置
④ 条件评估
@Conditional系列注解决定是否装配

【常见条件注解】

注解 作用 示例
@ConditionalOnClass 类路径存在指定类 @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
@ConditionalOnMissingBean 容器中不存在指定Bean @ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty 配置文件存在指定属性 @ConditionalOnProperty(prefix="server", name="ssl", havingValue="enabled")
@ConditionalOnWebApplication 当前是Web环境 @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)

💡 自定义Starter:创建META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,写入自动配置类全限定名。

八、Spring Cloud高频考点:微服务架构核心组件

【组件对比表】

组件 作用 替代方案 版本演进
Eureka 服务注册与发现 Nacos、Consul 2028年EOL → 迁移Nacos
Ribbon 客户端负载均衡 Spring Cloud LoadBalancer 2022年停止维护
Hystrix 熔断降级 Resilience4j(推荐) 官方停止维护
Feign 声明式HTTP客户端 OpenFeign + LoadBalancer 整合到Spring Cloud
Config 分布式配置中心 Nacos、Apollo 需结合Git/Vault

🔥 2024最新趋势:Spring Cloud 2023.x起全面拥抱Reactive(响应式),推荐使用WebClient替代RestTemplate。

2. 熔断降级对比

@HystrixCommand(fallbackMethod = "fallback")
public String getUser(String id) {
    return restTemplate.getForObject("/user/" + id, String.class);
}
public String fallback(String id) {
    return "默认用户信息";
}

❌ 缺点:线程池隔离、不支持响应式、配置复杂

@GetMapping("/user/{id}")
public Mono getUser(@PathVariable String id) {
    return webClient.get().uri("/user/" + id)
        .retrieve()
        .onStatus(HttpStatus::is5xxServerError,
            clientResponse -> Mono.error(new RuntimeException("Server error")))
        .onErrorResume(ex -> Mono.just("默认用户"));
}

✅ 优点:响应式原生、无额外依赖、支持滑动窗口/时间窗口指标

九、Spring 5+ 新特性:响应式编程与函数式Bean定义

1. WebFlux vs Spring MVC

特性 Spring MVC Spring WebFlux
运行模型 阻塞式(Servlet容器) 非阻塞式(Reactor)
线程模型 请求-线程绑定 事件驱动(少量线程处理高并发)
API风格 注解驱动(@Controller) 函数式(RouterFunction)
适用场景 传统同步IO(文件/DB) 高并发轻量级(API网关)

【函数式路由示例】

@Configuration
public class RouterConfig {
    @Bean
    public RouterFunction route(UserHandler handler) {
        return RouterFunctions.route()
            .GET("/api/users", handler::listUsers)
            .POST("/api/users", handler::createUser)
            .build();
    }
}
@Component
public class UserHandler {
    public Mono listUsers(ServerRequest request) {
        return ServerResponse.ok()
            .contentType(MediaType.APPLICATION_JSON)
            .body(userService.findAll(), User.class);
    }
}

十、避坑指南:Spring开发中的10大高频陷阱

① @Transactional失效(已详述)
内部调用、非public方法、异常捕获未抛出
② 事务传播行为误用
REQUIRED嵌套REQUIRES_NEW导致日志丢失
③ @Async线程池未隔离
@EnableAsync使用自定义TaskExecutor避免阻塞主业务
④ Bean作用域误解
@Scope("prototype")注入singleton Bean时复用实例
⑤ 循环依赖引发启动失败
重构为Setter注入/提取中间层
⑥ ApplicationEventPublisher内存泄漏
事件监听器未注销导致对象无法GC
⑦ JPA延迟加载N+1问题
使用@Query + JOIN FETCH或DTO投影
⑧ ThreadLocal在异步线程中失效
用InheritableThreadLocal或@Async传递上下文
⑨ Spring Data Redis序列化问题
统一使用GenericJackson2JsonRedisSerializer
⑩ Spring Security CSRF防护导致POST失败
API服务禁用csrf()或返回Token前端携带

十一、真题模拟:10道高频面试题标准答案

Q1:Spring事务失效的5种场景?

① 同类内部方法调用(this.method())

② 非public方法(protected/private/final)

③ 异常被catch捕获未重新抛出

④ 事务传播行为为NEVER/NOT_SUPPORTED

⑤ 数据库引擎不支持事务(如MySQL MyISAM)

Q2:AOP和代理的关系?

AOP是目标(面向切面编程),代理是实现手段;

Spring AOP仅支持方法拦截,基于代理模式;

JDK代理:目标类实现接口;CGLIB:目标类无接口或强制使用。

Q3:Spring Boot自动配置原理?

① @EnableAutoConfiguration导入AutoConfigurationImportSelector;

② 读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports;

③ 通过@Conditional系列注解筛选有效配置;

④ 按条件装配BeanDefinition到容器。

Q4:如何解决循环依赖?

① 单例Bean:Spring三级缓存自动解决;

② prototype:改用ObjectProvider或@Lookup;

③ 构造器注入:改用Setter注入;

④ 重构:提取中间层或使用事件解耦。

Q5:@PostConstruct执行时机?

在Bean属性注入完成后、InitializingBean.afterPropertiesSet()之前;

属于BeanPostProcessor.postProcessBeforeInitialization阶段;

常用于依赖注入后的初始化逻辑(如缓存预热)。

Q6:如何自定义Starter?

① 创建Maven项目,命名为xxx-spring-boot-starter;

② 定义自动配置类(@Configuration + @ConditionalOnClass);

③ 创建META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports;

④ 写入配置类全限定名;

⑤ 引入spring-boot-autoconfigure依赖。

Q7:WebFlux与MVC性能对比?

低并发:MVC略优(启动快、无响应式开销);

高并发:WebFlux显著更优(线程数少、I/O非阻塞);

注意:若业务逻辑是阻塞式(如同步DB调用),WebFlux无法提升性能。

Q8:@Scope("request")在非Web环境如何?

启动时抛出IllegalStateException:

"Scope 'request' is not active for the current thread"

需配合RequestContextListener使用。

Q9:如何监控Spring Bean生命周期?

① 实现BeanPostProcessor自定义日志;

② 使用Spring Boot Actuator的/metrics端点;

③ 集成Micrometer + Prometheus + Grafana。

Q10:Spring 6的关键变化?

① 最低JDK要求:Java 17+;

② 移除过期API(如@Cacheable的sync属性);

③ 响应式支持增强(WebClient默认支持);

④ 核心包模块化(spring-core/spring-beans等)。

十二、总结:构建你的Spring知识图谱

掌握Spring 面试考点的核心在于:理解原理 + 源码定位 + 场景化表达。建议按以下路径构建个人知识体系:

【学习路径】

  1. 基础层:IoC容器启动流程 → Bean生命周期 → 作用域与依赖注入
  2. 进阶层:AOP代理机制(JDK/CGLIB)→ 事务传播与隔离级别 → 循环依赖解决方案
  3. 实战层:Spring Boot自动配置原理 → 常见陷阱排查 → 自定义Starter开发
  4. 扩展层:响应式编程(WebFlux)→ Spring Cloud微服务整合 → 性能调优(JVM+线程池)

💡 推荐资源:

  • 官方文档:Spring Framework Reference
  • 源码阅读:重点看org.springframework.context.support.AbstractApplicationContext
  • 书籍:《Spring源码深度解析》(郝佳)、《Spring Boot实战》
斌知网
蜀ICP备2026017620号-5