Spring/SpringMVC/SprongBoot知识复习

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • [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、注解、配置类等,然后把这些信息解析成 BeanDefinitionBeanDefinition 可以理解成 Bean 的说明书,里面记录了类名、作用域、依赖关系、初始化方法等信息。

之后 Spring 根据这些 Bean 定义创建对象。创建对象时通常会用到反射,然后再进行依赖注入。比如一个 Service 依赖 Dao,Spring 会找到对应 Dao 对象,再注入到 Service 中。最后还会执行一些初始化逻辑,比如 BeanPostProcessor、初始化方法等。

所以 IoC 背后的关键点是:

  • 配置元数据。
  • BeanDefinition。
  • 反射创建对象。
  • 依赖注入。
  • Bean 生命周期管理。

AOP 的实现核心是代理。

Spring 在创建 Bean 的过程中,会判断这个 Bean 是否需要增强。如果需要增强,Spring 最终暴露出去的就不是普通原始对象,而是代理对象。调用者调用代理对象的方法时,代理对象会先执行切面逻辑,再调用真正的目标方法。

Spring AOP 常见代理方式有两种:

  1. JDK 动态代理

    目标类实现了接口时,可以基于接口创建代理对象。

  2. CGLIB 动态代理

    目标类没有实现接口时,可以生成目标类的子类作为代理对象。

简单说,IoC 主要靠容器和反射管理对象,AOP 主要靠代理增强对象。

4. 怎么理解 Spring IoC?

IoC 的全称是 Inversion of Control,翻译为控制反转。这个名字听起来比较抽象,实际可以理解成:对象的创建权和依赖管理权,从程序员手里的代码转移到了 Spring 容器手里。

没有 IoC 时,代码可能是这样的:

java 复制代码
public class OrderService {
    private OrderDao orderDao = new OrderDao();
}

这样的问题是 OrderServiceOrderDao 绑定得太死。如果以后换成 JdbcOrderDaoMyBatisOrderDao,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 给方法加上事务能力。开发者不需要手动写 begincommitrollback,代码会清爽很多。

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 大致有三个阶段:

  1. 实例化:先把对象 new 出来。
  2. 属性注入:给对象填充依赖。
  3. 初始化:执行初始化方法和后置处理器。

为了处理循环依赖,Spring 用到了三级缓存:

缓存 作用
一级缓存 singletonObjects 存放完整初始化好的单例 Bean
二级缓存 earlySingletonObjects 存放提前暴露的早期 Bean
三级缓存 singletonFactories 存放可以生成早期引用的工厂

以 A 依赖 B、B 又依赖 A 为例:

  1. Spring 先创建 A,完成实例化,但 A 还没有完成属性注入。
  2. Spring 把 A 的早期引用工厂放到三级缓存。
  3. A 发现自己需要 B,于是开始创建 B。
  4. B 注入属性时发现需要 A,于是从缓存里拿到 A 的早期引用。
  5. B 创建完成后,再注入给 A。
  6. 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 的总调度员。所有符合映射规则的请求,都会先到它这里。

一次请求的大致流程如下:

  1. 浏览器或客户端发送请求,请求先进入 DispatcherServlet
  2. DispatcherServletHandlerMapping,根据 URL、请求方式等信息匹配对应的 Controller 方法。
  3. 找到处理器后,会得到一个 HandlerExecutionChain,里面可能包含目标 Handler 和拦截器。
  4. DispatcherServlet 再找合适的 HandlerAdapter
  5. HandlerAdapter 调用真正的 Controller 方法。
  6. 调用 Controller 方法前,SpringMVC 会处理参数绑定、类型转换、数据校验等事情。
  7. Controller 调用 Service 完成业务,然后返回结果。
  8. 如果返回页面,会经过 ViewResolver 解析视图,再渲染页面。
  9. 如果返回 JSON,会通过 HttpMessageConverter 把 Java 对象转换成 JSON。
  10. 最终响应返回给客户端。

这套流程里几个组件比较关键:

组件 作用
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.ymlapplication.properties
  • 提供 Actuator,方便做健康检查、监控和运维。
  • 整合常见技术更省事,比如 MyBatis、Redis、RabbitMQ、Spring Security 等。

**总结:**Spring 更像基础能力,SpringBoot 更像把这些能力整理好后提供一套快速开发方式。它没有取代 Spring,而是让 Spring 用起来更顺手。

2. SpringBoot 用到哪些设计模式?

SpringBoot 以及它背后的 Spring 框架用到了很多设计模式。面试时不用死背太多,能结合具体例子说明更重要。

工厂模式很典型。Spring 容器本身就像一个 Bean 工厂,BeanFactoryApplicationContext 负责创建和管理对象。

单例模式也很常见。Spring Bean 默认是单例的,同一个 Bean 在容器里通常只有一个实例。

代理模式主要体现在 AOP、事务、缓存等功能中。比如 @Transactional 并不是直接修改业务方法,而是通过代理对象增强方法。

模板方法模式在 Spring 里也很多。比如一些固定流程由框架控制,开发者只需要扩展其中某些步骤。JdbcTemplate 就是典型例子,它封装了获取连接、执行 SQL、释放资源等固定流程。

观察者模式对应 Spring 事件机制。比如发布一个 ApplicationEvent,多个监听器可以监听并处理。

策略模式在 SpringMVC 中比较明显。不同的 HandlerMappingHandlerAdapterViewResolverHttpMessageConverter 都可以看成不同策略,框架根据实际情况选择使用。

适配器模式的代表是 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 转换器、静态资源映射等。

自动装配的流程可以简化为:

  1. 启动 SpringBoot 应用。
  2. 读取主启动类上的 @SpringBootApplication
  3. 开启组件扫描和自动配置。
  4. 加载自动配置类清单。
  5. 根据条件注解决定哪些配置生效。
  6. 把符合条件的 Bean 注册到 Spring 容器。
  7. 如果用户自己定义了 Bean,很多默认配置会自动让位。

4. SpringBoot 过滤器和拦截器

过滤器和拦截器都能在请求前后做处理,但它们属于不同体系,执行位置也不一样。

Filter 属于 Servlet 规范,和 SpringMVC 没有强绑定。请求进入 Servlet 之前,就会先经过 Filter。它更靠近底层 Web 容器。

Filter 常见用途:

  • 统一设置编码。
  • 跨域处理。
  • 请求和响应包装。
  • XSS 防护。
  • 粗粒度登录校验。
  • 记录原始请求日志。

在 SpringBoot 中,可以通过 @WebFilterFilterRegistrationBean 或直接注册为 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 生态的简化和整合,核心优势是起步依赖、自动配置、内嵌容器和约定优于配置。它让项目创建、运行和部署都更方便。

相关推荐
ㄟ留恋さ寂寞1 小时前
HTML5中SharedWorker生命周期与浏览器进程关闭的关系
jvm·数据库·python
彳亍1011 小时前
MongoDB备节点无法读取数据怎么解决_rs.slaveOk()与Secondary读取权限
jvm·数据库·python
m0_690825821 小时前
CSS如何实现圆形头像裁剪_使用border-radius50属性
jvm·数据库·python
雷工笔记1 小时前
不同数据库SQL语句中MD5的写法
数据库·sql
老纪1 小时前
HTML函数工具在NAS设备上能运行吗_轻服务器适配指南【指南】
jvm·数据库·python
老纪1 小时前
SQL如何高效提取大表前几行:分页查询与OFFSET优化
jvm·数据库·python
无风听海1 小时前
MongoDB GridFS 文件结构深度解析
数据库·mongodb
m0_470857641 小时前
Python如何构建异步消息队列_利用asyncio配合Redis实现任务分发
jvm·数据库·python
2301_781571421 小时前
SQL嵌套子查询中的变量如何传递_作用域与上下文限制解析
jvm·数据库·python