提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- [Spring、SpringMVC、SpringBoot 知识整理](#Spring、SpringMVC、SpringBoot 知识整理)
-
- [一、Spring 部分](#一、Spring 部分)
-
- [1. Spring IoC 和 AOP 的区别](#1. Spring IoC 和 AOP 的区别)
- [2. Spring 的 AOP](#2. Spring 的 AOP)
- [3. IoC 和 AOP 是通过什么机制实现的?](#3. IoC 和 AOP 是通过什么机制实现的?)
- [4. 怎么理解 Spring IoC?](#4. 怎么理解 Spring IoC?)
- [5. 依赖倒置、依赖注入、控制反转分别是什么?](#5. 依赖倒置、依赖注入、控制反转分别是什么?)
- [6. Spring AOP 主要想解决什么问题?](#6. Spring AOP 主要想解决什么问题?)
- [7. 动态代理和静态代理的区别](#7. 动态代理和静态代理的区别)
- [8. Spring 是如何解决循环依赖的?](#8. Spring 是如何解决循环依赖的?)
- [二、SpringMVC 部分](#二、SpringMVC 部分)
-
- [1. MVC 分层](#1. MVC 分层)
- [2. SpringMVC 的处理流程](#2. SpringMVC 的处理流程)
- [三、SpringBoot 部分](#三、SpringBoot 部分)
-
- [1. SpringBoot 比 Spring 好在哪里?](#1. SpringBoot 比 Spring 好在哪里?)
- [2. SpringBoot 用到哪些设计模式?](#2. SpringBoot 用到哪些设计模式?)
- [3. SpringBoot 自动装配原理是什么?](#3. SpringBoot 自动装配原理是什么?)
- [4. SpringBoot 过滤器和拦截器](#4. SpringBoot 过滤器和拦截器)
- 最后总结
Spring、SpringMVC、SpringBoot 知识整理
一、Spring 部分
1. Spring IoC 和 AOP 的区别
Spring 里最常被问到的两个核心就是 IoC 和 AOP。它们都是为了解耦,但是解耦的方向不一样。
IoC 解决的是对象之间的依赖问题。以前写代码时,如果 UserService 需要 UserDao,可能会在类里面直接 new UserDao()。这样写虽然直接,但是一旦 Dao 的实现换了,Service 也要跟着改。Spring IoC 的做法是把对象创建和依赖维护交给容器,业务类只需要声明自己需要什么。
AOP 解决的是公共逻辑重复的问题。比如日志、事务、权限校验这些功能,很多业务方法都需要。如果每个方法里都手写一遍,代码会很乱。AOP 就是把这类横向逻辑抽出来,统一织入到方法执行前后。
可以这样记:
| 内容 | IoC | AOP |
|---|---|---|
| 关注点 | 对象创建和依赖管理 | 方法增强和公共逻辑抽取 |
| 解决的问题 | 类之间耦合太强 | 日志、事务、权限等代码到处重复 |
| 典型实现 | Spring 容器、依赖注入 | 动态代理 |
| 典型场景 | @Component、@Autowired、@Bean |
@Transactional、日志切面、权限切面 |
个人理解:IoC 让对象不用自己找依赖,AOP 让业务方法不用自己写一堆额外逻辑。
2. Spring 的 AOP
Spring AOP 是 Spring 对面向切面编程的实现。它主要基于代理对象工作,也就是说,外部调用的很多时候并不是原始对象,而是 Spring 创建出来的代理对象。
举个常见例子,方法上加了 @Transactional:
java
@Transactional
public void createOrder() {
// 保存订单
// 扣减库存
}
表面上看只是加了一个注解,实际上 Spring 会通过 AOP 在方法执行前开启事务,在方法执行成功后提交事务,如果抛出异常就回滚事务。
Spring AOP 里有几个概念需要区分:
Aspect:切面,把公共增强逻辑放在一起,比如事务切面、日志切面。JoinPoint:连接点,在 Spring AOP 中通常就是方法调用。Pointcut:切点,用来匹配哪些方法需要增强。Advice:通知,也就是具体增强逻辑。Target:目标对象,被代理的原始对象。Proxy:代理对象,真正被外部调用的对象。
通知的执行时机也比较重要:
Before:目标方法执行前。After:目标方法执行后,不管是否有异常。AfterReturning:目标方法正常返回后。AfterThrowing:目标方法抛异常后。Around:环绕通知,功能最强,可以控制方法是否继续执行。
Spring AOP 更适合做方法级别的增强。如果要对构造器、字段或更底层字节码做增强,就不是 Spring AOP 最擅长的范围了,通常会考虑 AspectJ。
3. IoC 和 AOP 是通过什么机制实现的?
IoC 的实现可以从 Spring 容器启动过程理解。
Spring 启动后,会先读取配置来源,比如 XML、注解、配置类等,然后把这些信息解析成 BeanDefinition。BeanDefinition 可以理解成 Bean 的说明书,里面记录了类名、作用域、依赖关系、初始化方法等信息。
之后 Spring 根据这些 Bean 定义创建对象。创建对象时通常会用到反射,然后再进行依赖注入。比如一个 Service 依赖 Dao,Spring 会找到对应 Dao 对象,再注入到 Service 中。最后还会执行一些初始化逻辑,比如 BeanPostProcessor、初始化方法等。
所以 IoC 背后的关键点是:
- 配置元数据。
- BeanDefinition。
- 反射创建对象。
- 依赖注入。
- Bean 生命周期管理。
AOP 的实现核心是代理。
Spring 在创建 Bean 的过程中,会判断这个 Bean 是否需要增强。如果需要增强,Spring 最终暴露出去的就不是普通原始对象,而是代理对象。调用者调用代理对象的方法时,代理对象会先执行切面逻辑,再调用真正的目标方法。
Spring AOP 常见代理方式有两种:
-
JDK 动态代理
目标类实现了接口时,可以基于接口创建代理对象。
-
CGLIB 动态代理
目标类没有实现接口时,可以生成目标类的子类作为代理对象。
简单说,IoC 主要靠容器和反射管理对象,AOP 主要靠代理增强对象。
4. 怎么理解 Spring IoC?
IoC 的全称是 Inversion of Control,翻译为控制反转。这个名字听起来比较抽象,实际可以理解成:对象的创建权和依赖管理权,从程序员手里的代码转移到了 Spring 容器手里。
没有 IoC 时,代码可能是这样的:
java
public class OrderService {
private OrderDao orderDao = new OrderDao();
}
这样的问题是 OrderService 和 OrderDao 绑定得太死。如果以后换成 JdbcOrderDao 或 MyBatisOrderDao,Service 代码也要改。
使用 Spring 后,Service 只声明依赖:
java
@Service
public class OrderService {
@Autowired
private OrderDao orderDao;
}
OrderDao 到底是谁创建的、什么时候创建的、注入哪个实现类,这些都由 Spring 管。业务类不用关心对象组装过程,只专心写业务逻辑。
我觉得 IoC 的价值主要有三个:
- 降低类和类之间的耦合。
- 方便替换实现,符合面向接口编程思想。
- 方便测试,比如可以注入 Mock 对象。
所以 IoC 不是某一个注解,而是一种容器管理对象的思想。@Autowired、构造器注入、@Bean 这些只是它的具体使用方式。
5. 依赖倒置、依赖注入、控制反转分别是什么?
这三个概念容易混在一起,但层次不同。
依赖倒置是一条设计原则。它强调高层模块不要直接依赖低层模块,二者都应该依赖抽象。比如 Service 不应该直接依赖某个具体的 MySQL 实现类,而应该依赖一个 Dao 接口。
控制反转是一种思想。原来对象自己控制依赖的创建,现在交给外部容器控制。控制权从对象内部转移到了容器,所以叫反转。
依赖注入是控制反转的一种实现方式。对象需要什么依赖,不是自己创建,而是由容器注入进来。常见方式有构造器注入、Setter 注入、字段注入。
依赖倒置告诉我们应该面向抽象编程;控制反转把对象创建权交给容器;依赖注入则是容器把依赖交给对象的具体办法。
**注意:**项目中更多的使用构造器注入,因为依赖关系更清晰,也更利于单元测试。字段注入写起来方便,但依赖隐藏在类里面,不如构造器直观。
6. Spring AOP 主要想解决什么问题?
AOP 主要解决横切关注点的问题。
很多功能不是某一个业务独有的,而是大量业务方法都会用到。比如:
- 记录接口调用日志。
- 统计方法执行耗时。
- 进行权限校验。
- 开启、提交、回滚事务。
- 统一异常处理。
- 缓存处理。
这些逻辑如果都写进业务方法里,会有两个明显问题。第一是重复代码很多,第二是业务代码不干净。比如一个下单方法,本来应该只关心下单流程,却混入了日志、权限、事务等代码,可读性会下降。
AOP 的思路是:业务逻辑还是写在业务方法里,公共逻辑放到切面里,然后通过切点决定增强哪些方法。
最典型的例子就是事务。我们平时用 @Transactional,本质就是 Spring 通过 AOP 给方法加上事务能力。开发者不需要手动写 begin、commit、rollback,代码会清爽很多。
7. 动态代理和静态代理的区别
代理模式可以理解成:不直接访问目标对象,而是通过一个代理对象访问。代理对象可以在调用目标方法前后加一些额外操作。
静态代理是在写代码时就把代理类写出来。例如 UserServiceProxy 里面持有一个 UserService,调用方法前先打印日志,再调用真正的 userService.save()。这种方式简单,但缺点也明显:如果很多类都要代理,就要写很多代理类。
动态代理是在运行时生成代理对象,不需要手动给每个类写代理类。Spring AOP 用的就是动态代理思想。
两者区别如下:
| 对比项 | 静态代理 | 动态代理 |
|---|---|---|
| 代理类来源 | 程序员手写 | 运行时生成 |
| 灵活性 | 较低 | 较高 |
| 维护成本 | 类多时成本高 | 更适合统一增强 |
| 常见场景 | 简单固定代理 | Spring AOP、声明式事务 |
JDK 动态代理要求目标对象实现接口,因为它是基于接口生成代理类。CGLIB 不要求接口,它通过继承目标类生成子类代理,所以 final 类和 final 方法会受到限制。
8. Spring 是如何解决循环依赖的?
Spring 解决循环依赖,主要指解决单例 Bean 的属性注入循环依赖。
比如:
java
class A {
private B b;
}
class B {
private A a;
}
A 创建时需要 B,B 创建时又需要 A。如果按普通思路,会一直互相等待。Spring 的做法是提前暴露还没完全初始化好的 Bean。
Spring 创建单例 Bean 大致有三个阶段:
- 实例化:先把对象 new 出来。
- 属性注入:给对象填充依赖。
- 初始化:执行初始化方法和后置处理器。
为了处理循环依赖,Spring 用到了三级缓存:
| 缓存 | 作用 |
|---|---|
一级缓存 singletonObjects |
存放完整初始化好的单例 Bean |
二级缓存 earlySingletonObjects |
存放提前暴露的早期 Bean |
三级缓存 singletonFactories |
存放可以生成早期引用的工厂 |
以 A 依赖 B、B 又依赖 A 为例:
- Spring 先创建 A,完成实例化,但 A 还没有完成属性注入。
- Spring 把 A 的早期引用工厂放到三级缓存。
- A 发现自己需要 B,于是开始创建 B。
- B 注入属性时发现需要 A,于是从缓存里拿到 A 的早期引用。
- B 创建完成后,再注入给 A。
- A 继续完成自己的属性注入和初始化。
限制:
- 构造器循环依赖一般解决不了,因为对象还没实例化完成,没法提前暴露。
- prototype 原型 Bean 的循环依赖也不能这样解决。
- 如果 Bean 被 AOP 代理,三级缓存可以提前暴露代理对象,避免注入进去的是原始对象,而容器里最终保存的是代理对象。
所以三级缓存不只是为了解决普通对象循环依赖,也和 AOP 代理对象的提前暴露有关。
二、SpringMVC 部分
1. MVC 分层
MVC 是 Model、View、Controller 的缩写。它的核心目的就是分职责,不让所有代码都堆在一起。
Model 是模型层,负责数据和业务。实际项目里,Model 不只是实体类,还可以包括 Service、Dao、Repository 等业务和数据访问部分。
View 是视图层,负责展示。传统项目里可能是 JSP、Thymeleaf、Freemarker;现在很多项目是前后端分离,视图主要由前端框架负责。
Controller 是控制层,负责接收请求、处理参数、调用业务层、返回结果。Controller 不应该写太多业务逻辑,否则会变成"胖 Controller",后面很难维护。
在 Java Web 项目中,常见分层一般是:
text
Controller -> Service -> Mapper / Dao -> Database
各层职责可以这样理解:
- Controller:管请求和响应。
- Service:管业务流程和事务。
- Mapper / Dao:管数据库访问。
- Entity / DTO / VO:管数据传递。
比如一个"创建订单"的接口,Controller 负责接收商品 id、数量、用户信息等参数;Service 负责判断库存、计算价格、创建订单、扣减库存;Mapper 负责真正执行 SQL。这样分层之后,每一层要改什么比较清楚。
MVC 的好处是结构清晰,也方便团队协作。前端展示、请求控制、业务处理、数据库访问分别放在合适的位置,项目大了以后不容易乱。
2. SpringMVC 的处理流程
SpringMVC 的核心入口是 DispatcherServlet,它可以理解成整个 SpringMVC 的总调度员。所有符合映射规则的请求,都会先到它这里。
一次请求的大致流程如下:
- 浏览器或客户端发送请求,请求先进入
DispatcherServlet。 DispatcherServlet找HandlerMapping,根据 URL、请求方式等信息匹配对应的 Controller 方法。- 找到处理器后,会得到一个
HandlerExecutionChain,里面可能包含目标 Handler 和拦截器。 DispatcherServlet再找合适的HandlerAdapter。HandlerAdapter调用真正的 Controller 方法。- 调用 Controller 方法前,SpringMVC 会处理参数绑定、类型转换、数据校验等事情。
- Controller 调用 Service 完成业务,然后返回结果。
- 如果返回页面,会经过
ViewResolver解析视图,再渲染页面。 - 如果返回 JSON,会通过
HttpMessageConverter把 Java 对象转换成 JSON。 - 最终响应返回给客户端。
这套流程里几个组件比较关键:
| 组件 | 作用 |
|---|---|
DispatcherServlet |
前端控制器,统一接收和分发请求 |
HandlerMapping |
根据请求找到对应处理器 |
HandlerAdapter |
适配并调用 Controller 方法 |
HandlerInterceptor |
在 Controller 前后做拦截处理 |
ViewResolver |
解析视图 |
HttpMessageConverter |
处理 JSON、XML、请求体和响应体转换 |
现在前后端分离项目比较多,Controller 常用 @RestController。这种情况下返回的一般是 JSON,不走传统 JSP 那种页面渲染流程,而是由消息转换器把对象写到响应体中。
三、SpringBoot 部分
1. SpringBoot 比 Spring 好在哪里?
SpringBoot 不是另一个完全独立的框架,它是在 Spring 基础上做了简化和增强。它主要解决传统 Spring 项目配置繁琐、启动麻烦、整合组件成本高的问题。
传统 Spring 项目里,经常要配置很多内容,比如 MVC 配置、数据源配置、事务配置、JSON 转换器、扫描路径等。配置多了以后,项目刚搭起来就要花不少时间。
SpringBoot 的优势主要体现在这些方面:
- 起步依赖更方便。比如引入
spring-boot-starter-web,常用 Web 依赖基本就配好了。 - 自动配置减少了大量手写配置。
- 内嵌 Tomcat,可以直接运行 jar 包。
- 默认配置比较合理,大部分场景开箱即用。
- 外部化配置方便,可以用
application.yml或application.properties。 - 提供 Actuator,方便做健康检查、监控和运维。
- 整合常见技术更省事,比如 MyBatis、Redis、RabbitMQ、Spring Security 等。
**总结:**Spring 更像基础能力,SpringBoot 更像把这些能力整理好后提供一套快速开发方式。它没有取代 Spring,而是让 Spring 用起来更顺手。
2. SpringBoot 用到哪些设计模式?
SpringBoot 以及它背后的 Spring 框架用到了很多设计模式。面试时不用死背太多,能结合具体例子说明更重要。
工厂模式很典型。Spring 容器本身就像一个 Bean 工厂,BeanFactory 和 ApplicationContext 负责创建和管理对象。
单例模式也很常见。Spring Bean 默认是单例的,同一个 Bean 在容器里通常只有一个实例。
代理模式主要体现在 AOP、事务、缓存等功能中。比如 @Transactional 并不是直接修改业务方法,而是通过代理对象增强方法。
模板方法模式在 Spring 里也很多。比如一些固定流程由框架控制,开发者只需要扩展其中某些步骤。JdbcTemplate 就是典型例子,它封装了获取连接、执行 SQL、释放资源等固定流程。
观察者模式对应 Spring 事件机制。比如发布一个 ApplicationEvent,多个监听器可以监听并处理。
策略模式在 SpringMVC 中比较明显。不同的 HandlerMapping、HandlerAdapter、ViewResolver、HttpMessageConverter 都可以看成不同策略,框架根据实际情况选择使用。
适配器模式的代表是 HandlerAdapter。不同类型的 Handler 不能都用同一种方式直接调用,所以 SpringMVC 用适配器统一调用入口。
建造者模式可以看 SpringApplicationBuilder,它通过链式方式一步步构建应用启动配置。
这些设计模式不是为了显得复杂,而是为了让框架在扩展性和复用性上更好。
3. SpringBoot 自动装配原理是什么?
SpringBoot 自动装配的核心是:根据当前项目引入的依赖、配置文件和条件注解,自动给容器注册合适的 Bean。
启动类上常见的注解是:
java
@SpringBootApplication
它里面主要包含三个能力:
@SpringBootConfiguration:说明这是一个配置类。@ComponentScan:扫描当前包及子包下的组件。@EnableAutoConfiguration:开启自动配置。
自动装配的关键在 @EnableAutoConfiguration。
SpringBoot 启动时,会读取自动配置类清单。不同版本位置略有差异:早期常见的是 META-INF/spring.factories,SpringBoot 2.7 之后开始使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,SpringBoot 3.x 中主要就是这种方式。
读取到自动配置类之后,并不是全部无脑生效,而是要经过条件判断。常见条件注解有:
@ConditionalOnClass:某个类存在时生效。@ConditionalOnMissingBean:容器中没有某个 Bean 时生效。@ConditionalOnBean:容器中存在某个 Bean 时生效。@ConditionalOnProperty:配置文件中某个属性满足条件时生效。@ConditionalOnWebApplication:当前是 Web 应用时生效。
比如项目引入了 spring-boot-starter-web,classpath 中就会有 SpringMVC、Tomcat、Jackson 等相关类。SpringBoot 检测到这些条件后,就会自动配置内嵌 Tomcat、DispatcherServlet、JSON 转换器、静态资源映射等。
自动装配的流程可以简化为:
- 启动 SpringBoot 应用。
- 读取主启动类上的
@SpringBootApplication。 - 开启组件扫描和自动配置。
- 加载自动配置类清单。
- 根据条件注解决定哪些配置生效。
- 把符合条件的 Bean 注册到 Spring 容器。
- 如果用户自己定义了 Bean,很多默认配置会自动让位。
4. SpringBoot 过滤器和拦截器
过滤器和拦截器都能在请求前后做处理,但它们属于不同体系,执行位置也不一样。
Filter 属于 Servlet 规范,和 SpringMVC 没有强绑定。请求进入 Servlet 之前,就会先经过 Filter。它更靠近底层 Web 容器。
Filter 常见用途:
- 统一设置编码。
- 跨域处理。
- 请求和响应包装。
- XSS 防护。
- 粗粒度登录校验。
- 记录原始请求日志。
在 SpringBoot 中,可以通过 @WebFilter、FilterRegistrationBean 或直接注册为 Spring Bean 的方式使用 Filter。
Interceptor 属于 SpringMVC。请求已经进入 DispatcherServlet 后,在 Controller 方法执行前后触发。
Interceptor 常见方法:
preHandle:Controller 执行前调用,返回false可以中断请求。postHandle:Controller 执行后、视图渲染前调用。afterCompletion:整个请求完成后调用,常用于日志收尾和资源清理。
Interceptor 常见用途:
- 登录状态检查。
- 权限校验。
- 接口耗时统计。
- 设置用户上下文。
- 记录 Controller 层访问日志。
两者区别可以这样看:
| 对比项 | Filter | Interceptor |
|---|---|---|
| 所属体系 | Servlet 规范 | SpringMVC |
| 执行时机 | 进入 Servlet 前后 | 进入 DispatcherServlet 后,Controller 前后 |
| 拦截范围 | 更大,几乎所有 Web 请求 | 主要是 SpringMVC 请求 |
| 是否依赖 SpringMVC | 不依赖 | 依赖 |
| 适合场景 | 编码、跨域、包装请求、安全过滤 | 登录、权限、业务上下文 |
执行顺序大致是:
text
客户端请求
↓
Filter
↓
DispatcherServlet
↓
Interceptor preHandle
↓
Controller
↓
Interceptor postHandle
↓
响应写出或视图渲染
↓
Interceptor afterCompletion
↓
Filter
↓
客户端
实际项目里,如果逻辑偏底层、希望尽早处理,可以放在 Filter;如果逻辑和 Controller、用户权限、业务路径关系更强,放在 Interceptor 更合适;如果是方法级增强,比如事务、缓存、注解权限,也可以考虑 AOP。
最后总结
Spring 主要提供 IoC、AOP、事务等基础能力。IoC 负责管理对象和依赖,AOP 负责抽取公共增强逻辑。
SpringMVC 是 Web 层框架,核心是 DispatcherServlet,它负责把请求分发到对应 Controller,并处理参数、返回值、视图或 JSON。
SpringBoot 是对 Spring 生态的简化和整合,核心优势是起步依赖、自动配置、内嵌容器和约定优于配置。它让项目创建、运行和部署都更方便。