From 16b86763b6181b211dfe5e971c6e4d76fa49b6cf Mon Sep 17 00:00:00 2001 From: guide Date: Wed, 27 Jul 2022 19:58:53 +0800 Subject: [PATCH] =?UTF-8?q?[docs=20update]Spring=E5=B8=B8=E8=A7=81?= =?UTF-8?q?=E9=9D=A2=E8=AF=95=E9=A2=98=E6=80=BB=E7=BB=93=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spring-knowledge-and-questions-summary.md | 259 +++++++++++++----- 1 file changed, 191 insertions(+), 68 deletions(-) diff --git a/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md b/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md index e18e760f..fe060066 100644 --- a/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md +++ b/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md @@ -9,71 +9,84 @@ tag: 下面的很多问题我自己在使用 Spring 的过程中也并没有注意,自己也是临时查阅了很多资料和书籍补上的。网上也有一些很多关于 Spring 常见问题/面试题整理的文章,我感觉大部分都是互相 copy,而且很多问题也不是很好,有些回答也存在问题。所以,自己花了一周的业余时间整理了一下,希望对大家有帮助。 -## 什么是 Spring 框架? +## Spring 基础 + +### 什么是 Spring 框架? Spring 是一款开源的轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。 -Spring 翻译过来就是春天的意思,可见其目标和使命就是为 Java 程序员带来春天啊!感动! - -> 题外话 : 语言的流行通常需要一个杀手级的应用,Spring 就是 Java 生态的一个杀手级的应用框架。 - -我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。 - -比如说 Spring 自带 IoC(Inverse of Control:控制反转) 和 AOP(Aspect-Oriented Programming:面向切面编程)、可以很方便地对数据库进行访问、可以很方便地集成第三方组件(电子邮件,任务,调度,缓存等等)、对单元测试支持比较好、支持 RESTful Java 应用程序的开发。 +我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发,比如说 Spring 支持 IoC(Inverse of Control:控制反转) 和 AOP(Aspect-Oriented Programming:面向切面编程)、可以很方便地对数据库进行访问、可以很方便地集成第三方组件(电子邮件,任务,调度,缓存等等)、对单元测试支持比较好、支持 RESTful Java 应用程序的开发。 ![](https://img-blog.csdnimg.cn/38ef122122de4375abcd27c3de8f60b4.png) -Spring 最核心的思想就是不重新造轮子,开箱即用! +Spring 最核心的思想就是不重新造轮子,开箱即用,提高开发效率。 + +Spring 翻译过来就是春天的意思,可见其目标和使命就是为 Java 程序员带来春天啊!感动! + +🤐 多提一嘴 : **语言的流行通常需要一个杀手级的应用,Spring 就是 Java 生态的一个杀手级的应用框架。** Spring 提供的核心功能主要是 IoC 和 AOP。学习 Spring ,一定要把 IoC 和 AOP 的核心思想搞懂! - Spring 官网: - Github 地址: https://github.com/spring-projects/spring-framework -## 列举一些重要的 Spring 模块? +### Spring 包含的模块有哪些? -下图对应的是 Spring4.x 版本。目前最新的 5.x 版本中 Web 模块的 Portlet 组件已经被废弃掉,同时增加了用于异步响应式处理的 WebFlux 组件。 +**Spring4.x 版本** : -![Spring主要模块](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/jvme0c60b4606711fc4a0b6faf03230247a.png) +![Spring4.x主要模块](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/framework/spring/jvme0c60b4606711fc4a0b6faf03230247a.png) -**Spring Core** +**Spring5.x 版本** : -核心模块, Spring 其他所有的功能基本都需要依赖于该模块,主要提供 IoC 依赖注入功能的支持。 +![Spring5.x主要模块](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/framework/spring/20200831175708.png) -**Spring Aspects** +Spring5.x 版本中 Web 模块的 Portlet 组件已经被废弃掉,同时增加了用于异步响应式处理的 WebFlux 组件。 -该模块为与 AspectJ 的集成提供支持。 +Spring 各个模块的依赖关系如下: -**Spring AOP** +![Spring 各个模块的依赖关系](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/framework/spring/20200902100038.png) -提供了面向切面的编程实现。 +#### Core Container -**Spring Data Access/Integration :** +Spring 框架的核心模块,也可以说是基础模块,主要提供 IoC 依赖注入功能的支持。Spring 其他所有的功能基本都需要依赖于该模块,我们从上面那张 Spring 各个模块的依赖关系图就可以看出来。 -Spring Data Access/Integration 由 5 个模块组成: +- **spring-core** :Spring 框架基本的核心工具类。 +- **spring-beans** :提供对 bean 的创建、配置和管理等功能的支持。 +- **spring-context** :提供对国际化、事件传播、资源加载等功能的支持。 +- **spring-expression** :提供对表达式语言(Spring Expression Language) SpEL 的支持,只依赖于 core 模块,不依赖于其他模块,可以单独使用。 -- spring-jdbc : 提供了对数据库访问的抽象 JDBC。不同的数据库都有自己独立的 API 用于操作数据库,而 Java 程序只需要和 JDBC API 交互,这样就屏蔽了数据库的影响。 -- spring-tx : 提供对事务的支持。 -- spring-orm : 提供对 Hibernate 等 ORM 框架的支持。 -- spring-oxm : 提供对 Castor 等 OXM 框架的支持。 -- spring-jms : Java 消息服务。 +#### AOP -**Spring Web** +- **spring-aspects** :该模块为与 AspectJ 的集成提供支持。 +- **spring-aop** :提供了面向切面的编程实现。 +- **spring-instrument** :提供了为 JVM 添加代理(agent)的功能。 具体来讲,它为 Tomcat 提供了一个织入代理,能够为 Tomcat 传递类文 件,就像这些文件是被类加载器加载的一样。没有理解也没关系,这个模块的使用场景非常有限。 -Spring Web 由 4 个模块组成: +#### Data Access/Integration -- spring-web :对 Web 功能的实现提供一些最基础的支持。 -- spring-webmvc : 提供对 Spring MVC 的实现。 -- spring-websocket : 提供了对 WebSocket 的支持,WebSocket 可以让客户端和服务端进行双向通信。 -- spring-webflux :提供对 WebFlux 的支持。WebFlux 是 Spring Framework 5.0 中引入的新的响应式框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步. +- **spring-jdbc** :提供了对数据库访问的抽象 JDBC。不同的数据库都有自己独立的 API 用于操作数据库,而 Java 程序只需要和 JDBC API 交互,这样就屏蔽了数据库的影响。 +- **spring-tx** :提供对事务的支持。 +- **spring-orm** : 提供对 Hibernate、JPA 、iBatis 等 ORM 框架的支持。 +- **spring-oxm** :提供一个抽象层支撑 OXM(Object-to-XML-Mapping),例如:JAXB、Castor、XMLBeans、JiBX 和 XStream 等。 +- **spring-jms** : 消息服务。自 Spring Framework 4.1 以后,它还提供了对 spring-messaging 模块的继承。 -**Spring Test** +#### Spring Web + +- **spring-web** :对 Web 功能的实现提供一些最基础的支持。 +- **spring-webmvc** : 提供对 Spring MVC 的实现。 +- **spring-websocket** : 提供了对 WebSocket 的支持,WebSocket 可以让客户端和服务端进行双向通信。 +- **spring-webflux** :提供对 WebFlux 的支持。WebFlux 是 Spring Framework 5.0 中引入的新的响应式框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步。 + +#### Messaging + +**spring-messaging** 是从 Spring4.0 开始新加入的一个模块,主要职责是为 Spring 框架集成一些基础的报文传送应用。 + +#### Spring Test Spring 团队提倡测试驱动开发(TDD)。有了控制反转 (IoC)的帮助,单元测试和集成测试变得更简单。 Spring 的测试模块对 JUnit(单元测试框架)、TestNG(类似 JUnit)、Mockito(主要用来 Mock 对象)、PowerMock(解决 Mockito 的问题比如无法模拟 final, static, private 方法)等等常用的测试框架支持的都比较好。 -## Spring,Spring MVC,Spring Boot 之间什么关系? +### Spring,Spring MVC,Spring Boot 之间什么关系? 很多人对 Spring,Spring MVC,Spring Boot 这三者傻傻分不清楚!这里简单介绍一下这三者,其实很简单,没有什么高深的东西。 @@ -93,7 +106,7 @@ Spring 旨在简化 J2EE 企业应用程序开发。Spring Boot 旨在简化 Spr Spring Boot 只是简化了配置,如果你需要构建 MVC 架构的 Web 程序,你还是需要使用 Spring MVC 作为 MVC 框架,只是说 Spring Boot 帮你简化了 Spring MVC 的很多配置,真正做到开箱即用! -## Spring IOC & AOP +## Spring IoC ### 谈谈自己对于 Spring IoC 的了解 @@ -119,26 +132,6 @@ Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉 - [IoC 源码阅读](https://javadoop.com/post/spring-ioc) - [面试被问了几百遍的 IoC 和 AOP ,还在傻傻搞不清楚?](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486938&idx=1&sn=c99ef0233f39a5ffc1b98c81e02dfcd4&chksm=cea24211f9d5cb07fa901183ba4d96187820713a72387788408040822ffb2ed575d28e953ce7&token=1736772241&lang=zh_CN#rd) -### 谈谈自己对于 AOP 的了解 - -AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。 - -Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 **JDK Proxy**,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 **Cglib** 生成一个被代理对象的子类来作为代理,如下图所示: - -![SpringAOPProcess](https://img-blog.csdnimg.cn/img_convert/230ae587a322d6e4d09510161987d346.jpeg) - -当然你也可以使用 **AspectJ** !Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。 - -### Spring AOP 和 AspectJ AOP 有什么区别? - -**Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。** Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。 - -Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单, - -如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比 Spring AOP 快很多。 - -## Spring Bean - ### 什么是 Spring Bean? 简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象。 @@ -218,7 +211,7 @@ Spring 内置的 `@Autowired` 以及 JDK 内置的 `@Resource` 和 `@Inject` 都 | `@Resource` | `javax.annotation` | Java JSR-250 | | `@Inject` | `javax.inject` | Java JSR-330 | - `@Autowired` 和`@Resource`使用的比较多一些。 +`@Autowired` 和`@Resource`使用的比较多一些。 ### @Autowired 和 @Resource 的区别是什么? @@ -287,11 +280,12 @@ private SmsService smsService; Spring 中 Bean 的作用域通常有下面几种: -- **singleton** : 唯一 bean 实例,Spring 中的 bean 默认都是单例的,对单例设计模式的应用。 -- **prototype** : 每次请求都会创建一个新的 bean 实例。 -- **request** : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。 -- **session** : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。 -- **global-session** : 全局 session 作用域,仅仅在基于 portlet 的 web 应用中才有意义,Spring5 已经没有了。Portlet 是能够生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像 servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。 +- **singleton** : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。 +- **prototype** : 每次获取都会创建一个新的 bean 实例。也就是说,连续 `getBean()` 两次,得到的是不同的 Bean 实例。 +- **request** (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。 +- **session** (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。 +- **application/global-session** (仅 Web 应用可用): 每个 Web 应用在启动时创建一个 Bean(应用 Bean),,该 bean 仅在当前应用启动时间内有效。 +- **websocket** (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。 **如何配置 bean 的作用域呢?** @@ -348,6 +342,75 @@ public Person personPrototype() { ![Spring Bean 生命周期](https://images.xiaozhuanlan.com/photo/2019/b5d264565657a5395c2781081a7483e1.jpg) +## Spring AoP + +### 谈谈自己对于 AOP 的了解 + +AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。 + +Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 **JDK Proxy**,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 **Cglib** 生成一个被代理对象的子类来作为代理,如下图所示: + +![SpringAOPProcess](https://img-blog.csdnimg.cn/img_convert/230ae587a322d6e4d09510161987d346.jpeg) + +当然你也可以使用 **AspectJ** !Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。 + +AOP 切面编程设计到的一些专业术语: + +| 术语 | 含义 | +| :---------------- | :----------------------------------------------------------: | +| 目标(Target) | 被通知的对象 | +| 代理(Proxy) | 向目标对象应用通知之后创建的代理对象 | +| 连接点(JoinPoint) | 目标对象的所属类中,定义的所有方法均为连接点 | +| 切入点(Pointcut) | 被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点) | +| 通知(Advice) | 增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情 | +| 切面(Aspect) | 切入点(Pointcut)+通知(Advice) | +| Weaving(织入) | 将通知应用到目标对象,进而生成代理对象的过程动作 | + +### Spring AOP 和 AspectJ AOP 有什么区别? + +**Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。** Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。 + +Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单, + +如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比 Spring AOP 快很多。 + +### AspectJ 定义的通知类型有哪些? + +- **Before**(前置通知):目标对象的方法调用之前触发 +- **After** (后置通知):目标对象的方法调用之后触发 +- **AfterReturning**(返回通知):目标对象的方法调用完成,在返回结果值之后触发 +- **AfterThrowing**(异常通知) :目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。 +- **Around**: (环绕通知)编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法 + +### 多个切面的执行顺序如何控制? + +1、通常使用`@Order` 注解直接定义切面顺序 + +```java +// 值越小优先级越高 +@Order(3) +@Component +@Aspect +public class LoggingAspect implements Ordered { +``` + +**2、实现`Ordered` 接口重写 `getOrder` 方法。** + +```java +@Component +@Aspect +public class LoggingAspect implements Ordered { + + // .... + + @Override + public int getOrder() { + // 返回值越小优先级越高 + return 1; + } +} +``` + ## Spring MVC ### 说说自己对于 Spring MVC 了解? @@ -390,6 +453,16 @@ Model2 模式下还存在很多问题,Model2 的抽象和封装程度还远远 MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring MVC 可以帮助我们进行更简洁的 Web 层的开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。 +### Spring MVC 的核心组件有哪些? + +记住了下面这些组件,也就记住了 SpringMVC 的工作原理。 + +- **`DispatcherServlet`** :**核心的中央处理器**,负责接收请求、分发,并给予客户端响应。 +- **`HandlerMapping`** :**处理器映射器**,根据 uri 去匹配查找能处理的 `Handler` ,并会将请求涉及到的拦截器和 `Handler` 一起封装。 +- **`HandlerAdapter`** :**处理器适配器**,根据 `HandlerMapping` 找到的 `Handler` ,适配执行对应的 `Handler`; +- **`Handler`** :**请求处理器**,处理实际请求的处理器。 +- **`ViewResolver`** :**视图解析器**,根据 `Handler` 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 `DispatcherServlet` 响应客户端 + ### SpringMVC 工作原理了解吗? **Spring MVC 原理如下图所示:** @@ -400,14 +473,63 @@ MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring **流程说明(重要):** -1. 客户端(浏览器)发送请求,直接请求到 `DispatcherServlet`。 -2. `DispatcherServlet` 根据请求信息调用 `HandlerMapping`,解析请求对应的 `Handler`。 -3. 解析到对应的 `Handler`(也就是我们平常说的 `Controller` 控制器)后,开始由 `HandlerAdapter` 适配器处理。 -4. `HandlerAdapter` 会根据 `Handler`来调用真正的处理器开处理请求,并处理相应的业务逻辑。 -5. 处理器处理完业务后,会返回一个 `ModelAndView` 对象,`Model` 是返回的数据对象,`View` 是个逻辑上的 `View`。 -6. `ViewResolver` 会根据逻辑 `View` 查找实际的 `View`。 -7. `DispaterServlet` 把返回的 `Model` 传给 `View`(视图渲染)。 -8. 把 `View` 返回给请求者(浏览器) +1. 客户端(浏览器)发送请求, `DispatcherServlet`拦截请求。 +2. `DispatcherServlet` 根据请求信息调用 `HandlerMapping` 。`HandlerMapping` 根据 uri 去匹配查找能处理的 `Handler`(也就是我们平常说的 `Controller` 控制器) ,并会将请求涉及到的拦截器和 `Handler` 一起封装。 +3. `DispatcherServlet` 调用 `HandlerAdapter`适配执行 `Handler` 。 +4. `Handler` 完成对用户请求的处理后,会返回一个 `ModelAndView` 对象给`DispatcherServlet`,`ModelAndView` 顾名思义,包含了数据模型以及相应的视图的信息。`Model` 是返回的数据对象,`View` 是个逻辑上的 `View`。 +5. `ViewResolver` 会根据逻辑 `View` 查找实际的 `View`。 +6. `DispaterServlet` 把返回的 `Model` 传给 `View`(视图渲染)。 +7. 把 `View` 返回给请求者(浏览器) + +### 统一异常处理怎么做? + +推荐使用注解的方式统一异常处理,具体会使用到 `@ControllerAdvice` + `@ExceptionHandler` 这两个注解 。 + +```java +@ControllerAdvice +@ResponseBody +public class GlobalExceptionHandler { + + @ExceptionHandler(BaseException.class) + public ResponseEntity handleAppException(BaseException ex, HttpServletRequest request) { + //...... + } + + @ExceptionHandler(value = ResourceNotFoundException.class) + public ResponseEntity handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) { + //...... + } +} +``` + +这种异常处理方式下,会给所有或者指定的 `Controller` 织入异常处理的逻辑(AOP),当 `Controller` 中的方法抛出异常的时候,由被`@ExceptionHandler` 注解修饰的方法进行处理。 + +`ExceptionHandlerMethodResolver` 中 `getMappedMethod` 方法决定了异常具体被哪个被 `@ExceptionHandler` 注解修饰的方法处理异常。 + +```java +@Nullable + private Method getMappedMethod(Class exceptionType) { + List> matches = new ArrayList<>(); + //找到可以处理的所有异常信息。mappedMethods 中存放了异常和处理异常的方法的对应关系 + for (Class mappedException : this.mappedMethods.keySet()) { + if (mappedException.isAssignableFrom(exceptionType)) { + matches.add(mappedException); + } + } + // 不为空说明有方法处理异常 + if (!matches.isEmpty()) { + // 按照匹配程度从小到大排序 + matches.sort(new ExceptionDepthComparator(exceptionType)); + // 返回处理异常的方法 + return this.mappedMethods.get(matches.get(0)); + } + else { + return null; + } + } +``` + +从源代码看出:**`getMappedMethod()`会首先找到可以匹配处理异常的所有方法信息,然后对其进行从小到大的排序,最后取最小的那一个匹配的方法(即匹配度最高的那个)。** ## Spring 框架中用到了哪些设计模式? @@ -550,6 +672,7 @@ String transient4; // not persistent because of @Transient ## 参考 - 《Spring 技术内幕》 +- 《从零开始深入学习 Spring》:https://juejin.cn/book/6857911863016390663 - - -