软件技术体系汇总-Spring篇

本文深入探讨了Spring框架的核心概念,包括IoC容器、依赖注入、Bean生命周期、BeanFactory和ApplicationContext等,还涵盖了Spring的事件机制、模块驱动、注解原理以及循环依赖处理。此外,文章还讨论了Spring与Java泛型、AOP、配置属性、Bean作用域等方面的知识。
摘要由CSDN通过智能技术生成

Spring源码学习总结

版本说明

系列文章是本人在学习 Spring 源码的过程中总结

Spring 版本:5.2.8.RELEASE

调试工具:IDEA2020.3

作者:虎哥

常见面试题

1. 什么是 Spring Framework ?

官方文档:

Spring makes it easy to create Java enterprise applications. It provides everything you need to embrace the Java language in an enterprise environment, with support for Groovy and Kotlin as alternative languages on the JVM, and with the flexibility to create many kinds of architectures depending on an application’s needs.

Spring使创建Java企业应用程序变得容易。它提供了在企业环境中使用Java语言所需的一切,支持Groovy和Kotlin作为JVM上的替代语言,并具有根据应用程序需求创建多种体系结构的灵活性。

这个问题很难回答,在 Spring 官方文档中的描述也很抽象,答案在于你对 Spring 是如何理解的,想必每个人都有自己的回答方式,以下是我个人对于 Spring 的理解:

整个 Spring 生态在涉及到 Java 的项目中被广泛应用,它提供了非常多的组件,能够让你在开发 Java 应用的过程变得更加容易,弹性地支持其他软件框架,可以比作一个“排插座”,其他软件框架简单地“插上”即可结合 Spring 一起使用,给开发人员带来了非常多的便利。Spring 底层 IoC 容器的设计实现也是非常完美的,在整个 Spring 应用上下文的生命周期Spring Bean 的生命周期的许多阶段提供了相应的扩展点,供开发者自行扩展,使得框架非常的灵活。

2. Spring Framework 的优势和不足?

优势:Spring 面向模块进行开发,根据不同的功能进行划分,根据需求引入对应的模块即可,对于开发人员非常友好。例如 Spring IoC 容器,将我们的 Java 对象作为 Spring Bean 进行管理,管理着 Bean 的整个生命周期;Spring MVC 提供“模型-视图-控制器”(Model-View-Controller)架构和随时可用的组件,用于开发灵活且松散耦合的 Web 应用程序;Spring AOP 提供面向切面编程的接口,可以很方便的使用;还有许多其他的功能模块,就不一一讲述了。

不足:整个 Spring 体系比较复杂,对于开发人员需要一定的学习成本,遇到相关问题时需要对底层实现有充分的了解,这也就需要开发人员投入更多的时间和精力去学习。当然,如今 Spring 体系整合了 Java 生态非常多的东西,为开发人员带来的便利远大于这些不足,我觉得是有必要对 Spring 进行充分的学习,去了解 Spring 的贡献者们的设计思路,对自身也会有很大的提升,从中可以学习到许多的价值设计。

3. 你对 IoC 的理解?

Inversion of Control(IoC)是面向对象中的一种编程思想或原则。可以先回到传统方式,当我依赖一个对象,我需要主动去创建它并进行属性赋值,然后我才能去使用这个对象。对于 IoC 这种方式来说,它使得对象或者组件的创建更为透明,你不需要过多地关注细节如创建对象、属性赋值,这些工作交都由 IoC 容器来完成,已达到解耦的目的。

IoC 控制反转,简单来理解其实就是把获取依赖对象的方式,交由 IoC 容器来实现,由“主动拉取”变为“被动获取”。

4. 为什么需要 IoC ?

实际上,IoC 是为了屏蔽构造细节。例如 new 出来的对象的生命周期中的所有细节对于使用端都是知道的,如果在没有 IoC 容器的前提下简单项目中,IoC 是没有存在的必要,不过在复杂的系统中,我们的应用更应该关注的是对象的运用,而非它的构造和初始化等细节

5. IoC 和 DI 的区别?

DI 依赖注入不完全等同于 IoC,更应该说 DI 依赖注入是 IoC 的一种实现方式或策略

依赖查找依赖注入都是 IoC 的实现策略。依赖查找就是在应用程序里面主动调用 IoC 容器提供的接口去获取对应的 Bean 对象,而依赖注入是在 IoC 容器启动或者初始化的时候,通过构造器、字段、setter 方法或者接口等方式注入依赖。依赖查找相比于依赖注入对于开发者而言更加繁琐,具有一定的代码入侵性,需要借助 IoC 容器提供的接口,所以我们总是强调后者。依赖注入在 IoC 容器中的实现也是调用相关的接口获取 Bean 对象,只不过这些工作都是在 IoC 容器启动时由容器帮你实现了,在应用程序中我们通常很少主动去调用接口获取 Bean 对象。

6. IoC 容器的职责?

主要有以下职责:

  • 依赖处理,通过依赖查找或者依赖注入
  • 管理托管的资源(Java Bean 或其他资源)的生命周期
  • 管理配置(容器配置、外部化配置、托管的资源的配置)

IoC 容器有非常多,例如 JDK 的 Java Beans,Java EE 的 EJB,Apache Avalon,Google guice,Spring,其中 Spring 是最成功的的一个,目前被广泛应用。

其中 Spring 借鉴了 JDK 的 Java Beans 设计思想,也使用到其中相关类(例如 java.beans.PropertyEditor 属性编辑器)。

7. 什么是 Spring IoC 容器?

Spring 框架是一个 IoC 容器的实现,DI 依赖注入是它的实现的一个原则,提供依赖查找和依赖注入两种依赖处理,管理着 Bean 的生命周期。Spring 还提供了 AOP 抽象、事件抽象、事件监听机制、SPI 机制、强大的第三方整合、易测试性等其他特性。

8. 构造器注入和 Setter 注入

构造器注入:通过构造器的参数注入相关依赖对象

Setter 注入:通过 Setter 方法注入依赖对象,也可以理解为字段注入

对于两种注入方式的看法:

  • 构造器注入可以避免一些尴尬的问题,比如说状态不确定性地被修改,在初始化该对象时才会注入依赖对象,一定程度上保证了 Bean 初始化后就是不变的对象,这样对于我们的程序和维护性都会带来更多的便利;
  • 构造器注入不允许出现循环依赖,因为它要求被注入的对象都是成熟态,保证能够实例化,而 Setter 注入或字段注入没有这样的要求;
  • 构造器注入可以保证依赖的对象能够有序的被注入,而 Setter 注入或字段注入底层是通过反射机制进行注入,无法完全保证注入的顺序;
  • 如果构造器注入出现比较多的依赖导致代码不够优雅,我们应该考虑自身代码的设计是否存在问题,是否需要重构代码结构。

除了上面的注入方式外,Spring 还提供了接口回调注入,通过实现 Aware 接口(例如 BeanNameAware、ApplicationContextAware)可以注入相关对象,Spring 在初始化这类 Bean 时会调用其 setXxx 方法注入对象,例如注入 beanName、ApplicationContext

9. BeanFactory 和 ApplicationContext 谁才是 Spring IoC 容器?

BeanFactory 是 Spring 底层 IoC 容器,ApplicationContext 是 BeanFactory 的子接口,是 BeanFactory 的一个超集,提供 IoC 容器以外更多的功能。ApplicationContext 除了扮演 IoC 容器角色,还提供了这些企业特性:面向切面(AOP)、配置元信息、资源管理、事件机制、国际化、注解、Environment 抽象等。我们一般称 ApplicationContext 是 Spring 应用上下文,BeanFactory 为 Spring 底层 IoC 容器。

10. Spring Bean 的生命周期?

生命周期:

  1. Spring Bean 元信息配置阶段,可以通过面向资源(XML 或 Properties)、面向注解、面向 API (JavaConfig)进行配置
  2. Spring Bean 元信息解析阶段,对上一步的配置元信息进行解析,解析成 BeanDefinition 对象,该对象包含定义 Bean 的所有信息,用于实例化一个 Spring Bean
  3. Spring Bean 元信息注册阶段,将 BeanDefinition 配置元信息 保存至 BeanDefinitionRegistry 的 ConcurrentHashMap 集合中
  4. Spring BeanDefinition 合并阶段,定义的 Bean 可能存在层次性关系,则需要将它们进行合并,存在相同配置则覆盖父属性,最终生成一个 RootBeanDefinition 对象
  5. Spring Bean 的实例化阶段,首先的通过类加载器加载出一个 Class 对象,通过这个 Class 对象的构造器创建一个实例对象,构造器注入在此处会完成。在实例化阶段 Spring 提供了实例化前后两个扩展点(InstantiationAwareBeanPostProcessor 的 postProcessBeforeInstantiation、postProcessAfterInstantiation 方法)
  6. Spring Bean 属性赋值阶段,在 Spring 实例化后,需要对其相关属性进行赋值,注入依赖的对象。首先获取该对象所有属性与属性值的映射,可能已定义,也可能需要注入,在这里都会进行赋值(反射机制)。提示一下,依赖注入的实现通过 CommonAnnotationBeanPostProcessor(@Resource、@PostConstruct、@PreDestroy)和 AutowiredAnnotationBeanPostProcessor(@Autowired、@Value)两个处理器实现的。
  7. Aware 接口回调阶段,如果 Spring Bean 是 Spring 提供的 Aware 接口类型(例如 BeanNameAware、ApplicationContextAware),这里会进行接口的回调,注入相关对象(例如 beanName、ApplicationContext)
  8. Spring Bean 初始化阶段,这里会调用 Spring Bean 配置的初始化方法,执行顺序:@PostConstruct 标注方法、实现 InitializingBean 接口的 afterPropertiesSet() 方法、自定义初始化方法。在初始化阶段 Spring 提供了初始化前后两个扩展点(BeanPostProcessor 的 postProcessBeforeInitialization、postProcessAfterInitialization 方法)
  9. Spring Bean 初始化完成阶段,在所有的 Bean(不是抽象、单例模式、不是懒加载方式)初始化后,Spring 会再次遍历所有初始化好的单例 Bean 对象,如果是 SmartInitializingSingleton 类型则调用其 afterSingletonsInstantiated() 方法,这里也属于 Spring 提供的一个扩展点
  10. Spring Bean 销毁阶段,当 Spring 应用上下文关闭或者你主动销毁某个 Bean 时则进入 Spring Bean 的销毁阶段,执行顺序:@PreDestroy 注解的销毁动作、实现了 DisposableBean 接口的 Bean 的回调、destroy-method 自定义的销毁方法。这里也有一个销毁前阶段,也属于 Spring 提供的一个扩展点,@PreDestroy 就是基于这个实现的
  11. Spring 垃圾收集(GC)

总结:

  1. 上面 123 属于 BeanDefinition 配置元信息阶段,算是 Spring Bean 的前身,想要生成一个 Bean 对象,需要将这个 Bean 的所有信息都定义好;
  2. 其中 45 属于实例化阶段,想要生成一个 Java Bean 对象,那么肯定需要根据 Bean 的元信息先实例化一个对象;
  3. 接下来的 6 属于属性赋值阶段,实例化后的对象还是一个空对象,我们需要根据 Bean 的元信息对该对象的所有属性进行赋值;
  4. 后面的 789 属于初始化阶段,在 Java Bean 对象生成后,可能需要对这个对象进行相关初始化工作才予以使用;
  5. 最后面的 1011 属于销毁阶段,当 Spring 应用上下文关闭或者主动销毁某个 Bean 时,可能需要对这个对象进行相关销毁工作,最后等待 JVM 进行回收。

11. BeanDefinition 是什么?

BeanDefinition 是 Spring Bean 的“前身”,其内部包含了初始化一个 Bean 的所有元信息,在 Spring 初始化一个 Bean 的过程中需要根据该对象生成一个 Bean 对象并进行一系列的初始化工作。

12. Spring 内建的 Bean 作用域有哪些?

来源说明
singleton默认 Spring Bean 作用域,一个 BeanFactory 有且仅有一个实例
prototype原型作用域,每次依赖查找和依赖注入生成新 Bean 对象
request将 Spring Bean 存储在 ServletRequest 上下文中
session将 Spring Bean 存储在 HttpSession 中
application将 Spring Bean 存储在 ServletContext 中

13. BeanPostProcessor 与 BeanFactoryPostProcessor 的区别?

BeanPostProcessor 提供 Spring Bean 初始化前和初始化后的生命周期回调,允许对关心的 Bean 进行扩展,甚至是替换,其相关子类也提供 Spring Bean 生命周期中其他阶段的回调。

BeanFactoryPostProcessor 提供 Spring BeanFactory(底层 IoC 容器)的生命周期的回调,用于扩展 BeanFactory(实际为 ConfigurableListableBeanFactory),BeanFactoryPostProcessor 必须由 Spring ApplicationContext 执行,BeanFactory 无法与其直接交互。

14. 依赖注入和依赖查找的来源是否相同?

否,依赖查找的来源仅限于 Spring BeanDefinition 以及单例对象,而依赖注入的来源还包括 Resolvable Dependency(Spring 应用上下文定义的可已处理的注入对象,例如注入 BeanFactory 注入的是 ApplicationContext 对象)以及 @Value 所标注的外部化配置

15. 如何基于 Extensible XML authoring 扩展 Spring XML 元素?

Spring XML 扩展

  1. 编写 XML Schema 文件(XSD 文件):定义 XML 结构
  2. 自定义 NamespaceHandler 实现:定义命名空间的处理器
  3. 自定义 BeanDefinitionParser 实现:绑定命名空间下不同的 XML 元素与其对应的解析器
  4. 注册 XML 扩展(META-INF/spring.handlers 文件):命名空间与命名空间处理器的映射
  5. 编写 Spring Schema 资源映射文件(META-INF/spring.schemas 文件):XML Schema 文件通常定义为网络的形式,在无网的情况下无法访问,所以一般在本地的也有一个 XSD 文件,可通过编写 spring.schemas 文件,将网络形式的 XSD 文件与本地的 XSD 文件进行映射,这样会优先从本地获取对应的 XSD 文件

Mybatis 对 Spring 的集成项目中的 <mybatis:scan /> 标签就是这样实现的,可以参考:NamespaceHandlerMapperScannerBeanDefinitionParserXSD 等文件

具体实现逻辑参考后续**《解析自定义标签(XML 文件)》**

16. Java 泛型擦写发生在编译时还是运行时?

运行时。编译时,泛型参数类型还是存在的,运行时会忽略。

17. 简述 Spring 事件机制原理?

主要有以下几个角色:

  • Spring 事件 - org.springframework.context.ApplicationEvent,实现了 java.util.EventListener 接口
  • Spring 事件监听器 - org.springframework.context.ApplicationListener,实现了 java.util.EventObject 类
  • Spring 事件发布器 - org.springframework.context.ApplicationEventPublisher
  • Spring 事件广播器 - org.springframework.context.event.ApplicationEventMulticaster

Spring 内建的事件:

  • ContextRefreshedEvent:Spring 应用上下文就绪事件
  • ContextStartedEvent:Spring 应用上下文启动事件
  • ContextStoppedEvent:Spring 应用上下文停止事件
  • ContextClosedEvent:Spring 应用上下文关闭事件

Spring 应用上下文就是一个 ApplicationEventPublisher 事件发布器,其内部有一个 ApplicationEventMulticaster 事件广播器(被观察者),里面保存了所有的 ApplicationListener 事件监听器(观察者)。Spring 应用上下文发布一个事件后会通过 ApplicationEventMulticaster 事件广播器进行广播,能够处理该事件类型的 ApplicationListener 事件监听器则进行处理。

18. @EventListener 的工作原理?

@EventListener 用于标注在方法上面,该方法则可以用来处理 Spring 的相关事件。

Spring 内部有一个处理器 EventListenerMethodProcessor,它实现了 SmartInitializingSingleton 接口,在所有的 Bean(不是抽象、单例模式、不是懒加载方式)初始化后,Spring 会再次遍历所有初始化好的单例 Bean 对象时会执行该处理器对该 Bean 进行处理。在 EventListenerMethodProcessor 中会对标注了 @EventListener 注解的方法进行解析,如果符合条件则生成一个 ApplicationListener 事件监听器并注册。

19. Spring 提供的注解有哪些?

核心注解有以下:

  • Spring 模式注解
Spring 注解场景说明起始版本
@Repository数据仓储模式注解2.0
@Component通用组件模式注解2.5
@Service服务模式注解2.5
@ControllerWeb 控制器模式注解2.5
@Configuration配置类模式注解3.0

Spring 模式注解都是 @Component 的派生注解,Spring 为什么会提供这么多派生注解?

@Component 注解是一个通用组件注解,标注这个注解后表明你需要将其作为一个 Spring Bean 进行使用,而其他注解都有各自的作用,例如 @Controller 及其派生注解用于 Web 场景下处理 HTTP 请求,@Configuration 注解通常会将这个 Spring Bean 作为一个配置类,也会被 CGLIB 提供,帮助实现 AOP 特性。这也是领域驱动设计中的一种思想。

领域驱动设计:Domain-Driven Design,简称 DDD。过去系统分析和系统设计都是分离的,这样割裂的结果导致需求分析的结果无法直接进行设计编程,而能够进行编程运行的代码却扭曲需求,导致客户运行软件后才发现很多功能不是自己想要的,而且软件不能快速跟随需求变化。DDD 则打破了这种隔阂,提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。

  • 装配注解
Spring 注解场景说明起始版本
@ImportResource替换 XML 元素 <import>2.5
@Import导入 Configuration 类2.5
@ComponentScan扫描指定 package 下标注 Spring 模式注解的类3.1
  • 依赖注入注解
Spring 注解场景说明起始版本
@AutowiredBean 依赖注入,支持多中依赖查找方式2.5
@Qualifier细粒度的 @Autowired 依赖查找2.5
  • @Enable 模块驱动
Spring 注解场景说明起始版本
@EnableWebMvc启动整个 Web MVC 模块3.1
@EnableTransactionManagement启动整个事务管理模块3.1
@EnableCaching启动整个缓存模块3.1
@EnableAsync启动整个异步处理模块3.1

@Enable 模块驱动是以 @Enable 为前缀的注解驱动编程模型。所谓“模块”是指具备相同领域的功能组件集合,组合所形成一个独立的单元。比如 Web MVC 模块、AspectJ 代理模块、Caching(缓存)模块、JMX(Java 管理扩展)模块、Async(异步处理)模块等。

这类注解底层原理就是通过 @Import 注解导入相关类(Configuration Class、 ImportSelector 接口实现、ImportBeanDefinitionRegistrar 接口实现),来实现引入某个模块或功能。

  • 条件注解
Spring 注解场景说明起始版本
@Conditional条件限定,引入某个 Bean4.0
@Profile从 Spring 4.0 开始,@Profile 基于 @Conditional 实现,限定 Bean 的 Spring 应用环境4.0

20. 简述 Spring Environment ?

统一 Spring 配置属性的存储,用于占位符处理和类型转换,还支持更丰富的配置属性源(PropertySource);

通过 Environment Profiles 信息,帮助 Spring 容器提供条件化地装配 Bean。

21. Environment 完整的生命周期是怎样的?

在 Spring 应用上下文进入刷新阶段之前,可以通过 setEnvironment(Environment) 方法提前设置 Environment 对象,在刷新阶段如果没有 Environment 对象则会创建一个新的 Environment 对象

22. Spring 应用上下文的生命周期?

Spring 应用上下文就是 ApplicationContext,生命周期主要体现在 org.springframework.context.support.AbstractApplicationContext#refresh() 方法中,大致如下:

  1. Spring 应用上下文启动准备阶段,设置相关属性,例如启动时间、状态标识、Environment 对象
  2. BeanFactory 初始化阶段,初始化一个 BeanFactory 对象,加载出 BeanDefinition 们;设置相关组件,例如 ClassLoader 类加载器、表达式语言处理器、属性编辑器,并添加几个 BeanPostProcessor 处理器
  3. BeanFactory 后置处理阶段,主要是执行 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 的处理,对 BeanFactory 和 BeanDefinitionRegistry 进行后置处理,这里属于 Spring 应用上下文的一个扩展点
  4. BeanFactory 注册 BeanPostProcessor 阶段,主要初始化 BeanPostProcessor 类型的 Bean(依赖查找),在 Spring Bean 生命周期的许多节点都能见到该类型的处理器
  5. 初始化内建 Bean,初始化当前 Spring 应用上下文的 MessageSource 对象(国际化文案相关)、ApplicationEventMulticaster 事件广播器对象、ThemeSource 对象
  6. Spring 事件监听器注册阶段,主要获取到所有的 ApplicationListener 事件监听器进行注册,并广播早期事件
  7. BeanFactory 初始化完成阶段,主要是初始化所有还未初始化的 Bean(不是抽象、单例模式、不是懒加载方式)
  8. Spring 应用上下文刷新完成阶段,清除当前 Spring 应用上下文中的缓存,例如通过 ASM(Java 字节码操作和分析框架)扫描出来的元数据,并发布上下文刷新事件
  9. Spring 应用上下文启动阶段,需要主动调用 AbstractApplicationContext#start() 方法,会调用所有 Lifecycle 的 start() 方法,最后会发布上下文启动事件
  10. Spring 应用上下文停止阶段,需要主动调用 AbstractApplicationContext#stop() 方法,会调用所有 Lifecycle 的 stop() 方法,最后会发布上下文停止事件
  11. Spring 应用上下文关闭阶段,发布当前 Spring 应用上下文关闭事件,销毁所有的单例 Bean,关闭底层 BeanFactory 容器;注意这里会有一个钩子函数(Spring 向 JVM 注册的一个关闭当前 Spring 应用上下文的线程),当 JVM “关闭” 时,会触发这个线程的运行

总结:

  • 上面的 12345678 都属于 Sping 应用上下文的刷新阶段,完成了 Spring 应用上下文一系列的初始化工作;
  • 9 属于 Spring 应用上下文启动阶段,和 Lifecycle 生命周期对象相关,会调用这些对象的 start() 方法,最后发布上下文启动事件;
  • 10 属于 Spring 应用上下文停止阶段,和 Lifecycle 生命周期对象相关,会调用这些对象的 stop() 方法,最后发布上下文停止事件;
  • 11 属于 Spring 应用上下文关闭阶段,发布上下文关闭事件,销毁所有的单例 Bean,关闭底层 BeanFactory 容器。

23. Spring 应用上下文生命周期有哪些阶段?

参考Spring 应用上下文的生命周期

  • 刷新阶段 - ConfigurableApplicationContext#refresh()
  • 启动阶段 - ConfigurableApplicationContext#start()
  • 停止阶段 - ConfigurableApplicationContext#stop()
  • 关闭阶段 - ConfigurableApplicationContext#close()

24. 简述 ObjectFactory?

ObjectFactory(或 ObjectProvider) 可关联某一类型的 Bean,仅提供一个 getObject() 方法用于返回目标 Bean 对象,ObjectFactory 对象被依赖注入或依赖查找时并未实时查找到关联类型的目标 Bean 对象,在调用 getObject() 方法才会依赖查找到目标 Bean 对象。

根据 ObjectFactory 的特性,可以说它提供的是延迟依赖查找。通过这一特性在 Spring 处理循环依赖(字段注入)的过程中就使用到了 ObjectFactory,在某个 Bean 还没有完全初始化好的时候,会先缓存一个 ObjectFactory 对象(调用其 getObject() 方法可返回当前正在初始化的 Bean 对象),如果初始化的过程中依赖的对象又依赖于当前 Bean,会先通过缓存的 ObjectFactory 对象获取到当前正在初始化的 Bean,这样一来就解决了循环依赖的问题。

注意这里是延迟依赖查找而不是延迟初始化,ObjectFactory 无法决定是否延迟初始化,而需要通过配置 Bean 的 lazy 属性来决定这个 Bean 对象是否需要延迟初始化,非延迟初始化的 Bean 在 Spring 应用上下文刷新过程中就会初始化。

提示:如果是 ObjectFactory(或 ObjectProvider)类型的 Bean,在被依赖注入或依赖查找时返回的是 DefaultListableBeanFactory#DependencyObjectProvider 私有内部类,实现了 ObjectProvider<T> 接口,关联的类型为 Object。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iibt8cI6-1684077648497)(C:\Users\Gary\AppData\Roaming\Typora\typora-user-images\image-20230204233454750.png)]

简述 FactoryBean

FactoryBean 关联一个 Bean 对象,提供了一个 getObject() 方法用于返回这个目标 Bean 对象,FactoryBean 对象在被依赖注入或依赖查找时,实际得到的 Bean 就是通过 getObject() 方法获取到的目标类型的 Bean 对象。如果想要获取 FactoryBean 本身这个对象,在 beanName 前面添加 & 即可获取。

我们可以通过 FactoryBean 帮助实现复杂的初始化逻辑,例如在 Spring 继集成 MyBatis 的项目中,Mapper 接口没有实现类是如何被注入的?其实 Mapper 接口就是一个 FactoryBean 对象,当你注入该接口时,实际的到的就是其 getObject() 方法返回的一个代理对象,关于数据库的操作都是通过该代理对象来完成。

26. ObjectFactory、FactoryBean 和 BeanFactory 的区别

根据其名称可以知道其字面意思分别是:对象工厂,工厂 Bean

ObjectFactory、FactoryBean 和 BeanFactory 均提供依赖查找的能力。

  • ObjectFactory 提供的是延迟依赖查找,想要获取某一类型的 Bean,需要调用其 getObject() 方法才能依赖查找到目标 Bean 对象。ObjectFactory 就是一个对象工厂,想要获取该类型的对象,需要调用其 getObject() 方法生产一个对象。
  • FactoryBean 不提供延迟性,在被依赖注入或依赖查找时,得到的就是通过 getObject() 方法拿到的实际对象。FactoryBean 关联着某个 Bean,可以说在 Spring 中它就是某个 Bean 对象,无需我们主动去调用 getObject() 方法,如果想要获取 FactoryBean 本身这个对象,在 beanName 前面添加 & 即可获取。
  • BeanFactory 则是 Spring 底层 IoC 容器,里面保存了所有的单例 Bean,ObjectFactory 和 FactoryBean 自身不具备依赖查找的能力,能力由 BeanFactory 输出。

27. @Bean 的处理流程是怎样的?

Spring 应用上下文生命周期,在 BeanDefinition(@Component 注解、XML 配置)的加载完后,会执行所有 BeanDefinitionRegistryPostProcessor 类型的处理器,Spring 内部有一个 ConfigurationClassPostProcessor 处理器,它会对所有的配置类进行处理,解析其内部的注解(@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean),其中 @Bean 注解标注的方法会生成对应的 BeanDefinition 对象并注册。详细步骤可查看后续文章:@Bean 等注解的实现原理

28. BeanFactory 是如何处理循环依赖?

前言,下面的“循环依赖”换成“循环依赖注入”比较合适,在 Spring 中通过 depends-on 配置的依赖对象如果出现循环依赖会抛出异常

说明:这里的循环依赖指的是单例模式下的 Bean 字段注入时出现的循环依赖。构造器注入对于 Spring 无法自动解决(应该考虑代码设计是否有问题),可通过延迟初始化来处理(lazy注解)。Spring 只解决单例模式下的循环依赖。

在 Spring 底层 IoC 容器 BeanFactory 中处理循环依赖的方法主要借助于以下 3 个 Map 集合:

  1. singletonObjects(一级 Map),里面保存了所有已经初始化好的单例 Bean,也就是会保存 Spring IoC 容器中所有单例的 Spring Bean;
  2. earlySingletonObjects(二级 Map),里面会保存从 三级 Map 获取到的正在初始化的 Bean
  3. singletonFactories(三级 Map),里面保存了正在初始化的 Bean 对应的 ObjectFactory 实现类,调用其 getObject() 方法返回正在初始化的 Bean 对象(仅实例化还没完全初始化好),如果存在则将获取到的 Bean 对象并保存至 二级 Map,同时从当前 三级 Map 移除该 ObjectFactory 实现类。

当通过 getBean 依赖查找时会首先依次从上面三个 Map 获取,存在则返回,不存在则进行初始化,这三个 Map 是处理循环依赖的关键。

例如两个 Bean 出现循环依赖,A 依赖 B,B 依赖 A;当我们去依赖查找 A,在实例化后初始化前会先生成一个 ObjectFactory 对象(可获取当前正在初始化 A)保存在上面的 singletonFactories 中,初始化的过程需注入 B;接下来去查找 B,初始 B 的时候又要去注入 A,又去查找 A ,由于可以通过 singletonFactories 直接拿到正在初始化的 A,那么就可以完成 B 的初始化,最后也完成 A 的初始化,这样就避免出现循环依赖。

问题一:为什么需要上面的 二级 Map

因为通过 三级 Map获取 Bean 会有相关 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(…) 的处理,避免重复处理,处理后返回的可能是一个代理对象

例如在循环依赖中一个 Bean 可能被多个 Bean 依赖, A -> B(也依赖 A) -> C -> A,当你获取 A 这个 Bean 时,后续 B 和 C 都要注入 A,没有上面的 二级 Map的话,三级 Map 保存的 ObjectFactory 实现类会被调用两次,会重复处理,可能出现问题,这样做在性能上也有所提升

问题二:为什么不直接调用这个 ObjectFactory#getObject() 方法放入 二级Map 中,而需要上面的 三级 Map

对于不涉及到 AOP 的 Bean 确实可以不需要 singletonFactories(三级 Map),但是 Spring AOP 就是 Spring 体系中的一员,如果没有singletonFactories(三级 Map),意味着 Bean 在实例化后就要完成 AOP 代理,这样违背了 Spring 的设计原则。Spring 是通过 AnnotationAwareAspectJAutoProxyCreator 这个后置处理器在完全创建好 Bean 后来完成 AOP 代理,而不是在实例化后就立马进行 AOP 代理。如果出现了循环依赖,那没有办法,只有给 Bean 先创建代理对象,但是在没有出现循环依赖的情况下,设计之初就是让 Bean 在完全创建好后才完成 AOP 代理。

提示:AnnotationAwareAspectJAutoProxyCreator 是一个 SmartInstantiationAwareBeanPostProcessor 后置处理器,在它的 getEarlyBeanReference(…) 方法中可以创建代理对象。所以说对于上面的问题二,如果出现了循环依赖,如果是一个 AOP 代理对象,那只能给 Bean 先创建代理对象,设计之初就是让 Bean 在完全创建好后才完成 AOP 代理。

为什么 Spring 的设计是让 Bean 在完全创建好后才完成 AOP 代理?

因为创建的代理对象需要关联目标对象,在拦截处理的过程中需要根据目标对象执行被拦截的方法,所以这个目标对象最好是一个“成熟态”,而不是仅实例化还未初始化的一个对象。

29. Spring 中几种初始化方法的执行顺序?

有以下初始化方式:

  • Aware 接口:实现了 Spring 提供的相关 XxxAware 接口,例如 BeanNameAware、ApplicationContextAware,其 setXxx 方法会被回调,可以注入相关对象
  • @PostConstruct 注解:该注解是 JSR-250 的标准注解,Spring 会调用该注解标注的方法
  • InitializingBean 接口:实现了该接口,Spring 会调用其 afterPropertiesSet() 方法
  • 自定义初始化方法:通过 init-method 指定的方法会被调用

在 Spring 初始 Bean 的过程中上面的初始化方式的执行顺序如下:

  1. Aware 接口的回调

  2. JSR-250 @PostConstruct 标注的方法的调用

  3. InitializingBean#afterPropertiesSet 方法的回调

  4. init-method 初始化方法的调用

30. 通过 @Bean 注解定义在方法上面注入一个 Spring Bean,每次调用该方法所属的 Bean 的这个方法,得到的是同一个对象吗?

举个例子,大多数情况下,@Configuration Class 会通过 @Bean 注解为 Bean 定义,比如 @Bean User user() { return new User(); },那这样是不是每次主动调用这个方法都会返回一个新的 User 对象呢?

不是的,@Configuration Class 在得到 CGLIB 提升后,会设置一个拦截器专门对 @Bean 方法进行拦截处理,通过依赖查找的方式从 IoC 容器中获取 Bean 对象,如果是单例 Bean,那么每次都是返回同一个对象,所以当主动调用这个方法时获取到的都是同一个 User 对象。

源码验证:

考察技能点是关于@Bean的解析原理

直观验证
在这里插入图片描述

源码层验证

1、
在这里插入图片描述

2、
在这里插入图片描述

3、
在这里插入图片描述

4、
在这里插入图片描述

5、
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

@configuration 中的@bean方法实例化 和 xml配置方式的`factory-method` 工厂方法,创建一个实例对象 原理一一致

在这里插入图片描述

在这里插入图片描述

执行核心流程总结:

这2个参数是AbstractBeanDefinition中的属性

@Nullable
private String factoryBeanName;// 对应config

@Nullable
private String factoryMethodName;//@Bean 方法名称

通过CGLIB代理创建对象工厂(配置类都会通过ConfigurationClassEnhancer生产代理类),然后通过反射执行工厂里的方法获取目标对象

每个**配置类**会生成一个 {@link ConfigurationClass} 对象
其中 @Bean 标注的方法转换成 {@link BeanMethod} 对象保存在 {@link ConfigurationClass#beanMethods} 集合中
当执行Object result = factoryMethod.invoke(factoryBean, args);时会被ConfigurationClassEnhancer
静态内部类BeanMethodInterceptor拦截方法intercept拦截

在这里插入图片描述

org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions 处理配置类的核心方法
在这里插入图片描述
在这里插入图片描述

Spring源码中的使用到的规则

Holder类的作用

在这里插入图片描述

Holder技术的实现原理分析

在Java中,虽然我们通常会说值传递和引用传递。但是,在底层工作原理上是不存在传引用的概念的,Java的参数传递只有传值

Java中的基本类型(PS:int、long、boolean等)存放在栈内存中,Java对象引用(PS:Integer、Long、Boolean等其他Java对象)参数也存放在栈内存中,但是引用指向的具体的值却存放在堆内存中。

public class Demo {
 
    public static void main(String[] args) {
        int a = 1;
        change(a);
        System.out.println("a之后的值: " + a);
        System.out.println("-----------------我是分割线--------------------");
        
        Integer b = 1;
        change2(b);
        System.out.println("b之后的值: " + b);
    }
    
    public static void change(int aa){
        System.out.println("aa初始值: " + aa);
        aa = 10;
        System.out.println("aa之后的值: " + aa);
    }
    
    public static void change2(Integer bb){
        System.out.println("bb初始值: " + bb);
        bb = 10;
        System.out.println("bb之后的值: " + bb);
    }
 
}

在这里插入图片描述

很明显,这两个方法都没能改变原始的a、b变量。上面这段代码执行时的内存单元变化大致是这样的:

在这里插入图片描述

从这个图可以看出,执行change方法时,aa变量先是在另一个内存单元中复制了一份a变量的东西,接着在方法中一直改变的都是aa变量,因此很明显a变量从始至终都没有发生改变;执行change2方法时,同样bb变量也是在另一个内存单元中复制了一份b变量的东西,接着方法中改变了bb变量具体指向的值(即:堆内存中的 1 改变成了 10),很明显这个过程跟b变量也没有关系,因此在change2方法执行完毕之后b变量也没有发生改变

  1. 我画图时为了简便并没有严格区分栈内存和堆内存,大家明白这个意思就行
  2. 上图中的内存地址是我随便虚构的,一段代码执行时的实际内存地址并不一定是这样

那么,我在最上面提到的Holder技术是怎么实现在一个方法中改变对象的值的呢?

public class IntegerHolder {
	public Integer value;

	public IntegerHolder() {

	}

	public IntegerHolder(Integer value) {
		this.value = value;
	}
}
public class Demo2 {

	public static void changeInteger(IntegerHolder ii){
		ii.value = new Integer(10);
	}

	public static void main(String[] args) {
		IntegerHolder i = new IntegerHolder(1);
		
		System.out.println("初始值: " + i.value);
		
		changeInteger(i);
		System.out.println("改变之后的值: " + i.value);
	}

}

在这里插入图片描述

发现没有,没有使用return返回值但是却让值发生了改变。如果还是用内存单元图来表示的话,那么大致上是这样的:

在这里插入图片描述

从上面的图可以看出,虽然在changeInteger方法中,变量 ii 的地址跟变量 i 的地址不一样,但是它们存储的内容(PS:ii.value这个Integer对象的地址)却从始至终都没有发生改变。相反,在changeInteger方法中改变的是ii.value这个Integer对象真正的值,ii.value的内存地址并没有发生改变,仍然和 i.value的内存地址一样。因此在这个方法结束之后 i.value对应的值也相应发生了改变。这就是Holder技术的实现原理

临时变量的作用

在这里插入图片描述

Aware接口

资源回调接口,获取spring的内部资源使用(例如ApplicationContextAware用于获取ApplicationContext)

*基础校验**spring-boot-starter-validation*

*嵌套校验* *hibernate-validator*

*分组校验* *spring-validation*

*集合校验* *lombok*

*自定义校验* *ConstraintValidator*

*快速失败**检验*

调试环境的搭建

Bean 的“前世”

BeanDefinition 的加载阶段(XML 文件)

BeanDefinition 的解析阶段(XML 文件)

解析自定义标签(XML 文件)

BeanDefinition 的解析过程(面向注解)

开启 Bean 的加载流程

Bean 的创建过程

Bean 的实例化阶段

单例 Bean 的循环依赖处理

Bean 的属性填充阶段

@Autowired 等注解的实现原理

@Bean 等注解的实现原理

Spring 应用上下文 ApplicationContext

Spring注解与Bean生命周期关联

Spring中主要通过org.springframework.core.io.Resource接口描述一个文件资源的位置信息,其常用的实现类有四个,分别是FileSystemResource、UrlResource、ClassPathResource、ServletContextResource。
在这里插入图片描述

总结Spring相关原理图

Bean相关类型对象的关系图

在这里插入图片描述

**面向资源(XML)**定义 Bean 的 BeanDefinition 的解析过程

在这里插入图片描述

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

Spring中涉及到的技术

代理技术

CGLib采用底层的字节码技术ASM, 可以为一个类创建子类, 在子类中采用方法拦截的技术拦截所有父类方法的调用, 并织入横切逻辑。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D3CsfRPc-1684077648511)(C:\Users\Gary\AppData\Roaming\Typora\typora-user-images\image-20230212175403607.png)]

反射技术

Spring MVC 面试题

简单介绍一下 Spring MVC 框架

在早期 Java Web 的开发中,统一把显示层、控制层、显示层的操作全部交给 JSP 或者 Java Bean 来进行处理,存在一定的弊端,例如:JSP 和 Java Bean 之间严重耦合、开发效率低等弊端。

Spring MVC 是 Spring 体系中的一员,提供“模型-视图-控制器”(Model-View-Controller)架构和随时可用的组件,用于开发灵活且松散耦合的 Web 应用程序。

MVC 模式有助于分离应用程序的不同方面,如输入逻辑,业务逻辑和 UI 逻辑,同时在所有这些元素之间提供松散耦合。

Spring MVC 有什么优点?

  1. 使用真的非常方便,无论是添加 HTTP 请求方法映射的方法,还是不同数据格式的响应。
  2. 提供拦截器机制,可以方便的对请求进行拦截处理。
  3. 提供异常机制,可以方便的对异常做统一处理。
  4. 可以任意使用各种视图技术,而不仅仅局限于 JSP ,例如 Freemarker、Thymeleaf 等等。

描述一下 Spring MVC 的工作流程

Spring MVC 也是基于 Servlet 来处理请求的,主要通过 DispatcherServlet 这个 Servlet 来处理请求,处理过程需要通过九大组件来完成,先看到下面这个流程图:

img

Spring MVC 处理请求的流程大致如上图所示

  1. 用户的浏览器发送一个请求,这个请求经过互联网到达了我们的服务器。Servlet 容器首先接待了这个请求,并将该请求委托给 DispatcherServlet 进行处理。
  2. DispatcherServlet 将该请求传给了处理器映射组件 HandlerMapping,并获取到适合该请求的 HandlerExecutionChain 拦截器和处理器对象。
  3. 在获取到处理器后,DispatcherServlet 还不能直接调用处理器的逻辑,需要进行对处理器进行适配。处理器适配成功后,DispatcherServlet 通过处理器适配器 HandlerAdapter 调用处理器的逻辑,并获取返回值 ModelAndView 对象。
  4. 之后,DispatcherServlet 需要根据 ModelAndView 解析视图。解析视图的工作由 ViewResolver 完成,若能解析成功,ViewResolver 会返回相应的 View 视图对象。
  5. 在获取到具体的 View 对象后,最后一步要做的事情就是由 View 渲染视图,并将渲染结果返回给用户。

以上就是 Spring MVC 处理请求的全过程,上面的流程进行了一定的简化,主要涉及到最核心的组件,还有许多其他组件没有表现出来,不过这并不影响大家对主过程的理解。

总结:客户端发起请求后,最终会交由 DispatcherServlet 来处理,它会通过你的 URI 找到对应的方法,从请求中解析参数,然后通过反射机制调用该方法,将方法的执行结果设置到响应中,如果存在对应的 View 对象,则进行页面渲染,实际上就是将请求转发到指定的 URL

简单介绍 Spring MVC 的核心组件

那么接下来就简单介绍一下 DispatcherServlet 和九大组件(按使用顺序排序的):

组件说明
DispatcherServletSpring MVC 的核心组件,是请求的入口,负责协调各个组件工作
MultipartResolver内容类型( Content-Type )为 multipart/* 的请求的解析器,例如解析处理文件上传的请求,便于获取参数信息以及上传的文件
HandlerMapping请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors
HandlerAdapter处理器的适配器。因为处理器 handler 的类型是 Object 类型,需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变,比如用户处理器可以实现 Controller 接口、HttpRequestHandler 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器
HandlerExceptionResolver处理器异常解析器,将处理器( handler )执行时发生的异常,解析( 转换 )成对应的 ModelAndView 结果
RequestToViewNameTranslator视图名称转换器,用于解析出请求的默认视图名
LocaleResolver本地化(国际化)解析器,提供国际化支持
ThemeResolver主题解析器,提供可设置应用整体样式风格的支持
ViewResolver视图解析器,根据视图名和国际化,获得最终的视图 View 对象
FlashMapManagerFlashMap 管理器,负责重定向时,保存参数至临时存储(默认 Session)

Spring MVC 对各个组件的职责划分的比较清晰。DispatcherServlet 负责协调,其他组件则各自做分内之事,互不干扰。

@Controller 注解有什么用?

@Controller 注解标记一个类为 Spring Web MVC 控制器 Controller。Spring MVC 会将扫描到该注解的类,然后扫描这个类下面带有 @RequestMapping 注解的方法,根据注解信息,为这个方法生成一个对应的处理器对象,在上面的 HandlerMapping 和 HandlerAdapter组件中讲到过。

当然,除了添加 @Controller 注解这种方式以外,你还可以实现 Spring MVC 提供的 Controller 或者 HttpRequestHandler 接口,对应的实现类也会被作为一个处理器对象

@RequestMapping 注解有什么用?

@RequestMapping 注解,在上面已经讲过了,配置处理器的 HTTP 请求方法,URI等信息,这样才能将请求和方法进行映射。这个注解可以作用于类上面,也可以作用于方法上面,在类上面一般是配置这个控制器的 URI 前缀

@RestController 和 @Controller 有什么区别?

@RestController 注解,在 @Controller 基础上,增加了 @ResponseBody 注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回例如 JSON 数据格式。当然,返回什么样的数据格式,根据客户端的 ACCEPT 请求头来决定。

@RequestMapping 和 @GetMapping 注解的不同之处在哪里?

  1. @RequestMapping:可注解在类和方法上;@GetMapping 仅可注册在方法上
  2. @RequestMapping:可进行 GET、POST、PUT、DELETE 等请求方法;@GetMapping@RequestMapping 的 GET 请求方法的特例,目的是为了提高清晰度。

@RequestParam 和 @PathVariable 两个注解的区别

两个注解都用于方法参数,获取参数值的方式不同,@RequestParam 注解的参数从请求携带的参数中获取,而 @PathVariable 注解从请求的 URI 中获取

返回 JSON 格式使用什么注解?

可以使用 @ResponseBody 注解,或者使用包含 @ResponseBody 注解的 @RestController 注解。

当然,还是需要配合相应的支持 JSON 格式化的 HttpMessageConverter 实现类。例如,Spring MVC 默认使用 MappingJackson2HttpMessageConverter

介绍一下 Spring MVC 中的 WebApplicationContext ?

WebApplicationContext 是实现 ApplicationContext 接口的子类,专门为 WEB 应用准备的

  • 它允许从相对于 Web 根目录的路径中加载配置文件完成初始化 Spring MVC 组件的工作
  • 从 WebApplicationContext 中,可以获取 ServletContext 引用,整个 Web 应用上下文对象将作为属性放置在 ServletContext 中,以便 Web 应用环境可以访问 Spring 上下文。

Spring MVC 和 Struts2 的异同?

入口不同

  • Spring MVC 的入门是一个 Servlet 控制器
  • Struts2 入门是一个 Filter 过滤器

配置映射不同,

  • Spring MVC 是基于方法开发,传递参数是通过方法形参,一般设置为单例
  • Struts2 是基于开发,传递参数是通过类的属性,只能设计为多例

视图不同

  • Spring MVC 通过参数解析器是将 Request 对象内容进行解析成方法形参,将响应数据和页面封装成 ModelAndView 对象,最后又将模型数据通过 Request 对象传输到页面。其中,如果视图使用 JSP 时,默认使用 JSTL
  • Struts2 采用值栈存储请求和响应的数据,通过 OGNL 存取数据。

介绍下 Spring MVC 拦截器?

Spring MVC 拦截器有三个增强处理的地方:

  1. 前置处理:在执行方法前执行,全部成功执行才会往下执行方法
  2. 后置处理:在成功执行方法后执行,倒序
  3. 已完成处理:不管方法是否成功执行都会执行,不过只会执行前置处理成功的拦截器,倒序

可以通过拦截器进行权限检验,参数校验,记录日志等操作

Spring MVC 的拦截器和 Filter 过滤器有什么差别?

有以下几点:

  • 功能相同:拦截器和 Filter 都能实现相应的功能,谁也不比谁强
  • 容器不同:拦截器构建在 Spring MVC 体系中;Filter 构建在 Servlet 容器之上
  • 使用便利性不同:拦截器提供了三个方法,分别在不同的时机执行;过滤器仅提供一个方法,当然也能实现拦截器的执行时机的效果,就是麻烦一些

一般拓展性好的框架,都会提供相应的拦截器或过滤器机制,方便的开发人员做一些拓展

REST 面试题

REST 代表着什么?

REST 代表着抽象状态转移,它是根据 HTTP 协议从客户端发送数据到服务端,例如:服务端的一本书可以以 XML 或 JSON 格式传递到客户端

然而,假如你不熟悉REST,我建议你先看看 REST API design and development 这篇文章来更好的了解它。也可以阅读知乎上的 《怎样用通俗的语言解释 REST,以及 RESTful?》 讨论

资源是什么?

资源是指数据在 REST 架构中如何显示的。将实体作为资源公开 ,它允许客户端通过 HTTP 方法如:GET, POST,PUT, DELETE 等读,写,修改和创建资源

什么是安全的 REST 操作?

REST 接口是通过 HTTP 方法完成操作

  • 一些 HTTP 操作是安全的,如 GET 和 HEAD ,它不能在服务端修改资源
  • 换句话说,PUT、POST 和 DELETE 是不安全的,因为他们能修改服务端的资源

所以,是否安全的界限,在于是否修改服务端的资源

什么是幂等操作? 为什么幂等操作如此重要?

有一些 HTTP 方法,如:GET,不管你使用多少次它都能产生相同的结果,在没有任何一边影响的情况下,发送多个 GET 请求到相同的URI 将会产生相同的响应结果。因此,这就是所谓幂等操作

换句话说,POST方法不是幂等操作 ,因为如果发送多个 POST 请求,它将在服务端创建不同的资源。但是,假如你用 PUT 更新资源,它将是幂等操作。

甚至多个 PUT 请求被用来更新服务端资源,将得到相同的结果

REST 是可扩展的或说是协同的吗?

是的,REST 是可扩展的和可协作的。它既不托管一种特定的技术选择,也不定在客户端或者服务端。你可以用 Java, C++, Python, 或 JavaScript 来创建 RESTful Web 服务,也可以在客户端使用它们。

我建议你读一本关于REST接口的书来了解更多,如:RESTful Web Services

所以这里的“可拓展”、“协同”对应到我们平时常说的,“跨语言”、“语言无关”

REST 用哪种 HTTP 方法呢?

REST 能用任何的 HTTP 方法,但是,最受欢迎的是:

恰好,这四个操作,对上我们日常逻辑的 CRUD 操作

删除的 HTTP 状态返回码是什么 ?

在删除成功之后,您的 REST API 应该返回什么状态代码,并没有严格的规则。它可以返回 200 或 204 没有内容

  • 一般来说,如果删除操作成功,响应主体为空,返回 204
  • 如果删除请求成功且响应体不是空的,则返回 200

REST API 是无状态的吗?

是的,REST API 应该是无状态的,因为它是基于 HTTP 的,它也是无状态的

REST API 中的请求应该包含处理它所需的所有细节。它不应该依赖于以前或下一个请求或服务器端维护的一些数据,例如会话

REST 规范为使其无状态设置了一个约束,在设计 REST API 时,你应该记住这一点

REST安全吗? 你能做什么来保护它?

安全是一个宽泛的术语。它可能意味着消息的安全性,这是通过认证和授权提供的加密或访问限制提供的

REST 通常不是安全的,需要开发人员自己实现安全机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XWwKaBFF-1684077648513)(C:\Users\Gary\AppData\Roaming\Typora\typora-user-images\image-20230219005131585.png)]

1.模板方法模式
应用场景:JdbcTemplate、HttpServlet
2.简单工厂
应用场景:spring中有两种bean,一种是普通bean,另一种是工厂bean。FactoryBean就是spring中工厂类的顶级接口,当通过类
名获取类的实例的时候,如果该类是一个工厂类即实现了Factory,他会自动调用其getObject方法,返回真的需要的对象。
3.工厂方法
应用场景:使用xml配置bean的时候,bean标签有个factory-method属性,该属性的值生成类的静态方法名。
4.观察者
应用场景:ContextLoaderListener
5.装饰器
应用场景:HttpHeadResponseDecorator
6.代理
应用场景:ProxyFactory、JdkDynamicFactory、CglibAopFactory
7.单例
应用场景:spring中的bean默认是单例的。
8.适配器
应用场景:AdvisorAdapter、HanderAdapter
9.原型模式:
应用场景:ProtorypeBean

img

微服务化 — 容器化 ----云原生化

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder_Boy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值