(1)说说 Spring 启动过程?

Spring 容器的启动过程可以概括为以下几个核心步骤:

1. 初始化容器:创建 ApplicationContext 实例(如 AnnotationConfigApplicationContext),这会触发容器启动。

2. 加载与解析配置:容器扫描指定的配置源(如配置类@Configuration),解析 @ComponentScan 等注解,将 Bean 的定义信息(BeanDefinition)收集并注册到 BeanFactory 中。

3. 执行 BeanFactoryPostProcessor:调用所有 BeanFactoryPostProcessorBeanDefinition 进行修改或扩展。这是容器的一个重要扩展点。

4. 实例化 Bean:容器(非懒加载的单例)通过反射创建 Bean 的实例。

5. 依赖注入:按照 BeanDefinition 中的信息,为 Bean 注入所需的属性或依赖(@Autowired)。

6. 执行 BeanPostProcessor:调用所有 BeanPostProcessor 对 Bean 实例进行增强。AOP 代理就是在此阶段生成的

7. 容器就绪:完成所有单例 Bean 的初始化,容器启动完毕,应用程序可以正常使用这些 Bean。

简单记忆:初始化 -> 加载配置注册Bean定义 -> [BeanFactory后处理] -> 实例化 -> 依赖注入 -> [Bean后处理] -> 就绪。

(2)你了解的 Spring 都用到哪些设计模式?

Spring 框架中大量使用了设计模式,以下是一些最典型的:

1. 工厂模式 (Factory)BeanFactoryApplicationContext 核心就是容器工厂,用于创建和管理 Bean 对象。

2. 单例模式 (Singleton):Spring Bean 的默认作用域就是单例,容器中只存在一个实例。

3. 代理模式 (Proxy)AOP 的实现基础。Spring 使用 JDK 动态代理和 CGLIB 代理来增强目标类。

4. 模板方法模式 (Template Method)JdbcTemplateRestTemplate 等,封装了固定流程,将易变部分(如 SQL 语句)通过回调接口暴露给用户。

5. 观察者模式 (Observer)Spring 事件机制ApplicationEvent 代表事件,ApplicationListener 是观察者,ApplicationContext 是事件发布者。

6. 适配器模式 (Adapter):Spring MVC 中的 HandlerAdapter,使得多种类型的 Controller(如 @ControllerServlet)能够被统一调用。

7. 策略模式 (Strategy):在资源加载 (ResourceLoader)、AOP 代理创建等方面,根据不同情况使用不同的实现策略。

8. 装饰器模式 (Decorator):例如 HttpServletRequest 的包装类,用于增强原始对象的功能。

一句话总结:Spring 本身就是一个设计模式的最佳实践教科书。

(3)Spring 有哪几种事务传播行为?

Spring 事务定义了 7 种传播行为,用来规定当多个事务方法相互调用时,事务应该如何传播。

核心的 3 种:

1. REQUIRED(默认)

  • 如果当前存在事务,则加入该事务。
  • 如果当前没有事务,则创建一个新的事务。

2. REQUIRES_NEW

  • 总是创建一个新的事务,并暂停当前事务(如果存在)。
  • 两个事务互不干扰,独立提交或回滚。

3. NESTED

  • 如果当前存在事务,则在嵌套事务中执行。
  • 嵌套事务是外部事务的一部分,只有外部事务提交时,它才会提交。它有自己的回滚点,可以单独回滚而不影响外部事务。

其他 4 种:

1. SUPPORTS:如果当前存在事务,则加入;如果当前没有事务,就以非事务方式执行。
2. NOT_SUPPORTED:以非事务方式执行,并暂停当前事务(如果存在)。
3. MANDATORY:强制必须存在一个事务,否则抛出异常。
4. NEVER:强制必须不能存在任何事务,否则抛出异常。

一句话总结:
传播行为定义了事务的边界。最常用的是 REQUIRED(同生共死)和 REQUIRES_NEW(另起炉灶)。

(4)说说 Spring Boot 的启动流程?

Spring Boot 的启动流程可以简化为以下几个核心步骤:

1. 启动入口:执行 SpringApplication.run(...) 方法。

2. 初始化

  • 创建 SpringApplication 实例。
  • 进行初始化配置:推断应用类型(Servlet/Reactive)、加载 ApplicationContextInitializerApplicationListener(监听器)。

3. 运行应用

  • 创建并准备环境:加载配置文件(如 application.properties)、命令行参数等。
  • 创建应用上下文:根据应用类型创建 ApplicationContext(例如,对于 Web 应用,创建 AnnotationConfigServletWebServerApplicationContext)。
  • 准备上下文:设置环境、执行 ApplicationContextInitializer、加载 Bean 定义(主要是通过 @ComponentScan 和自动配置)。
  • 刷新上下文:这是最核心的一步,调用 AbstractApplicationContext.refresh() 方法。这个过程完成了传统 Spring 容器的启动,包括 Bean 工厂设置、Bean 的实例化、依赖注入、执行 BeanPostProcessor 等。
  • afterRefresh 回调:容器刷新完成后执行的一些扩展回调。
  • 调用 CommandLineRunnerApplicationRunner:执行应用中定义的这些 Runner 接口实现。

4. 启动完成:此时,内嵌的 Web 服务器(如 Tomcat)已经启动,应用开始监听指定端口,等待请求。

简单记忆:初始化配置 -> 准备环境 -> 创建上下文 -> 刷新上下文(核心)-> 执行Runner -> 启动完成。

(5)Spring Boot 是如何实现自动配置的?

Spring Boot 自动配置的核心原理是 @EnableAutoConfiguration 注解,其实现过程可以概括为以下几个关键步骤:

1. 启动注解@SpringBootApplication 注解包含了 @EnableAutoConfiguration,它开启了自动配置。

2. 加载配置@EnableAutoConfiguration 会通过 @Import 导入 AutoConfigurationImportSelector 类。

3. 选择配置类:这个类会从 META-INF/spring.factories 文件中加载所有预先定义好的全限定配置类名(例如 org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration)。

4. 条件化装配:这些自动配置类上都有 @Conditional 系列注解(如 @ConditionalOnClass, @ConditionalOnProperty, @ConditionalOnMissingBean)。Spring 会根据当前项目的类路径、配置文件、已存在的 Bean 等条件来判断是否应该启用该配置。

5. 创建 Bean:如果一个自动配置类的所有条件都满足,它内部定义的 Bean(例如 DataSource, RedisTemplate)就会被创建并加入到 Spring 容器中。

举个例子RedisAutoConfiguration 配置类上可能有 @ConditionalOnClass(RedisClient.class),意思是:只有当项目的类路径下存在 RedisClient 这个类时,这个自动配置才会生效

一句话总结:Spring Boot 通过读取 spring.factories 文件中的预置配置类,并根据一系列条件注解(@Conditional)动态地决定是否将这些配置类中的Bean加载到容器中,从而实现“开箱即用”。

(6)如何理解 Spring Boot 中的 starter?

在 Spring Boot 中,Starter 是一组有用的依赖集合,用于简化构建配置。Spring Boot 通过 Starter POMs 提供了各种常用功能模块的集成,开发者只需引入一个 Starter 依赖,便可以自动获得该模块的所有相关依赖和配置,而无需手动添加多个依赖库和配置文件。

有点像我们去买一些必需品,然后有人打包成一个套餐售卖,我们就不需要一个一个再去找了,直接一次性打包拿走,节省了时间。

(7)Spring Boot 是如何通过 main 方法启动 web 项目的?

Spring Boot 通过 main 方法启动 Web 项目的核心在于 内嵌式 Servlet 容器

启动流程可以简化为以下几步:

1. 执行 main 方法:调用 SpringApplication.run(Application.class, args)

2. 启动 Spring 容器

  • 这个方法会初始化 Spring 容器(ApplicationContext),完成所有 Bean 的创建和装配,包括自动配置。

3. 自动配置内嵌服务器

  • Spring Boot 的自动配置模块(如 TomcatServletWebServerFactoryAutoConfiguration)会检测到类路径下有 Servlet API 和相关服务器(如 Tomcat、Jetty),于是自动创建一个内嵌的 Servlet 容器实例(例如 Tomcat)。

4. 启动服务器并部署应用

  • 容器刷新过程中,会从容器中获取 ServletWebServerFactory 并调用其 getWebServer() 方法。
  • 这个方法会创建内嵌的 Tomcat 等服务器实例,并将我们的 Spring MVC 应用(DispatcherServlet部署到它上面。
  • 最后,启动这个内嵌服务器,使其开始在指定端口(默认 8080)监听 HTTP 请求。

一句话总结:Spring Boot 的 main 方法通过启动 Spring 容器,触发自动配置来创建并启动一个内嵌的 Servlet 容器(如Tomcat),从而将整个应用作为一个独立的 Java 进程运行,无需部署到外部 Web 服务器。

(8)Spring Boot 的核心特性有哪些?

好的。Spring Boot 的核心特性可以概括为以下几点:

1. 自动配置 (Auto-configuration):根据项目的类路径、已有的 Bean 和配置,自动配置 Spring 应用,实现了“约定优于配置”,大部分情况无需手动编写配置。

2. 起步依赖 (Starter Dependencies): 提供了一系列预定义的依赖描述符(如 spring-boot-starter-web),只需引入一个起步依赖,就能获得开发某种功能(如 Web、JPA)所需的所有依赖库,解决了依赖冲突和版本管理问题。

3. 内嵌服务器 (Embedded Server):将 Tomcat、Jetty 或 Undertow 等 Servlet 容器内嵌到应用中,使得应用可以被打包成一个独立的 JAR 文件,并通过 java -jar 命令直接运行,无需部署到外部 Web 服务器。

4. 生产就绪 (Production-Ready):提供了一系列用于监控和管理生产环境的工具,如Actuator(可以查看应用健康状态、指标等信息)。

一句话总结:Spring Boot 通过自动配置、起步依赖和内嵌服务器三大核心特性,极大地简化了基于 Spring 的独立应用和微服务开发,实现了开箱即用和快速部署。

(9)什么是 Spring Boot?

Spring Boot 是一个基于 Spring 框架的快速开发脚手架

它的核心设计目标是:简化 Spring 应用的初始搭建开发过程

它通过以下方式实现这一目标:

  1. 自动配置:无需编写繁琐的 XML 配置,大多数功能都可以“开箱即用”。
  2. 起步依赖:提供预定义的依赖组合,简化 Maven/Gradle 配置,解决版本冲突。
  3. 内嵌服务器:将 Tomcat 等服务器内嵌,允许你将应用打包成一个可独立运行的 JAR 文件

一句话总结:Spring Boot 让开发者能快速创建出具备生产级别的、独立运行的 Spring 应用程序。

(10)什么是 Spring IOC?

Spring IoC(控制反转) 是 Spring 框架的核心。

1. 核心思想:控制权的转移

  • 传统方式:程序由你在代码中“主动”创建和管理对象(new Object())。
  • IoC 方式:将对象的创建和依赖关系的控制权,从程序员手中“反转”交给了 Spring IoC 容器 来统一管理。

2. 实现方式:依赖注入 (DI)

IoC 是一种设计思想,而 依赖注入 (Dependency Injection) 是它的实现方式。容器通过依赖注入的方式来管理组件之间的依赖关系,而不是由组件自己创建或查找依赖。

3. 举个例子:

假设 A 类依赖于 B 类。

  • 没有 IoC:你需要在 A 的代码里 new B()
  • 有 IoC:你只需要在 A 中声明“我需要一个 B”(比如通过 @Autowired 注解)。Spring 容器会自动创建好 B 的实例,并注入(装配)到 A

总结一句话:
Spring IoC 就是一个容器,它负责创建、组装并管理你应用中所有的对象(称为 Bean),并通过依赖注入的方式将它们联系起来。

(11)Spring AOP 默认用的是什么动态代理,两者的区别?

Spring AOP 默认的动态代理策略是:

如果目标类实现了接口,则使用 JDK 动态代理;否则,使用 CGLIB 动态代理。

两者的核心区别:

特性 JDK 动态代理 CGLIB 动态代理
机制 基于接口 基于类继承
要求 目标类必须至少实现一个接口 目标类不能是 final(因为要继承)
性能 生成较快,早期执行稍慢 生成较慢,但执行速度一般更快
依赖 Java 标准库自带 需要引入 CGLIB 库

简单来说:JDK 代理要求有接口,它创建接口的代理实现;CGLIB 代理无此要求,它通过生成目标类的子类来代理。

(12)什么是 AOP?

AOP(面向切面编程) 是一种编程范式,用于将那些遍布在多个业务逻辑中的公共功能(称为横切关注点) 从业务代码中剥离出来,进行独立管理和集中处理。

核心概念:

  • 横切关注点:像日志、事务、安全校验等,它们与核心业务逻辑无关,但又是多个模块都需要用到的功能。
  • 核心思想:在不修改原有业务代码的前提下,通过“动态代理”技术,在程序运行的特定位置(如方法调用前、后)织入这些通用功能。

举个例子:
你有很多业务方法(下单、查询、付款),都需要记录日志。

  • 没有 AOP:你需要在每个方法里都写 log.info(...)
  • 有 AOP:你只定义一个日志切面。Spring AOP 会自动在所有这些方法执行前后,帮你加上日志代码。

一句话总结:
AOP 让关注点分离,使我们能专注于核心业务,同时优雅地实现通用功能。

(13)看过源码吗?说下 Spring 由哪些重要的模块组成?

Spring 框架主要由以下重要模块组成,像一个分层架构:

1. 核心容器 (Core Container)

  • Spring-Core:最基础模块,提供IoC(控制反转)功能。
  • Spring-Beans:负责Bean的创建、配置和管理。
  • Spring-Context:在Core和Beans之上,提供企业级服务(应用上下文、事件传播等)。
  • Spring-SpEL:提供强大的表达式语言。

2. AOP与切面 (AOP and Aspects)

  • Spring-AOP:提供面向切面编程的实现。
  • Spring-Aspects:集成AspectJ框架。

3. 数据访问与集成 (Data Access/Integration)

  • Spring-JDBC:提供了JDBC的抽象层,简化JDBC操作。
  • Spring-TX:支持强大的声明式和编程式事务管理。
  • Spring-ORM:集成主流ORM框架,如Hibernate、JPA。

4. Web层 (Web)

  • Spring-Web:提供基本的Web集成功能。
  • Spring-WebMVC:提供了MVC模式实现的Web框架。

5. 测试 (Test)

  • Spring-Test:提供了对单元测试和集成测试的出色支持。

简单来说,就是从核心容器出发,通过AOP增强功能,并由数据访问Web等模块提供具体领域支持,测试模块贯穿全程。

(14)什么是循环依赖(常问)?

循环依赖是指两个或多个模块、对象实例之间存在直接或间接的相互依赖关系,形成一个闭环 。在软件开发中,这是一种常见问题,它可能会导致代码难以理解、维护、测试和重用。具体类型如下:

  • 直接循环依赖:两个模块直接相互依赖,比如模块A的代码中引入了模块B,而模块B的代码中又引入了模块A 。
  • 间接循环依赖:三个或更多模块形成一个依赖闭环,例如模块A依赖模块B,模块B依赖模块C,而模块C又依赖模块A。

在Spring框架里,循环依赖主要指两个或两个以上的Bean互相依赖对方,最终形成闭环。比如Bean A依赖于Bean B,而Bean B又依赖于Bean A。

(15)Spring 如何解决循环依赖?(???)

关键就是提前暴露未完全创建完毕的 Bean

在 Spring 中主要是使用三级缓存来解决了循环依赖:

  • 一级缓存(Singleton Objects Map):用于存储完全初始化完成的单例Bean。
  • 二级缓存(Early Singleton Objects Map):用于存储尚未完全初始化,但已实例化的Bean,用于提前暴露对象,避免循环依赖问题。
  • 三级缓存(Singleton Factories Map):用于存储对象工厂,当需要时,可以通过工厂创建早期Bean(特别是为了支持AOP代理对象的创建)。

解决步骤

  • Spring 首先创建 Bean 实例,并将其加入三级缓存中(Factory)。
  • 当一个 Bean 依赖另一个未初始化的 Bean 时,Spring 会从三级缓存中获取 Bean 的工厂,并生成该 Bean 的对象(若有代理则是代理对象)。
  • 代理对象存入二级缓存,解决循环依赖。
  • 一旦所有依赖 Bean 被完全初始化,Bean 将转移到一级缓存中。

(16)为什么 Spring 循环依赖需要三级缓存,二级不够吗?(???)

Spring 之所以需要三级缓存而不是简单的二级缓存,主要原因在于AOP代理Bean的早期引用问题

二级缓存虽然可以解决循环依赖的问题,但在涉及到动态代理(AOP) 时,直接使用二级缓存不做任何处理会导致我们拿到的 Bean 是未代理的原始对象。如果二级缓存内存放的都是代理对象,则违反了 Bean 的生命周期(AOP 代理就在初始化后阶段生成)。

(17)说下 Spring Bean 的生命周期?

Spring Bean 的生命周期可以概括为以下几个核心步骤:

1. 实例化:容器通过构造函数或工厂方法创建 Bean 的实例。

2. 属性赋值:容器注入 Bean 的依赖(通过 Setter 或字段注入)。

3. 初始化前:调用所有 BeanPostProcessorpostProcessBeforeInitialization 方法。

4. 初始化:如果 Bean 实现了 InitializingBean 接口,则调用 afterPropertiesSet 方法;如果配置了 init-method,则调用指定的方法。

5. 初始化后:调用所有 BeanPostProcessorpostProcessAfterInitialization 方法。AOP 代理就在此阶段生成

6. 就绪:此时 Bean 已准备就绪,存在于应用上下文中,可以被使用。

7. 销毁:容器关闭时,如果 Bean 实现了 DisposableBean 接口,则调用 destroy 方法;如果配置了 destroy-method,则调用指定的方法。

简单记忆:实例化 -> 属性填充 -> [初始化前] -> 初始化 -> [初始化后] -> 使用 -> 销毁

(18)Spring MVC 具体的工作原理?

Spring MVC 的工作原理,核心是 DispatcherServlet 的处理流程,可以分为以下步骤:

1. 发送请求:用户发起请求,被 DispatcherServlet(前端控制器)统一接收。

2. 映射处理器DispatcherServlet 询问 HandlerMapping:“该请求由哪个 Controller 处理?” HandlerMapping 返回对应的处理器(也叫 Handler)。

3. 调用处理器DispatcherServlet 通过 HandlerAdapter真正调用上一步找到的处理器方法。

4. 执行控制器Controller 执行业务逻辑,返回一个包含视图名和模型数据的 ModelAndView 对象。

5. 解析视图DispatcherServlet 将视图名交给 ViewResolver(视图解析器)进行解析,得到具体的 View 对象(如 JSP、Thymeleaf模板)。

6. 渲染视图View 对象结合 Model 中的数据,进行视图渲染,生成最终的响应内容(如 HTML)。

7. 返回响应DispatcherServlet 将渲染结果返回给客户端。

简单记忆:

DispatcherServlet 收到请求 -> HandlerMapping 找谁处理 -> HandlerAdapter 去调用 -> Controller 处理返回结果 -> ViewResolver 找视图 -> View 渲染 -> 返回响应

(19)Spring 中的 DI 是什么?

DI(Dependency Injection,依赖注入)是IOC思想的具体实现方式:
容器在创建Bean时,自动将其依赖的对象(另一个 Bean、配置值等)注入到当前Bean中,替代手动new或查找依赖。

常见注入方式:构造器注入、Setter注入、字段注入

DI与IOC的关系:

  • IOC是思想:强调“控制权转移”(容器管对象)。
  • DI是手段:通过“注入依赖”的方式,实现IOC的思想落地。
    简单说:IOC 是“要解决什么问题”,DI 是“具体怎么解决”。

Spring IOC 是“让容器接管对象控制权”的设计思想,通过 DI(依赖注入)的方式实现对象创建、依赖管理,最终带来解耦、易维护、可测试的好处,是 Spring 框架的核心基石。

更多推荐