一篇文章,带你回顾 Spring 的相关知识

8 阅读17分钟

Spring 框架是 Java 开发者必备的技能之一,掌握 Spring 的核心概念和原理对于面试至关重要。以下是一份全面且有深度的 Spring 相关面试题及答案,涵盖了从基础到高级的知识点。

1. 什么是 Spring 框架?它的主要模块有哪些?

答案: Spring 框架是一个开源的 Java/J2EE 应用程序框架,旨在简化企业级应用的开发。它提供了 IoC(控制反转)和 AOP(面向切面编程)的核心功能,以及对各种技术(如事务管理、MVC、JDBC、ORM 等)的良好支持。

主要模块包括:

  • Spring Core: 核心容器,提供 IoC 和依赖注入(DI)功能。
  • Spring AOP: 提供面向切面编程实现,用于解耦横切关注点。
  • Spring Data Access/Integration: 提供对各种数据访问技术的支持,如 JDBC、ORM(JPA、Hibernate)、JMS、JCA 等。
  • Spring Web: 包含 Spring MVC 模块,用于构建 Web 应用程序。
  • Spring Test: 提供对单元测试和集成测试的支持。
  • Spring Security: 提供认证和授权功能。
  • Spring Boot: 用于快速构建独立的、生产级别的 Spring 应用程序。
  • Spring Cloud: 用于构建分布式系统和微服务架构。

2. 解释一下 IoC(控制反转)和 DI(依赖注入)的区别和联系。

答案:

  • IoC(Inversion of Control - 控制反转): 是一种设计原则,指的是将对象的创建、组装和管理过程的控制权从应用程序代码中转移到 Spring 容器。以前,对象通常自己创建或查找它们依赖的对象;而现在,Spring 容器负责管理这些依赖。
  • DI(Dependency Injection - 依赖注入): 是实现 IoC 的一种具体方式。它意味着 Spring 容器在创建对象时,将该对象所依赖的其他对象注入(传递)给它,而不是让对象自己去创建或查找这些依赖。

联系与区别:

  • 联系: DI 是实现 IoC 的一种技术手段。没有 DI,IoC 就无法具体落地。可以说,IoC 是一个更高层面的概念,DI 是实现这个概念的一种模式。
  • 区别: IoC 是一种思想,而 DI 是一种实现方式。理解上,IoC 更强调“谁控制谁”以及“控制什么”,而 DI 更强调“如何实现控制”。

3. Spring 中 Bean 的作用域有哪些?分别适用于什么场景?

答案: Spring 中 Bean 的作用域定义了 Spring 容器如何创建和管理 Bean 实例。

  • Singleton(单例):

    • 描述: 默认作用域。在整个 Spring IoC 容器中,一个 Bean 定义只对应一个实例。所有对该 Bean 的请求都将返回同一个实例。
    • 场景: 无状态的 Bean(例如,服务层、DAO 层、工具类),因为它们不包含可变数据,共享一个实例不会造成问题,可以节省资源。
  • Prototype(原型):

    • 描述: 每次请求该 Bean 时,Spring 容器都会创建一个新的实例。
    • 场景: 有状态的 Bean(例如,每个用户会话都需要一个独立的购物车对象),或者需要每次都获得一个全新实例的 Bean。
  • Request(请求):

    • 描述: 针对 Web 应用。每次 HTTP 请求都会创建一个新的 Bean 实例,该实例只在当前请求的生命周期内有效。
    • 场景: Web 应用中与请求相关的 Bean,如处理表单数据的 Bean。
  • Session(会话):

    • 描述: 针对 Web 应用。每个用户会话都会创建一个新的 Bean 实例,该实例只在当前会话的生命周期内有效。
    • 场景: Web 应用中与用户会话相关的 Bean,如用户登录信息、购物车信息。
  • Application(应用):

    • 描述: 针对 Web 应用。整个 Web 应用只会创建一个 Bean 实例,该实例在 ServletContext 的生命周期内有效。
    • 场景: Web 应用中需要全局共享的 Bean,例如 Web 应用的配置信息。
  • WebSocket(WebSocket):

    • 描述: 针对 WebSocket 应用。每个 WebSocket 会话创建一个新的 Bean 实例。
    • 场景: WebSocket 应用中与 WebSocket 会话相关的 Bean。

4. Spring Bean 的生命周期是怎样的?

答案: Spring Bean 的生命周期涉及多个回调方法,Spring 容器在不同阶段会调用它们。

  1. 实例化(Instantiation): Spring 容器找到 Bean 的定义并创建 Bean 实例(通过构造函数)。

  2. 属性填充(Populate Properties): Spring 容器为 Bean 实例设置属性值(通过 setter 方法或字段注入)。

  3. BeanNameAware 接口(如果实现): 如果 Bean 实现了 BeanNameAware 接口,会调用 setBeanName() 方法,传入 Bean 的 ID。

  4. BeanFactoryAware 接口(如果实现): 如果 Bean 实现了 BeanFactoryAware 接口,会调用 setBeanFactory() 方法,传入当前的 BeanFactory 实例。

  5. ApplicationContextAware 接口(如果实现): 如果 Bean 实现了 ApplicationContextAware 接口,会调用 setApplicationContext() 方法,传入当前的 ApplicationContext 实例。

  6. BeanPostProcessor(前置处理): 如果容器中存在 BeanPostProcessor,会调用其 postProcessBeforeInitialization() 方法。

  7. InitializingBean 接口(如果实现): 如果 Bean 实现了 InitializingBean 接口,会调用其 afterPropertiesSet() 方法。

  8. 自定义初始化方法: 如果在 Bean 定义中配置了 init-method 属性或使用了 @PostConstruct 注解,会调用指定的初始化方法。

  9. BeanPostProcessor(后置处理): 如果容器中存在 BeanPostProcessor,会调用其 postProcessAfterInitialization() 方法。

  10. Bean 就绪(Ready for Use): Bean 现在可以被应用程序使用了。

  11. 容器关闭(Container Shutdown): 当 Spring 容器关闭时:

    • DisposableBean 接口(如果实现): 如果 Bean 实现了 DisposableBean 接口,会调用其 destroy() 方法。
    • 自定义销毁方法: 如果在 Bean 定义中配置了 destroy-method 属性或使用了 @PreDestroy 注解,会调用指定的销毁方法。

5. 解释一下 Spring AOP(面向切面编程)的核心概念和应用场景。

核心概念:

  • 横切关注点(Cross-cutting Concerns): 指那些分散在应用程序中多个模块的功能,如日志记录、事务管理、安全检查、性能监控等。它们与业务逻辑本身无关,但又无处不在。

  • 切面(Aspect): 横切关注点的模块化单元。它将关注点从业务逻辑中分离出来,通常由切点和通知组成。

  • 通知(Advice): 定义了在切点的特定连接点执行的动作。例如,在方法执行前、后或异常抛出时执行的代码。

    • 前置通知(@Before): 在目标方法执行前执行。
    • 后置通知(@AfterReturning): 在目标方法成功执行后(没有抛出异常)执行。
    • 异常通知(@AfterThrowing): 在目标方法抛出异常后执行。
    • 最终通知(@After): 在目标方法执行后(无论是否成功)执行。
    • 环绕通知(@Around): 包围目标方法执行,可以控制目标方法的调用,是最强大的通知类型。
  • 连接点(Joinpoint): 程序执行过程中可以插入切面的点。在 Spring AOP 中,通常是方法的调用。

  • 切点(Pointcut): 匹配连接点的表达式。它定义了哪些连接点应该被应用通知。例如,execution(* com.example.service.*.*(..)) 表示匹配 com.example.service 包下所有类的所有方法。

  • 目标对象(Target Object): 被一个或多个切面所通知的对象。

  • 织入(Weaving): 将切面应用到目标对象,创建新的代理对象的过程。Spring AOP 默认使用动态代理(JDK 动态代理或 CGLIB 代理)在运行时进行织入。

应用场景:

  • 日志记录: 在方法执行前后统一记录日志。
  • 事务管理: 声明式事务管理,通过注解或 XML 配置实现事务的开始、提交、回滚。
  • 性能监控: 统计方法的执行时间。
  • 权限控制: 在方法执行前进行权限校验。
  • 缓存: 在方法执行前检查缓存,或在方法执行后更新缓存。
  • 异常处理: 统一处理业务逻辑中抛出的异常。

6. Spring 中是如何实现事务管理的?声明式事务和编程式事务有什么区别?

答案: Spring 提供了强大的事务管理功能,支持编程式和声明式两种方式。

Spring 事务管理的抽象:

Spring 事务管理的核心是 PlatformTransactionManager 接口,它为各种事务策略(如 JDBC、JTA、JPA 等)提供了统一的抽象。

实现方式:

  • 声明式事务(Declarative Transaction Management):

    • 方式: 通过 XML 配置或注解(@Transactional)来实现。
    • 原理: 底层是 Spring AOP。当一个方法被标记为事务性时,Spring 会在运行时创建一个代理对象,并在该方法执行前后插入事务管理逻辑(开启事务、提交事务、回滚事务)。
    • 优点: 简单方便,代码侵入性低,将事务管理与业务逻辑分离,提高了可维护性。
    • 缺点: 粒度较粗,只能作用于方法级别。
    • 场景: 大多数情况下的推荐方式。
  • 编程式事务(Programmatic Transaction Management):

    • 方式: 在代码中手动调用事务管理 API(如 PlatformTransactionManager)。
    • 优点: 粒度更细,可以更灵活地控制事务的开始、提交和回滚。
    • 缺点: 代码侵入性强,增加了业务逻辑的复杂性。
    • 场景: 当声明式事务无法满足复杂事务需求时,例如需要在单个方法内部进行多个事务操作。

@Transactional 注解的属性:

  • propagation(传播行为): 定义了事务方法如何与当前事务关联。

    • REQUIRED(默认): 如果当前有事务,则加入该事务;如果没有,则创建一个新事务。
    • REQUIRES_NEW 总是创建一个新事务,并挂起当前事务(如果存在)。
    • SUPPORTS 如果当前有事务,则加入该事务;如果没有,则以非事务方式执行。
    • NOT_SUPPORTED 总是以非事务方式执行,并挂起当前事务(如果存在)。
    • NEVER 总是以非事务方式执行;如果当前有事务,则抛出异常。
    • MANDATORY 必须在现有事务中执行;如果没有,则抛出异常。
    • NESTED 如果当前有事务,则在嵌套事务中执行;如果没有,则创建一个新事务(与 REQUIRED 类似,但支持回滚到保存点)。
  • isolation(隔离级别): 定义了事务之间的隔离程度,以解决并发问题。

    • DEFAULT(默认): 使用底层数据库的默认隔离级别。
    • READ_UNCOMMITTED 允许读取未提交的数据(脏读),隔离级别最低,性能最好。
    • READ_COMMITTED 只能读取已提交的数据,避免脏读。
    • REPEATABLE_READ 保证在同一个事务中多次读取同一数据的结果一致,避免脏读和不可重复读。
    • SERIALIZABLE 最高的隔离级别,完全串行化执行,避免脏读、不可重复读和幻读,但性能最差。
  • timeout 事务超时时间(秒),如果事务在指定时间内没有完成,则自动回滚。

  • readOnly 标识事务是否为只读。对于只读事务,底层数据库可能会进行优化。

  • rollbackFor 指定哪些异常会导致事务回滚。

  • noRollbackFor 指定哪些异常不会导致事务回滚。


7. Spring MVC 的工作原理是怎样的?

答案: Spring MVC 是一个基于 MVC(Model-View-Controller)模式的 Web 框架。其工作流程如下:

  1. 用户发送请求: 用户在浏览器中输入 URL,发送 HTTP 请求到 Web 服务器。

  2. DispatcherServlet 接收请求: Web 服务器(如 Tomcat)接收到请求后,将其发送给 DispatcherServlet(Spring MVC 的核心控制器)。DispatcherServlet 是一个前端控制器,负责协调整个请求处理流程。

  3. HandlerMapping 查找处理器: DispatcherServlet 根据请求的 URL,委托 HandlerMapping 查找能够处理该请求的处理器(Controller)。HandlerMapping 会将请求映射到对应的 Handler(处理器方法)。

  4. HandlerAdapter 调用处理器: DispatcherServlet 找到对应的 Handler 后,会委托 HandlerAdapter 调用 Handler 的处理方法。HandlerAdapter 负责适配不同类型的处理器(例如,适配基于注解的 Controller)。

  5. Controller 处理请求: Controller 中的业务逻辑处理请求,可能调用服务层(Service)和数据访问层(DAO)来获取或处理数据。处理完成后,Controller 返回一个 ModelAndView 对象或一个字符串(表示视图名称)。

    • ModelAndView 包含模型数据(需要传递给视图的数据)和视图名称。
  6. ViewResolver 解析视图: DispatcherServlet 接收到 Controller 返回的视图名称后,会委托 ViewResolver 解析视图名称,将其解析成一个具体的 View 对象。例如,将逻辑视图名 "home" 解析为 "/WEB-INF/views/home.jsp"。

  7. View 渲染: View 对象接收到 Model 数据后,进行渲染(例如,将数据填充到 JSP 页面中),生成最终的响应页面。

  8. DispatcherServlet 响应: DispatcherServlet 将渲染好的页面返回给用户。

核心组件:

  • DispatcherServlet 前端控制器,负责整个请求的分发和协调。
  • HandlerMapping 负责将请求映射到对应的处理器。
  • HandlerAdapter 负责调用处理器的方法。
  • Controller 业务逻辑处理器,处理请求并返回 ModelAndView 或视图名称。
  • ViewResolver 负责将逻辑视图名解析为具体的视图对象。
  • View 负责渲染视图,生成响应页面。

8. Spring Boot 有哪些核心特性?为什么选择 Spring Boot?

答案: Spring Boot 是一个用于快速构建独立的、生产级别的 Spring 应用程序的框架。

核心特性:

  • 内嵌服务器: 内置 Tomcat、Jetty 或 Undertow 等服务器,无需单独部署 WAR 包,可以直接以 JAR 包运行。
  • 自动化配置(Auto-Configuration): Spring Boot 根据项目中的依赖自动配置 Spring 应用程序。例如,如果检测到 spring-webmvc 依赖,就会自动配置 Spring MVC;如果检测到 spring-data-jpa 依赖,就会自动配置 JPA。
  • 起步依赖(Starter Dependencies): 提供了一系列 spring-boot-starter-* 的依赖,它们预先配置了常用的库和版本,简化了依赖管理。例如,spring-boot-starter-web 包含了 Spring MVC、Tomcat 等。
  • 外部化配置: 支持使用 application.propertiesapplication.yml 文件进行外部化配置,方便在不同环境中部署。
  • 生产就绪特性: 提供 Actuator,用于监控、管理和审计应用程序,以及其他生产级别功能,如指标收集、健康检查等。
  • 约定优于配置: 倾向于使用合理的默认配置,减少开发人员的配置工作量。

为什么选择 Spring Boot?

  • 简化配置: 大大减少了 XML 配置和繁琐的 Java 配置,通过自动化配置和起步依赖,让开发者更专注于业务逻辑。
  • 快速开发: 内嵌服务器、开箱即用的特性以及快速搭建脚手架的能力,显著提升了开发效率。
  • 易于部署: 可以打包成独立的 JAR 文件,直接运行,无需外部应用服务器。
  • 微服务支持: 是构建微服务架构的理想选择,其轻量级和独立部署的特性与微服务理念高度契合。
  • 生态系统完善: 继承了 Spring 框架强大的生态系统,拥有丰富的第三方集成和社区支持。
  • 生产级别特性: 提供了监控、健康检查等生产就绪的功能,方便应用程序的运维。

9. Spring Cloud 是什么?它解决了哪些问题?

答案: Spring Cloud 是一系列框架的集合,它基于 Spring Boot,用于构建分布式系统和微服务架构。它提供了一套工具和模式,简化了在分布式环境中开发、部署和管理应用程序的复杂性。

解决了哪些问题?

在构建微服务架构时,会面临一系列挑战,Spring Cloud 针对这些问题提供了解决方案:

  • 服务注册与发现(Service Registration and Discovery):

    • 问题: 微服务数量庞大,服务实例动态变化(启动、停止、扩缩容),服务消费者如何找到服务提供者?
    • 解决方案: Eureka、Consul、Zookeeper 等,服务提供者将自己注册到注册中心,服务消费者从注册中心获取服务地址。
  • 负载均衡(Load Balancing):

    • 问题: 如何将服务消费者的请求均匀地分发到多个服务提供者实例上,以提高吞吐量和可用性?
    • 解决方案: Ribbon(客户端负载均衡),与服务发现结合使用。
  • 服务调用(Service Invocation):

    • 问题: 如何简化不同微服务之间的 HTTP 或 RPC 调用?
    • 解决方案: OpenFeign(声明式 REST 客户端),通过接口和注解实现服务调用,无需手动构建 HTTP 请求。
  • API 网关(API Gateway):

    • 问题: 如何统一管理所有微服务的入口、路由、认证、鉴权、限流等功能?
    • 解决方案: Spring Cloud Gateway、Zuul,提供统一的入口,对外暴露接口。
  • 容错与熔断(Fault Tolerance and Circuit Breaker):

    • 问题: 当某个微服务出现故障或响应慢时,如何防止故障扩散,避免整个系统雪崩?
    • 解决方案: Hystrix(已进入维护模式,推荐使用 Resilience4j),通过服务熔断、降级、隔离等机制提高系统的弹性。
  • 分布式配置(Distributed Configuration):

    • 问题: 如何集中管理和动态更新所有微服务的配置信息?
    • 解决方案: Spring Cloud Config,通过 Git 等版本控制系统管理配置,并支持配置的动态刷新。
  • 消息总线(Message Bus):

    • 问题: 如何实现配置的动态刷新和微服务之间的事件通知?
    • 解决方案: Spring Cloud Bus,通过消息代理(如 RabbitMQ、Kafka)实现配置更新通知。
  • 链路追踪(Distributed Tracing):

    • 问题: 在分布式系统中,一个请求可能经过多个微服务,如何追踪请求的整个调用链,进行故障排查和性能分析?
    • 解决方案: Spring Cloud Sleuth 结合 ZipkinSkyWalking

10. Spring 中有哪些常用的设计模式?

答案: Spring 框架广泛使用了各种经典的设计模式,这些模式是其健壮性、可扩展性和灵活性的基石。

  • 工厂模式(Factory Pattern):

    • 应用: Spring IoC 容器就是典型的工厂模式,BeanFactoryApplicationContext 负责创建和管理 Bean 实例。
    • 作用: 将对象的创建与使用分离,降低耦合度。
  • 单例模式(Singleton Pattern):

    • 应用: Spring 默认将 Bean 定义为单例模式,即每个 Bean 定义只对应一个实例。
    • 作用: 确保一个类只有一个实例,并提供一个全局访问点,节省资源。
  • 代理模式(Proxy Pattern):

    • 应用: Spring AOP 的实现就是基于代理模式。当对目标对象进行增强时,Spring 会为目标对象创建一个代理对象,通过代理对象来执行增强逻辑。
    • 作用: 在不修改原有代码的基础上,为目标对象添加新的功能。
  • 模板方法模式(Template Method Pattern):

    • 应用: Spring 提供了许多模板类来简化重复性操作,例如 JdbcTemplateHibernateTemplateJmsTemplate 等。这些模板类定义了操作的骨架,但具体实现细节由用户提供。
    • 作用: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
  • 观察者模式(Observer Pattern):

    • 应用: Spring 的事件发布机制(ApplicationContextEventApplicationListener)就是基于观察者模式。
    • 作用: 当一个对象状态改变时,所有依赖它的对象都会得到通知并自动更新。
  • 适配器模式(Adapter Pattern):

    • 应用: Spring MVC 中的 HandlerAdapter 就是适配器模式的应用。它将不同类型的处理器(Controller)适配成统一的接口,方便 DispatcherServlet 调用。
    • 作用: 允许接口不兼容的类一起工作。
  • 装饰器模式(Decorator Pattern):

    • 应用: Spring 中,许多 Bean 都会被各种 BeanPostProcessor 增强,这些 BeanPostProcessor 可以在 Bean 的初始化前后对 Bean 进行“装饰”或修改。
    • 作用: 动态地给一个对象添加一些额外的职责。
  • 策略模式(Strategy Pattern):

    • 应用: Spring 在选择不同的实现策略时会用到,例如事务管理中的不同事务传播行为和隔离级别,或者资源加载中的不同资源加载策略。
    • 作用: 定义一系列算法,将它们封装起来,并且使它们可以相互替换。