为什么 Spring 是面试必考点?
在Java后端开发领域,Spring 框架凭借其轻量级、解耦和强大的生态系统,成为了企业级应用开发的事实标准。无论是初创公司还是大型互联网企业,Spring 及其衍生项目(如Spring Boot, Spring Cloud)都是构建微服务架构和单体应用的首选。因此,在技术面试中,面试官往往会针对 Spring 的核心原理进行深入考察,以评估候选人的基础扎实程度。
对于准备 Spring面试题目及答案 的求职者来说,仅仅记住答案是不够的。您需要理解 Spring 设计背后的哲学,例如“约定优于配置”、“面向接口编程”以及“控制反转”等理念。本站提供的 Spring面试题 不仅包含标准答案,还结合了实际开发场景,帮助您从原理层面掌握知识。
⚡ IOC 控制反转
理解 Spring IOC 容器如何管理Bean的生命周期。面试常问:Bean的默认作用域是什么?singleton和prototype的区别?如何避免单例模式下的线程安全问题?
⚙️ AOP 面向切面
掌握 Spring AOP 的实现机制,包括JDK动态代理和CGLIB代理的选择。常见考点:AOP的通知类型有哪些?@Transactional事务失效的场景有哪些?
🚀 Spring Boot 自动配置
深入剖析 Spring Boot 的自动配置原理,了解 @EnableAutoConfiguration 和 spring.factories 文件的作用。这是当前Java开发岗位的必备技能。
Spring 核心原理深度解析
为了更清晰地展示 Spring面试题 中的高频考点,我们将核心原理分为三个主要部分。请点击下方选项卡查看详细内容。
Bean 的生命周期
Bean的生命周期 是 Spring面试 中几乎必考的题目。一个Bean从创建到销毁,经历了以下几个关键阶段:
- 实例化 (Instantiation):Spring容器通过构造函数或工厂方法创建Bean实例。
- 属性赋值 (Populate Bean):为Bean的属性设置值,并解析依赖注入。
- 初始化 (Initialization):
- 如果Bean实现了
BeanNameAware接口,调用setBeanName()。 - 如果Bean实现了
BeanFactoryAware接口,调用setBeanFactory()。 - 执行
BeanPostProcessor.postProcessBeforeInitialization()。 - 如果Bean指定了
init-method或实现了InitializingBean,调用初始化方法。 - 执行
BeanPostProcessor.postProcessAfterInitialization()。这是AOP代理生成的关键时刻。
- 如果Bean实现了
- 使用 (Usage):Bean处于可用状态,供应用程序调用。
- 销毁 (Destruction):
- 如果Bean实现了
DisposableBean接口,调用destroy()。 - 如果Bean指定了
destroy-method,调用销毁方法。
- 如果Bean实现了
注意:在面试中,面试官可能会追问 BeanPostProcessor 的作用。它是Spring提供的扩展点,允许在Bean初始化前后进行自定义处理,是许多Spring功能(如AOP、@Autowired)的基础。
IOC 容器的实现机制
Spring IOC 容器是Spring框架的核心,它负责管理对象的生命周期和依赖关系。实现IOC主要依赖于反射和工厂模式。
1. BeanDefinition
在Spring中,每个Bean的信息都被封装在 BeanDefinition 对象中。它包含了Bean的类名、作用域、构造函数参数、属性值等信息。容器启动时,会解析配置文件或注解,生成对应的 BeanDefinition 并存入 BeanDefinitionRegistry。
2. 依赖注入 (DI)
依赖注入是IOC的具体实现方式。Spring支持三种注入方式:
- 构造器注入:推荐用于必填依赖,保证Bean不可变性。
- Setter注入:适用于可选依赖,灵活性高。
- 字段注入 (@Autowired):使用最广泛,但降低了代码的可测试性。
3. 循环依赖解决
Spring通过三级缓存解决单例Bean的循环依赖问题:
singletonObjects:存放完全初始化的Bean。earlySingletonObjects:存放原始的Bean对象(未填充属性),用于解决循环依赖。singletonFactories:存放Bean工厂,用于生成早期引用。
注意:Spring只能解决单例作用域下的setter注入循环依赖,构造器注入和原型作用域下的循环依赖无法解决。
AOP 代理机制
Spring AOP 通过动态代理实现切面编程。Spring根据目标类是否实现了接口,选择不同的代理策略:
1. JDK Dynamic Proxy
如果目标类实现了至少一个接口,Spring默认使用JDK动态代理。它基于接口创建代理类,在运行时生成实现接口的匿名内部类。
// 伪代码示例
Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() { ... }
);
2. CGLIB
如果目标类没有实现接口,Spring会使用CGLIB库生成目标类的子类作为代理。CGLIB通过字节码技术生成子类,覆盖目标方法并织入切面逻辑。
3. 代理选择
可以通过 @EnableAspectJAutoProxy(exposeProxy = true) 或 proxyTargetClass = true 强制使用CGLIB。在现代Spring Boot应用中,通常建议让业务类实现接口,以便使用JDK代理,减少依赖外部库。
Spring Boot 面试核心
随着微服务架构的流行,Spring Boot 已成为Java开发的主流框架。面试中关于 Spring Boot 的问题通常聚焦于其自动配置原理、Starter机制以及常见配置。
1. 自动配置原理
Spring Boot 的核心是 @SpringBootApplication,它组合了 @Configuration、@EnableAutoConfiguration 和 @ComponentScan。其中,@EnableAutoConfiguration 是关键。
当启用自动配置时,Spring Boot 会扫描 spring-boot-autoconfigure jar包中的 META-INF/spring.factories 文件(Spring Boot 2.7+ 为 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports)。该文件列出了所有可能的自动配置类。Spring Boot 根据当前类路径下的依赖和Bean,通过 @Conditional 注解(如 @ConditionalOnClass、@ConditionalOnMissingBean)决定是否加载这些配置类。
2. Starter 机制
Spring-Boot-Starter 是一组便捷的依赖描述符。例如,spring-boot-starter-web 会自动引入Spring MVC、Tomcat、Jackson等依赖。开发者只需引入一个Starter,即可快速搭建特定功能的项目环境,避免了手动管理版本冲突的麻烦。
3. 内嵌容器
Spring Boot 默认内嵌Tomcat、Jetty或Undertow。启动时,它会自动配置这些容器的Bean,使得应用可以独立运行,无需部署到外部Web服务器。这极大地简化了部署流程。
4. 常见面试题
Q: 如何自定义Starter?
A: 创建一个普通Maven项目,编写自动配置类(使用@Configuration和@Conditional注解),然后在META-INF/spring.factories中注册该配置类。最后,将依赖打包并发布。
Q: Spring Boot 如何优化启动速度?
A: 1. 使用 spring.main.lazy-initialization=true 开启懒加载;2. 减少自动配置的扫描范围;3. 使用GraalVM进行原生镜像编译。
Spring Cloud 微服务架构
对于高级Java开发岗位,Spring Cloud 是必须掌握的技能。它提供了一整套微服务解决方案,包括服务注册与发现、配置中心、网关、负载均衡等。
| 组件名称 | 功能描述 | 常用实现 |
|---|---|---|
| 服务注册与发现 | 管理微服务实例的健康状态和地址信息 | Eureka, Nacos, Consul |
| 配置中心 | 集中管理微服务的配置文件 | Spring Cloud Config, Nacos |
| 负载均衡 | 在多个服务实例间分发请求 | Ribbon, Spring Cloud LoadBalancer |
| 服务网关 | 统一入口,处理鉴权、限流、路由 | Zuul, Spring Cloud Gateway |
| 熔断器 | 防止雪崩效应,提高系统容错性 | Hystrix, Resilience4j |
| 分布式追踪 | 追踪请求在微服务间的调用链路 | Sleuth + Zipkin, SkyWalking |
网友还关心:Nacos vs Eureka
在当前技术选型中,Nacos 逐渐取代 Eureka 成为主流的服务注册中心。Nacos 同时支持 AP 和 CP 模式,而 Eureka 仅支持 AP。此外,Nacos 还集成了配置中心功能,简化了架构复杂度。对于新项目,强烈建议使用 Nacos。
Spring 版本演进与技术趋势
了解 Spring 的历史演进有助于在面试中展现对技术背景的深刻理解。以下是Spring发展的重要时间节点:
2002: Spring 诞生
Juergen Hoeller 发布了 Spring 框架的早期版本,旨在解决企业级Java开发的复杂性。
2004: Spring 1.0 发布
正式推出,引入了IOC和AOP的核心概念,迅速成为Java开发的标准。
2007: Spring Security
发布了 Spring Security,提供了全面的安全解决方案。
2014: Spring Boot 1.0
Spring Boot 正式发布,简化了Spring应用的初始搭建和开发过程,推动了微服务浪潮。
2017: Spring Cloud Netflix
Spring Cloud 与 Netflix OSS 深度整合,成为微服务架构的事实标准。
2021: Spring Boot 3.0 & Spring 6.0
全面支持 Java 17 和 Jakarta EE 9,引入了 GraalVM 原生镜像支持,性能大幅提升。
网友还关心:高频问答
以下整理了求职者最常搜索的 Spring面试题目及答案,请仔细阅读并理解其背后的原理。
@Autowired 是Spring提供的注解,默认按类型(byType)注入,如果有多个同类型Bean,则按名称(byName)匹配。它只能用于字段、方法或构造器。@Resource 是JDK提供的注解(JSR-250),默认按名称(byName)注入,如果找不到则按类型匹配。它支持 name 和 type 属性指定。建议优先使用 @Autowired,除非需要指定具体的Bean名称。
Spring定义了7种事务传播行为:
1. REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新事务。
2. REQUIRES_NEW:创建一个新事务,如果当前存在事务,则把当前事务挂起。
3. SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
4. NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则把当前事务挂起。
5. NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
6. NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与REQUIRED类似的操作。
7. MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
Spring主要通过三级缓存解决单例Bean的setter注入循环依赖。当A依赖B,B依赖A时:
1. Spring创建A,将A的ObjectFactory放入三级缓存。
2. A填充属性时,发现依赖B,创建B。
3. B填充属性时,发现依赖A,从三级缓存获取A的ObjectFactory,生成A的早期引用(未填充属性的实例),放入二级缓存,并从三级缓存移除。
4. B完成初始化,放入一级缓存。
5. A获取B的引用,完成初始化,放入一级缓存。
注意:构造器注入的循环依赖无法解决,会抛出BeanCurrentlyInCreationException。
1. 用户发送请求至前端控制器 DispatcherServlet。
2. DispatcherServlet 调用 HandlerMapping 查找处理器。
3. HandlerMapping 找到具体的处理器,返回 HandlerExecutionChain。
4. DispatcherServlet 调用 HandlerAdapter 适配处理器。
5. HandlerAdapter 执行具体的 Controller(Handler)。
6. Controller 执行业务逻辑,返回 ModelAndView。
7. HandlerAdapter 将 ModelAndView 返回给 DispatcherServlet。
8. DispatcherServlet 调用 ViewResolver 解析视图。
9. ViewResolver 返回具体的 View。
10. DispatcherServlet 对 View 进行渲染(填充数据),返回响应给用户。
1. 创建一个新的Maven项目。
2. 编写自动配置类,使用 @Configuration 和 @ConditionalOnClass 等注解。
3. 在 src/main/resources/META-INF/ 下创建 spring.factories 文件(Spring Boot 2.7+ 为 spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports)。
4. 在文件中注册你的自动配置类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\n com.example.MyAutoConfiguration
5. 打包并发布到Maven仓库,其他项目引入该依赖即可。