文章目录
【导读】 本文并非简单的面试题罗列,而是致力于构建一座从Spring基础到高级特性的知识大厦。我们不仅提供“是什么”的答案,更深入挖掘“为什么”和“怎么办”,结合底层原理和实战经验,助你彻底征服Spring面试,斩获心仪Offer!
📖 一、Spring基石篇:IoC与DI深度解密
1.1 谈谈你对Spring框架的理解?
参考答案:
Spring是一个轻量级、开源、一站式的Java企业级应用开发框架。其核心使命是简化企业级应用开发,通过提供一套集成化的解决方案,让开发者能更专注于业务逻辑。
核心策略与优势:
- 控制反转(IoC) / 依赖注入(DI):实现组件间松耦合的基石。
- 面向切面编程(AOP):将横切关注点(如日志、事务、安全)模块化,提高代码复用性和可维护性。
- 丰富的生态集成:无缝集成各种优秀框架(如Hibernate, MyBatis)和技术(如缓存、消息队列)。
- 简化API使用:对JDBC、JavaMail等繁琐的JavaEE API进行了大幅简化。
- 非侵入式设计:你的POJO类通常不直接依赖于Spring特定类,代码污染极低。
1.2 详细阐述Spring IoC容器的工作机制?
参考答案:
IoC(控制反转)是一种设计思想,将传统由程序代码直接操控的对象调用权交给容器,由容器来管理对象的生命周期和依赖关系。
Spring IoC容器实现机制:工厂模式 + 反射机制 + 策略模式。
- 加载与解析配置:容器启动时,读取配置文件(XML/Java Config/注解),将每个
<bean>或@Bean解析为一个BeanDefinition对象,它包含了创建一个Bean的所有元数据。 - BeanDefinition注册:将
BeanDefinition注册到BeanDefinitionRegistry(一个巨大的Map)中。 - Bean实例化:当调用
getBean()时,容器根据BeanDefinition中的信息,通过反射机制调用构造方法创建Bean的实例。 - 依赖注入:根据配置(构造器注入、Setter注入、自动注入),容器将依赖的其他Bean注入到目标Bean的属性中。
- 生命周期回调:如果Bean实现了
InitializingBean接口或定义了init-method,容器会调用其初始化方法。 - 返回可用对象:此时,一个完整的、依赖关系就绪的Bean就可以被应用程序使用了。
1.3 BeanFactory vs. ApplicationContext,有何区别?
参考答案:
| 特性 | BeanFactory | ApplicationContext |
|---|---|---|
| 定位 | IoC核心基础容器,最底层的接口 | BeanFactory的超集,企业级应用上下文 |
| 加载时机 | 懒加载 (Lazy Loading):只有在使用getBean()时才创建和注入Bean | 预加载 (Eager Loading):容器启动时,就立即创建并配置所有单例Bean |
| 功能提供 | 基础的IoC和DI功能 | 在IoC基础上,提供消息资源处理(i18n)、事件发布、AOP集成、层级上下文、更便捷的资源访问等 |
| 适用场景 | 资源受限的移动设备或Applet | 绝大多数J2EE应用,是Spring应用的首选 |
结论:ApplicationContext功能更强大,除非在非常关注内存的场景,否则都应使用它。
1.4 Spring中Bean的作用域有哪些?
参考答案:
- singleton (默认):每个Spring IoC容器中,一个Bean定义只存在一个共享的实例。
- prototype:每次请求(调用
getBean()或注入时)都会创建一个新的Bean实例。 - request:一次HTTP请求一个实例(仅适用于Web应用)。
- session:一个HTTP Session一个实例(仅适用于Web应用)。
- application:一个
ServletContext生命周期一个实例(仅适用于Web应用)。 - websocket:一个
WebSocket生命周期一个实例(仅适用于Web应用)。
1.5 单例Bean是线程安全的吗?如何保证?
参考答案:
Spring框架并不保证单例Bean的线程安全。 线程安全取决于Bean本身的状态(State)。
- 无状态Bean (Stateless Bean):Bean中没有可变的成员变量(只有常量或无状态服务),所有操作都是基于方法参数。这种Bean是线程安全的。例如:
Service,Dao等。 - 有状态Bean (Stateful Bean):Bean中包含可变的成员变量。这种Bean是非线程安全的。
保证线程安全的策略:
- 首选:将Bean的作用域改为
prototype。每次请求都创建新实例,从根本上避免共享。 - 使用ThreadLocal:将有状态的数据存储在
ThreadLocal中,为每个线程提供独立的变量副本。 - 避免使用实例变量:尽量设计成无状态Bean。
- 在方法中加锁 (synchronized):但会严重影响性能,不推荐。
⚔️ 二、AOP核心篇:纵横切面的艺术
2.1 解释一下AOP及其相关术语?
参考答案:
AOP(面向切面编程)是一种编程范式,用于将那些与核心业务逻辑无关,但却被多个模块所共同使用的功能(横切关注点)分离出来,形成可重用的模块。
核心术语:
- Aspect (切面):横切关注点的模块化。它是
Advice和Pointcut的集合。通常是一个用@Aspect注解的类。 - Join Point (连接点):程序执行过程中的一个点,如方法调用、异常抛出。在Spring AOP中,它总是代表一个方法的执行。
- Advice (通知):在特定
Join Point上执行的动作。类型有:@Before,@After,@AfterReturning,@AfterThrowing,@Around。 - Pointcut (切点):一个表达式,用于匹配哪些
Join Point需要被切入。Advice与Pointcut表达式关联。 - Weaving (织入):将
Aspect应用到目标对象并创建代理对象的过程。Spring AOP在运行时完成织入。
2.2 Spring AOP 和 AspectJ 有什么区别?
参考答案:
| 特性 | Spring AOP | AspectJ |
|---|---|---|
| 实现方式 | 动态代理(JDK Proxy 或 CGLIB) | 字节码编辑(编译时、编译后、加载时) |
| 织入时机 | 运行时织入 | 编译期、类加载期织入 |
| 性能 | 较好 | 更优(编译后无额外开销) |
| 功能 | 仅支持方法级别的连接点 | 功能全面,支持字段、构造器、静态代码块等连接点 |
| 依赖 | 仅需Spring核心包,轻量 | 需要额外的AspectJ编译器和织入器 |
| 适用场景 | Spring管理的Bean,满足大部分企业应用需求 | 需要更强大、更细粒度AOP控制的复杂场景 |
2.3 JDK动态代理和CGLIB代理如何选择?
参考答案:
Spring AOP自动根据目标类选择代理策略:
- JDK Dynamic Proxy (默认策略):
- 条件:如果目标对象实现了至少一个接口。
- 原理:基于Java反射,在运行时为接口创建一个
$Proxy0之类的代理类。 - 要求:代理类和目标类是实现相同接口的“兄弟”关系。
- CGLIB Proxy:
- 条件:如果目标对象没有实现任何接口。
- 原理:通过继承目标类,生成其子类作为代理类,并重写父类方法。
- 限制:无法代理
final类或final方法。
🔄 三、生命周期与高级特性篇
3.1 描述Spring Bean的完整生命周期?
参考答案:
这是一个非常高频的面试题!生命周期大致可分为以下几个阶段:
- 实例化 (Instantiate):调用Bean的构造方法,创建一个新的实例。
- 属性赋值 (Populate):根据配置(XML,
@Autowired等),将依赖的Bean和值注入到属性中。 - Aware接口回调:如果Bean实现了各种
Aware接口(如BeanNameAware,BeanFactoryAware,ApplicationContextAware),Spring会回调相应方法,将容器信息注入给Bean。 - BeanPostProcessor前置处理:调用所有
BeanPostProcessor的postProcessBeforeInitialization方法。 - 初始化 (Initialization):
- 执行
@PostConstruct注解的方法。 - 如果实现了
InitializingBean接口,执行afterPropertiesSet()方法。 - 执行自定义的
init-method方法。
- 执行
- BeanPostProcessor后置处理:调用所有
BeanPostProcessor的postProcessAfterInitialization方法。AOP代理就在此阶段生成! - Bean就绪:此时Bean已完全初始化,存放在单例池中,可供使用。
- 销毁 (Destruction):
- 容器关闭时,执行
@PreDestroy注解的方法。 - 如果实现了
DisposableBean接口,执行destroy()方法。 - 执行自定义的
destroy-method方法。
- 容器关闭时,执行
3.2 Spring如何解决循环依赖问题?
参考答案:
循环依赖就是两个或多个Bean相互持有对方的引用,构成一个循环链(如A依赖B,B又依赖A)。
Spring通过三级缓存的机制巧妙地解决了单例Bean通过Setter注入/字段注入造成的循环依赖。
三级缓存:
- 一级缓存 (singletonObjects):存放已经完全初始化好的成品Bean。
- 二级缓存 (earlySingletonObjects):存放半成品Bean(已实例化但未完成属性注入和初始化)。用于从三级缓存中获取经过
BeanPostProcessor处理后的早期引用。 - 三级缓存 (singletonFactories):存放Bean工厂对象 (
ObjectFactory),用于生成Bean的早期引用(一个提前暴露的代理对象)。
解决流程(以A和B循环依赖为例):
- 开始创建A,实例化A,将A的工厂对象放入三级缓存。
- 为A进行属性注入,发现依赖B,于是去创建B。
- 开始创建B,实例化B,将B的工厂对象放入三级缓存。
- 为B进行属性注入,发现依赖A。此时,B从三级缓存中找到A的工厂对象,调用
getObject()方法获取A的早期引用(一个代理对象),并将这个早期引用放入二级缓存,同时从三级缓存移除A的工厂。B成功注入A的早期引用,完成初始化,放入一级缓存。 - A接着注入已完全初始化好的B,完成自己的初始化,放入一级缓存。清理二级缓存。
无法解决的循环依赖:
- 构造器循环依赖:在实例化阶段就需要依赖,此时Bean甚至还未创建,无法提前暴露引用,Spring直接抛出
BeanCurrentlyInCreationException。 prototype作用域的循环依赖:Spring不缓存prototypeBean,无法暴露早期引用。
3.3 @Autowired 和 @Resource 注解的区别?
参考答案:
| 特性 | @Autowired (Spring) | @Resource (JSR-250) |
|---|---|---|
| 提供方 | Spring框架 | Java标准注解 (JSR-250) |
| 默认注入方式 | byType | byName |
| 注入流程 | 1. 按类型查找Bean 2. 如果找到多个,再按属性名作为Bean Name进行匹配 3. 可通过 @Qualifier指定Bean名 | 1. 按属性名作为Bean Name查找Bean 2. 如果找不到,再回退到byType |
| required属性 | 有 (@Autowired(required=false)) | 无(找不到就报错) |
| 适用场景 | 推荐在纯Spring项目中使用 | 希望代码与Spring解耦,或需要按名称注入时 |
💽 四、数据访问与事务篇
4.1 Spring事务管理的原理是什么?
参考答案:
Spring事务的本质是对数据库事务的抽象和封装。其本身并不实现事务,而是提供一套统一的事务管理接口(PlatformTransactionManager),将实际工作委托给底层的持久化框架(如JDBC、Hibernate、JPA)去做。
声明式事务原理:基于AOP和动态代理。
- 在事务方法开始前,通过AOP拦截,调用
TransactionInterceptor。 - 获取事务管理器(
PlatformTransactionManager)。 - 根据事务属性(
@Transactional配置),创建或加入一个事务。 - 执行目标方法(你的业务代码)。
- 根据执行是否成功(有无异常),提交或回滚事务。
4.2 Spring事务的传播行为有哪些?
参考答案:
传播行为定义了多个事务方法相互调用时,事务应该如何传播。
- PROPAGATION_REQUIRED (默认):如果当前没有事务,就新建一个;如果已存在,就加入它。确保在同一个事务中。
- PROPAGATION_REQUIRES_NEW:无论当前有无事务,都新建一个独立的事务。新事务与原事务无关,独立提交回滚。
- PROPAGATION_SUPPORTS:支持当前事务。如果当前有事务,就加入;如果没有,就以非事务方式执行。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作。如果当前有事务,则将其挂起。
- PROPAGATION_MANDATORY:强制要求必须在已有事务中运行,否则抛出异常。
- PROPAGATION_NEVER:必须在没有事务的情况下执行,否则抛出异常。
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。嵌套事务是外部事务的子事务,有自己的保存点(savepoint),可以独立回滚。依赖于JDBC 3.0的保存点机制。
4.3 @Transactional注解在什么情况下会失效?
参考答案:
这是一个高级且常见的问题!
- 方法非public:
@Transactional只能用于public方法上。 - 自调用问题:同一个类中,一个非事务方法A调用另一个**有
@Transactional注解的方法B**,事务会失效。因为代理对象调用的是this.B()`,绕过了代理。 - 异常被捕获:默认只在抛出运行时异常(RuntimeException)和Error时回滚。如果抛出了检查异常(IOException等)或异常在方法内被
try-catch吞掉,事务不会回滚。可通过@Transactional(rollbackFor = Exception.class)配置。 - 数据库引擎不支持:例如MySQL的MyISAM引擎不支持事务。
- 错误的传播属性:例如配置了
PROPAGATION_NOT_SUPPORTED。
🌐 五、Spring MVC与Web篇
5.1 描述Spring MVC的核心处理流程?
参考答案:
DispatcherServlet是Spring MVC的核心,是前端控制器模式的实现。
- 用户请求:发送请求至前端控制器
DispatcherServlet。 - 查找Handler:
DispatcherServlet调用HandlerMapping,根据请求URL找到对应的Handler(通常是Controller和Method)和HandlerInterceptor。 - 执行拦截器:执行
HandlerInterceptor的preHandle方法。 - 适配并执行Handler:
DispatcherServlet调用HandlerAdapter去实际执行Handler。HandlerAdapter会处理@RequestMapping、参数绑定等细节。 - 处理业务逻辑:
Controller执行完毕,返回一个ModelAndView对象(包含模型数据和视图名)。 - 处理视图:
DispatcherServlet调用ViewResolver,根据视图名解析出具体的View对象。 - 渲染视图:
View对象结合Model中的数据,进行视图渲染(如填充HTML模板)。 - 返回响应:将渲染结果返回给客户端。
- 执行拦截器:最后执行
HandlerInterceptor的postHandle和afterCompletion方法。
5.2 @RestController 和 @Controller 有什么区别?
参考答案:
@Controller:标识一个类是Spring MVC的控制器。方法通常返回一个视图名称(String),由ViewResolver解析为物理视图(如JSP, Thymeleaf)。@RestController:是@Controller和@ResponseBody的组合注解。它标识一个控制器,并且其所有方法都默认添加了@ResponseBody注解,返回值直接写入HTTP响应体(通常转换为JSON/XML),用于构建RESTful Web服务,不返回视图。
🚀 六、Spring Boot与Spring Cloud篇
6.1 Spring Boot的核心优势是什么?
参考答案:
Spring Boot是Spring的脚手架框架,其核心是约定大于配置,旨在快速创建独立、生产级的Spring应用。
- 自动配置 (Auto-Configuration):根据项目依赖(
starter)和类路径,自动配置Spring应用,极大减少XML或Java配置。 - 内嵌Web容器:内置Tomcat, Jetty或Undertow,无需打包成WAR部署到外部容器,直接打成可执行的JAR包即可运行。
- 起步依赖 (Starter Dependencies):提供一组预置依赖描述符,简化Maven/Gradle配置(如
spring-boot-starter-web)。 - 生产就绪特性:提供监控端点(
actuator)、健康检查、外部化配置等开箱即用的生产特性。
6.2 Spring Cloud的核心组件及其功能?
参考答案:
Spring Cloud是一套分布式系统解决方案的集合,基于Spring Boot构建,用于快速开发分布式系统中的常见模式(如配置管理、服务发现、熔断器等)。
- 服务发现与注册:Eureka / Nacos。服务提供者向注册中心注册自己,消费者从注册中心发现服务。
- 客户端负载均衡:Ribbon / LoadBalancer。在服务消费者端实现软负载均衡。
- 声明式REST客户端:Feign / OpenFeign。基于接口和注解,更优雅地调用HTTP API。
- API网关:Zuul / Spring Cloud Gateway。统一的入口,提供路由、过滤、限流、安全等功能。
- 熔断器:Hystrix / Resilience4j / Sentinel。防止服务雪崩,提供服务降级和熔断机制。
- 分布式配置:Spring Cloud Config。集中化管理所有环境的配置文件。
如需获取更多实战面试题宝典(Spring/MySQL/Redis/MongoDB/Elasticsearch/Kafka等),请持续关注本专栏《面试题宝典》系列文章。
2308

被折叠的 条评论
为什么被折叠?



