SpringBoot-1、基本原理
1. SpringBoot 的特性
https://www.bilibili.com/video/BV1Et411Y7tQ?p=117&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
1.1. 依赖管理
1.1.1. 父项目做依赖管理
1、无需关注版本号,自动版本仲裁
2、可以修改默认版本号
1.1.2. starter 场景启动器
1、见到很多 spring-boot-starter-xxx : xxx 就某种场景
2、只要引入 starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot 所有支持的场景
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
4、见到的 xxx-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
5、所有场景启动器最底层的依赖
1.2. 自动配置
● 自动配好 Tomcat
○ 引入 Tomcat 依赖。
○ 配置 Tomcat
● 自动配好 SpringMVC
○ 引入 SpringMVC 全套组件
○ 自动配好 SpringMVC 常用组件(功能)
● 自动配好 Web 常见功能,如:字符编码问题
○ SpringBoot 帮我们配置好了所有 web 开发的常见场景
● 默认的包结构
○ 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
○ 无需以前的包扫描配置
○ 想要改变扫描路径,@SpringBootApplication(scanBasePackages=”com.atguigu”)
● 或者@ComponentScan 指定扫描路径
● 各种配置拥有默认值
○ 默认配置最终都是映射到某个类上,如:MultipartProperties
○ 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
● 按需加载所有自动配置项
○ 非常多的 starter
○ 引入了哪些场景这个场景的自动配置才会开启
○ SpringBoot 所有的自动配置功能都在 spring-boot-autoconfigure 包里面
2. 谈谈你对 SpringBoot 的理解 (优点特性)
SpringBoot 的用来快速开发 Spring 应用的一个脚手架、其设计目的是用来简新 Spring 应用的初始搭建以及开发过程。
- SpringBoot 提供了很多内置的 Starter 结合自动配置,实现了对主流框架的无配置集成、开箱即用;
- SpringBoot 简化了开发,采用 JavaConfig 的方式可以使用零 xml的方式进行开发;
- SpringBoot内置 Web 容器无需依赖外部 Web 服务器,省略了 Web.xml,直接运行 jar 文件就可以启动 web 应用;
- SpringBoot 帮我管理了常用的第三方依赖的版本,减少出现版本冲突的问题;
- SpringBoot自带了监控功能,可以监控应用程序的运行状况,或者内存、线程池、Http 请求统计等,同时还提供了优雅关闭应用程序等功能。
3. SpringBoot 自动配置原理 -@SpringBootApplication ⭐️🔴
^py2rrk
https://www.yuque.com/atguigu/springboot/qb7hy2#UJZFM ^c46210
3.1. 引导加载自动配置类
❕ ^dxovlh
1 |
|
3.1.1. @SpringBootConfiguration
@Configuration。代表当前是一个配置类
3.1.2. @ComponentScan
指定扫描哪些,是一个 Spring 注解;
在 SpringBoot 中,在该注解属性中配置了排除规则:排除 2 种类型:
①继承了 TypeExcludeFilter 的类
②SpringBoot 的自动配置类
1 |
|
3.1.3. @EnableAutoConfiguration
3.1.3.1. @AutoConfigurationPackage
❕ ^j6d450
@Import(AutoConfigurationPackages.Registrar.class)
自动配置包,指定了默认的包规则,将指定的一个包下的所有组件导入进来。
1 |
|
3.1.3.2. @Import(AutoConfigurationImportSelector.class)
selectImports→getAutoConfigurationEntry 获取到所有需要导入到容器中的配置类
方法调用路径
AutoConfigurationImportSelector. getAutoConfigurationEntry
==→== SpringFactoriesLoader. loadSpringFactories
==→==
classLoader.getResources(“META-INF/spring.factories
“)
默认扫描当前系统里面所有 META-INF/spring.factories 位置的文件 ❕
比如 spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
1 |
|
3.1.3.2.1. SPI 原理
❕ ^bypdob
3.2. 按需开启自动配置项
虽然我们 127 个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration,但按照条件装配规则(@Conditional),最终会按需配置。
3.3. 修改默认配置
1 |
|
SpringBoot 默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先
3.4. 与 SpringMVC 配置的区别
3.4.1. 关于 JSON 编码配置
Spring 整合 SpringMVC 时需要设置 JSON 格式
Springboot 不需要,因为在
自动配置了 jacjson-databind,使用的是 MappingJackson2HttpMessageConverter
3.4.2. @EnableAspectJAutoProxy⭐️🔴
在 Spring 中,如果不在配置类中添加@EnableAspectJAutoProxy,那么所有 切面 注解是不生效的(springboot 因为有自动配置,所以不需要开发人员手工配置@EnableAspectJAutoProxy)
3.5. 自动配置总结
- SpringBoot 先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties 里面拿。xxxProperties 和配置文件进行了绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置
- 用户直接自己@Bean 替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。
xxxxxAutoConfiguration —> 组件 —> xxxxProperties 里面拿值 —-> application.properties
3.6. 黑马补充
https://www.bilibili.com/video/BV15b4y117RJ?p=172&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
4. SpringBoot 的启动原理⭐️🔴
❕ ^214fh8
概述
- 运行 main 方法: 初始化 new SpringApplication 从 spring.factories 读取 ApplicationListener、ApplicationContextInitializer
- 运行 run 方法
- 读取环境变量配置信息
- 创建 springApplication 上下文: ServletWebServerApplicationContext
- 预初始化上下文
- 调用 refresh 加载 ioc 容器
invokeBeanFactoryPostProcessor
解析@Import: 加载所有的自动配置类onRefresh
创建 (内置)servlet 容器
在这个过程中 springboot 会调用很多监听器对外进行扩展
https://www.bilibili.com/video/BV1Et411Y7tQ?p=196&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
4.1. 创建 SpringApplication
- 判定当前应用的类型。ClassUtils.isPresent → WebApplicationType.SERVLET
- 使用系统方法
getSpringFactoriesInstances
分别查找以下类型:
○ 查找 bootstrappers:==初始启动引导器==
去spring.factories
文件中找 org.springframework.boot.Bootstrapper
放入List<Bootstrapper>
○ 查找 ApplicationContextInitializer:==初始化器==
去 spring.factories 找 ApplicationContextInitializer
放入List<ApplicationContextInitializer<?>> initializers
○ 查找 ApplicationListener :==应用监听器==
去 spring.factories 找 ApplicationListener
放入List<ApplicationListener<?>> listeners
- 保存当前启动类为配置类
4.2. 运行 SpringApplication
4.2.1. 记录应用的启动时间 - 创建引导上下文 -bootstrappers.intitialize
○ StopWatch,记录应用的启动时间
○ 创建引导上下文(Context 环境)createBootstrapContext()
■ 获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置
○ 让当前应用进入 headless 模式。java.awt.headless
4.2.2. 发布 ApplicationStartingEvent 事件
○ 获取所有 RunListener(运行监听器)【为了方便所有 Listener 进行事件感知】
■ getSpringFactoriesInstances 去 spring.factories 找 SpringApplicationRunListener.
○ 遍历 SpringApplicationRunListener 调用 starting 方法;
■ 相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
4.2.3. 读取环境配置信息发布 environmentPreparedEvent
○ 保存命令行参数;ApplicationArguments
○ 准备环境 prepareEnvironment();
■ 返回或者创建基础环境信息对象。StandardServletEnvironment
■ 配置环境信息对象。
● 读取所有的配置源的配置属性值:ConfigFileApplicationListener
会监听 onApplicationEnvironmentPreparedEvent
事件来加载配置文件 application.properties
的环境变量
■ 绑定环境信息
■ 监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
4.2.4. 根据项目类型创建 IOC 容器⭐️🔴
○ 实例化 Spring 上下文(createApplicationContext())
■ 根据项目类型(Servlet)创建相应的 WEB 容器
■ 当前会创建 AnnotationConfigServletWebServerApplicationContext
4.2.5. 准备 IOC 容器基本信息 (刷新前准备 -contextPrepared-loaded)
○ 准备 ApplicationContext IOC 容器的基本信息 prepareContext()
■ 保存环境信息
■ IOC 容器的后置处理流程。
■ 应用初始化器;applyInitializers;
● 遍历所有的 ApplicationContextInitializer 。调用 initialize。来对 ioc 容器进行初始化扩展功能
● 遍历所有的 listener 调用 contextPrepared。EventPublishRunListenr;通知所有的监听器 contextPrepared
● 将启动类作为配置类进行读取 –>将配置注册为 BeanDefinition 未看到有这块代码
■ 所有的监听器调用 contextLoaded
。通知所有的监听器 contextLoaded;
4.2.6. 刷新 IOC 容器⭐️🔴
○ 刷新 IOC 容器。refreshContext
■ 创建容器中的所有组件(Spring 注解)
○ 容器刷新完成后工作 afterRefresh 无实现,可扩展
AnnotationConfigServletWebServerApplicationContext.refresh
4.2.6.1. invokeBeanFactoryPostProcessors
invokeBeanFactoryPostProcessor
解析@Import: 加载所有的自动配置类
❕
4.2.6.2. onrefresh 创建子容器
onRefresh
创建 (内置)servlet 容器
4.2.7. 记录启动结束时间并输出
4.2.8. 发布 ApplicationStartedEvent
○ 所有监听器调用 listeners.started(context); 通知所有的监听器 started
4.2.9. 调用所有 runners
○ 调用所有 runners;callRunners()
■ 获取容器中的 ApplicationRunner
■ 获取容器中的 CommandLineRunner
■ 合并所有 runner 并且按照@Order 进行排序
■ 遍历所有的 runner。调用 run 方法
○ 如果以上有异常,
■ 调用 Listener 的 failed
4.2.10. 如果启动异常则发送 ApplicationFailedEvent
○ 调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
○ running 如果有问题。继续通知 failed 。调用所有 Listener 的 failed;通知所有的监听器 failed
5. 嵌入式 Servlet 容器启动原理 V2
❕ ^kb445n
SpringBoot_V1: https://www.bilibili.com/video/BV1mf4y1c7cV/?p=79&spm_id_from=pageDriver&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
SpringBoot_V2: https://www.bilibili.com/video/BV1Et411Y7tQ?p=169&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
5.1. 引入依赖触发自动配置
5.1.1. 容器工厂自动配置类⭐️🔴
当引入 spring-boot-starter-web
依赖时会在 SpringBoot 中添加并生效
servlet 容器工厂自动配置类: ServletWebServerFactoryAutoConfiguration
(生效原理: @ConditionalOnWebApplication(type = Type.SERVLET)
)
V1: EmbeddedServletContainerAutoConfiguration
5.1.2. Web 容器工厂 - 默认 Tomcat
该自动配置类通过@Import 导入了 (通过@ConditionalOnClass 判断决定使用哪一个) 一个 Web 容器工厂(默认 web-starter 会导入 tomcat 包),即 TomcatServletWebServerFactory
5.1.3. Web 容器工厂定制化器的后置处理器
5.1.3.1. BeanPostProcessorsRegistrar
在 ServletWebServerFactoryAutoConfiguration
中还通过 BeanPostProcessorsRegistrar
引入了容器工厂定制化器的后置处理器 WebServerFactoryCustomizerBeanPostProcessor
WebServerFactoryCustomizerBeanPostProcessor
,会获取所有的定制器,调用每个定制器的 customer 方法,用来给 Servlet 容器进行赋值
V1: EmbeddedServletContainerCustomizerBeanPostProcessor
5.1.3.2. WebServerFactoryCustomizer(容器工厂定制化器)⭐️🔴
我们在自定义容器配置时,也可以使用这个 WebServerFactoryCustomizer
示例代码
1 |
|
5.2. SpringBoot 应用执行 Run 方法
SpringApplication.run(Boot05WebAdminApplication.class, args);
5.2.1. 创建 IOC 容器
【创建 IOC 容器对象,并初始化容器,创建容器的每一个组件】;如果是 web 环境则创建 AnnotationConfig==ServletWebServer==ApplicationContext。如果不是 web 环境则创建 AnnotationConfigApplicationContext
SpringBoot_V2:
SpringBoot_V1:
5.2.2. 调用父类 refresh()
刷新创建好的 IOC 容器
5.2.3. 执行重写的 onRefresh() 方法
刷新 12 步中的第 9 步:web 的 IOC 容器 ServletWebServerApplicationContext
重写了 onRefresh 方法
❕
5.2.4. 从容器中获取 TomcatServletWebServerFactory
V1:EmbeddedServletContainerFactory
因为前面有@Bean 注解注入,所以可以从 IOC 容器中获取嵌入式的 Servlet 容器工厂 TomcatServletWebServerFactory,然后就可以用来创建 Servlet 容器
5.2.5. 后置处理器执行配置定制
当 TomcatServletWebServerFactory 创建 Servlet 容器对象时,后置处理器看这个对象,就来获取所有的定制器来定制 Servlet 容器的相关配置;
5.2.6. 创建 Servlet 容器并启动
通过 getWebServer
创建嵌入式的 Servlet 容器;
5.2.7. 顺序总结
IOC 容器启动时创建 Servlet 容器,然后先启动嵌入式的 Servlet 容器,最后再将 IOC 容器中剩下的没有创建出的对象创建出来
5.3. 概述
❕
6. SpringBoot 外置 Tomcat 启动原理⭐️🔴
❕ ^er3z4j
https://www.bilibili.com/video/BV1Et411Y7tQ?p=51&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
6.1. 操作步骤⭐️🔴
1、必须创建一个 war 项目;
2、将嵌入式的 Tomcat 指定为 provided
1 |
|
3、必须编写一个 SpringBootServletInitializer
的子类,并重写 configure 方法里面的写法,遵照固定写法,将 SpringBoot 的启动类放入 application.sources
的方法参数中。 ❕
1 |
|
4、启动服务器就可以;
6.2. 底层原理⭐️🔴
利用了 servlet3.0 规范官方文档: 当 servlet 容器启动时候就会去所有 jar 包中的 META-INF/services
文件夹中找到 javax.servlet.ServletContainerInitializer
,有的 jar 包中存在这个文件并在里面会绑定一个 ServletContainerInitializer
. 当 servlet 容器启动时候就会去该文件中找到 ServletContainerInitializer 的实现类,从而 创建它的实例并调用其 onstartUp 方法
❕
6.3. 执行流程
1、启动 Tomcat 服务器
2、spring web 模块里有这个文件
文件内容是: org.springframework.web.SpringServletContainerInitializer
3、SpringServletContainerInitializer 将 handlerTypes 标注的所有类型的类传入到 onStartip 方法的 Set<Class<?>>
中,并为这些感兴趣类创建实例
4、每个创建好的 WebApplicationInitializer 调用自己的 onStratup
5、因为有下图所示的继承关系,我们编写的 ExternalServletInitializer
和 SpringBootServletInitializer
都会被创建实例,并执行各自的 StartUp
方法,然而 ExternalServletInitializer 没有该方法,所以会调用其父类 SpringBootServletInitializer 的 onstartUp 方法。
6、SpringBootServletInitializer
执行 onStartup 方法会执行 createRootApplicationContext,在调用其中的 Configure
时,由于其子类 ExternalServletInitializer
重写了这个方法,所以会调用子类重写的这个方法,那么就会将 SpringBoot 的主程序类传入进来。
7、build SpringApplication 并启动;
6.4. 其他
https://www.bilibili.com/video/BV1gW411W76m?p=51&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
嵌入式的 Servlet 容器:应用打成 jar 包
优点:简单、便携
缺点:默认不支持 JSP、优化定制比较复杂(使用定制器【ServerProperties、自定义定制器】,自己来编写嵌入式的容器工厂)
外置的 Servlet 容器:外面安装 Tomcat 是以 war 包的方式打包。
https://www.bilibili.com/video/BV1gW411W76m?p=52&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
7. SpringBoot 的核心注解
1. @SpringBootApplication 注解:这个注解标识了一个 SpringBoot 工程,它实际上是另外三个注解的组合,这三个注解是:
① @SpringBootConfiguration :这个注解实际就是一个@Configuration,表示启动类也是一个配置类
② @EnableAutoConfiguration:向 Spring 容器中导入了一个 Selector,用来加载 ClassPath 下 SpringFactories 中所定义的自动配置类,将这些自动加载为配置 Bean
③ @ComponentScan,但这个注解是 Spring 提供的
2. @Conditional 也很关键,如果没有它我们无法在自定义应用中进行定制开发
@ConditionalOnBean、
@ConditionalOnClass、
@ConditionalOnExpression、
@ConditionalOnMissingBean 等。
8. 为什么 SpringBoot 的 jar 可以直接运行⭐️🔴
^lf3ecs
8.1. spring-boot-maven-plugin 打 Fat jar
❕ ^s0yjgz
SpringBoot 提供了一个插件
spring-boot-maven-plugin
用于把程序打包成一个可执行的 jar 包。SpringBoot 应用打包之后,生成一个 Fat jar(jar 包中包含 jar),包含了应用依赖的 jar 包和
Spring Boot loader
相关的类。❕
如果我们要看 spring-boot-loader 源码,那么就需要自己引入依赖
8.2. MANIFEST.MF 文件
- java -jar 会去找 jar 中的
MANIFEST.MF
文件,在那里面找到真正的启动类(Main-Class
),Fat jar 的启动 Main 函数是JarLauncher
,在spring-boot-loader.jar
中
[[JVM源码分析之JVM启动流程_猿灯塔_InfoQ写作社区#^qwkqfc]]
在 JarLauncher 类中的 main 方法中有 launch
方法,进入之后有 2 个非常重要的方法: createClassLoader
和 launch
8.3. LaunchedURLClassLoader
createClassLoader
负责创建一LaunchedURLClassLoader
来加载 BOOT-INF\lib 下面的 jar
8.4. launch
launch
负责以一个新线程启动应用的启动类的 Main 函数(找到 manifest 中的 Start-Class)
9. 自定义 starter⭐️🔴
📙❕ ^5tcs5g
https://www.bilibili.com/video/BV1Et411Y7tQ?p=70&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
9.1. 使用场景⭐️🔴
在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,只需要将其在 pom 中引用依赖即可,利用 SpringBoot 为我们完成自动装配即可。
常见的自定义 Starter 场景比如:
动态数据源
登录模块
基于 AOP 技术实现日志切面
❕ ^xbsang
场景一:简化多服务公用框架集成- 封装 swagger 配置
众所周知,springboot 或者其他第三方所提供的 starter,都是做框架集成,通过简化配置,提高开发效率,所以我们自定义 starter 的第一个应用场景也是基于这个思路。那我们日常开发工作中,有哪些框架是多个服务共用的,并且 springboot 或者其他第三方暂未提供,或者嫌弃第三方写的太烂,想自己重新实现的,都可以通过编写自定义 starter 来简化工作。我们公司采用微服务架构,每个服务都会使用 swagger 来生成在线接口文档。未封装 swagger-starter 之前,那么在每个服务里边,都需要增加 swagger 的配置类。而封装 swagger-starter 之后,可以省去这一步的操作,还可以通过增加自定义配置来实现一些自定义的功能。比如我们公司安全部门要求生产环境不能对外开放 swagger 接口文档地址,那么我们就可以添加一个 enabled 的参数来代表 swagger 是否启用,默认启用,在生产环境的配置中将 enabled 设为 false 即可达到这个目的。类似的额外功能还有很多,比如增加请求头等等,其他的读者自行发掘。
上面提到的是业务无关性的 starter 应用场景,那么我们抛出一个问题,是否有业务相关且多个业务场景或者多个服务会使用的应用场景?根据这个问题的描述,我们至少可以列出以下几个业务相关场景。 服务治理-10、限流熔断降级(服务保护)-Sentinel
场景二:服务间调用的鉴权。
我们公司内部服务之间互相调用需要进行鉴权(还是安全部门的要求),由于服务间是通过 feign 来实现相互调用,所以无法通过网关来进行统一鉴权。实现方案是通过新增 feign 拦截器,==在源头服务发起调用之前增加鉴权参数,请求到达目标服务后通过鉴权参数进行鉴权==。这两步操作很明显是每个服务都需要的,那么这种情况下,我们就可以把这两步操作封装成 starter,达到简化开发的目的。同时,我们还可以通过增加配置,实现更细粒度的调用权限控制,比如订单服务只能调用库存服务的查询商品库存接口,而无法调用更新商品库存的接口。
场景三:邮件,短信,验证码功能。
这些功能,在某些公司可能会放在 common 包里,但是这样其实会导致 common 包的臃肿,因为并不是所有服务都会使用到。有些公司(还是我们公司)可能对邮件服务器的访问有严格权限控制的,而且权限开通流程比较繁复的,那么会考虑做成服务,部署在已经具有访问权限的主机上,减去重复申请权限工作。如果除去这些限制,那么将这些功能封装成 starter 还是挺不错的,可以避免 common 包的臃肿
9.2. 操作步骤
9.2.1. 确定场景需要使用依赖
9.2.2. 使用注解编写自动配置
1 |
|
9.2.3. 编写 2 个模块
❕ ^8m431i
启动器空的 jar 只需要做依赖管理导入;比如
xxx-spring-boot-starter
,使用方只需要引入这个启动器即可专门写一个自动配置模块;比如
xxx-spring-boot-starter-autoconfigurer
,所有的依赖项都放在这个模块的META-INF/spring.factories
中,key 为org.springframework.boot.autoconfigure.EnableAutoConfiguration
同时 pom 中需要添加基础依赖spring-boot-starter
启动器依赖自动配置,别人只需要引入 starter
xxx-spring-boot-starter
❕
9.3. 与 common 包对比
- common 包不灵活,需要不需要的内容都在一个 common 包里,不需要的内容也随着 common 包引入到工程中
- 单个功能增加太多,common 包容易变的臃肿
9.4. 示例代码
[[SpringBoot-V1-入门简介#^u6byiq]]
[[pages/002-schdule/001-Arch/001-Subject/013-DemoCode/springboot2/boot-09-customer-starter/atguigu-hello-spring-boot-starter-autoconfigure/src/main/java/com/atguigu/hello/auto/HelloServiceAutoConfiguration.java]]
[[SpringBoot-V1-入门简介#8 1 新建一个 starter]]
10. 配置读取原理和优先级规则⭐️🔴
^uxezha
10.1. 读取原理
通过事件监听的方式读取的配置文件:ConfigFileApplicationListener
优先级从高到低,高优先级的配置覆盖低优先级的配置,所有配置会形成互补配置。ConfigFileApplicationListener
会监听 onApplicationEnvironmentPreparedEvent
事件来加载配置文件 application.properties
的环境变量
10.2. 优先级规则
10.2.1. 文件优先级
10.2.1.1. 外部配置源
常用:properties 文件、YAML 文件、环境变量、命令行参数;
10.2.1.2. properties 优于 ymal
正常的情况是先加载 yml,接下来加载 properties 文件。如果相同的配置存在于两个文件中。最后会使用 properties 中的配置。最后读取的优先级最高。
两个配置文件中的端口号不一样会读取 properties 中的端口号
10.2.2. 查找覆盖优先级
配置文件查找位置:查找覆盖顺序:优先级逐级变高
1. classpath 根路径
2. classpath 根路径下 config 目录
3. jar 包当前目录
4. jar 包当前目录的 config 目录
5. /config 子目录的直接子目录
10.2.3. 加载生效优先级
配置文件加载顺序:加载顺序:外部>内部、加 profile>不加 profile
1. 当前 jar 包内部的 application.properties 和 application.yml
2. 当前 jar 包内部的 application-{profile}.properties 和 application-{profile}.yml
3. 引用的外部 jar 包的 application.properties 和 application.yml
4. 引用的外部 jar 包的 application-{profile}.properties 和 application-{profile}.yml
10.2.4. 官方文档
指定环境优先,外部优先,后面的可以覆盖前面的同名配置项
10.2.5. 案例
11. SpringBoot 的默认日志实现框架及切换
https://www.bilibili.com/video/BV1mf4y1c7cV?p=82&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
11.1. SpringBoot 日志框架
1. SpringBoot 底层使用 slf4j+logback 的方式进行日志记录
logback 桥接:logback-classic
2. SpringBoot 同时也把其他的日志都替换成了 slf4j;
a. log4j2 适配: log4j-over-slf4j (默认提供了适配器,如果切换 log4j2 只需要更换场景启动器即可)
b. jul 适配:jul-to-slf4j
c. jcl 适配:jcl-over-slf4j
11.2. 将 logback 切换成 log4j2
1. 将 logback 的场景启动器排除(slf4j 只能运行有 1 个桥接器)
2. 添加 log4j2 的场景启动器
3. 添加 log4j2 的配置文件
1 |
|
11.3. 将 logback 切换成 log4j
1. 要将 logback 的桥接器排除
1 |
|
2. 添加 log4j 的桥接器
1 |
|
3. 添加 log4j 的配置文件
12. 容器
在 Web 环境中是由 Spring 和 SpringMvc 两个容器组成的,在 SpringBoot 环境中只有一个容器 AnnotationConfigEmbeddedWebApplicationContext。也就是可以说是由 SpringBoot 容器管理的。
12.1. 配置嵌入式 Servlet 容器
12.2. 嵌入式 Servlet 容器自动配置原理 v1⭐️🔴
https://www.bilibili.com/video/BV1Et411Y7tQ?p=48&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204 ^17d8bq
12.2.1. 引入依赖触发自动配置
当加入 Spring-boot-starter-web
场景启动器依赖时,@ConditionalOnWebApplication
注解会触发 EmbeddedServletContainerAutoConfiguration 自动配置类开启嵌入式 Servlet 容器配置
1 |
|
12.2.2. 创建默认的嵌入式容器 -EmbeddedServletContainerFactory
如果当前容器没有用户自定义 EmbeddedServletContainerFactory,就会创建一个默认的 TomcatEmbeddedServletContainerFactory,用来创建嵌入式容器,默认为 Tomcat,也支持其他嵌入式容器,3 种工厂分别可以创建 3 种 EmbeddedServletContainer,继承关系如下图所示:
以 TomcatEmbeddedServletContainerFactory 为例,new 了一个 Tomcat 出来并配置,然后传入 getTomcatEmbeddedServletContainer
方法,在该方法中会启动 Tomcat。SpringBoot2 中该方法名字为 getWebServer。
1 |
|
12.2.3. 嵌入式配置自动解析方式 -EmbeddedServletContainerCustomizerBeanPostProcessor
有 2 种方式:ServerProperties、EmbeddedServletContainerCustomizer
EmbeddedServletContainerCustomizer 自动应用 Servlet 容器配置原理:
是通过 EmbeddedServletContainerCustomizerBeanPostProcessor 来完成的
容器在配置类 EmbeddedServletContainerAutoConfiguration
中通过 @Import(BeanPostProcessorsRegistrar.class)
导入了 EmbeddedServletContainerCustomizerBeanPostProcessor,在 postProcessBeforeInitialization
方法中,判断如果当前初始化的是一个 ConfigurableEmbeddedServletContainer,就会获取所有的定制器,调用每个定制器的 customer 方法给 Servlet 容器进行赋值。
BeanPostProcessorsRegistrar.registerBeanDefinitions
1 |
|
EmbeddedServletContainerCustomizerBeanPostProcessor.postProcessBeforeInitialization
1 |
|
而 ServerProperties 也是 EmbeddedServletContainerCustomizer 实现类,所以也会被自动配置
12.2.4. 总结
1)、SpringBoot 根据导入的依赖情况,给容器中添加响应的容器工厂比如 tomcat
对应的 TomcatEmbeddedServletContainerFactory
2)、容器中某个组件要创建对象就要通过后置处理器;EmbeddedServletContainerCustomizerBeanPostProcessor
只要是嵌入式的 Servlet 容器工厂,后置处理器就工作;
3)、后置处理器,从容器中获取的所有的 EmbeddedServletContainerCustomizer,调用定制器的定制方法
12.3. 定制化原理
- 修改配置文件;
- xxxxxCustomizer;
1 |
|
- 编写自定义的配置类 xxxConfiguration;+ @Bean 替换、增加容器中默认组件;视图解析器
- Web 应用编写一个配置类实现
WebMvcConfigurer
即可定制化 web 功能;+@Bean
给容器中再扩展一些组件
1 |
|
- @EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管 SpringMVC,所有规则全部自己重新配置;实现定制和扩展功能
○ 原理
○ 1、WebMvcAutoConfiguration 默认的 SpringMVC 的自动配置功能类。静态资源、欢迎页…..
○ 2、一旦使用 @EnableWebMvc 会@Import(DelegatingWebMvcConfiguration.class
)
○ 3、DelegatingWebMvcConfiguration 的作用,只保证 SpringMVC 最基本的使用
■ 把所有系统中的 WebMvcConfigurer 拿过来。所有功能的定制都是这些 WebMvcConfigurer 合起来一起生效
■ 自动配置了一些非常底层的组件。RequestMappingHandlerMapping、这些组件依赖的组件都是从容器中获取
■ public class DelegatingWebMvcConfiguration extendsWebMvcConfigurationSupport
○ 4、WebMvcAutoConfiguration 里面的配置要能生效必须 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
○ 5、@EnableWebMvc 导致了 WebMvcAutoConfiguration 没有生效。
13. Java Config
13.1. @Configuration(proxyBeanMethods = false)
lite 模式下,直接返回新实例对象,不用生成代理。
Spring 5.2.0+ 的版本,建议你的配置类均采用 Lite 模式去做,即显示设置 proxyBeanMethods = false
。Spring Boot 在 2.2.0 版本(依赖于 Spring 5.2.0)起就把它的所有的自动配置类的此属性改为了 false,即@Configuration(proxyBeanMethods = false),目的是为了提高 Spring 启动速度
https://blog.csdn.net/yunxing323/article/details/108655250
13.2. @Import
14. Rest 原理
14.1. filter+wrapper
Rest 原理(表单提交要使用 REST 的时候)
● 表单提交会带上 _method=PUT
● 请求过来被 HiddenHttpMethodFilter
拦截
○ 请求是否正常,并且是 POST
■ 获取到 _method 的值。
■ 兼容以下请求;PUT、DELETE、PATCH
■ 原生 request(post),包装模式 requesWrapper
重写了 getMethod 方法,返回的是传入的值。
■ 过滤器链放行的时候用 wrapper。以后的方法调用 getMethod 是调用 requesWrapper 的。
Rest 使用客户端工具,
● 如 PostMan 直接发送 Put、delete 等方式请求,无需 Filter,但要注意方式可用取值为 GET、PUT、DELETE、PATCH
。
14.2. 自定义隐藏域名称
1 |
|
15. 请求映射原理⭐️🔴
❕ ^ijjhr0
https://www.bilibili.com/video/BV1Et411Y7tQ?p=139&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
DispatchServlet
15.1. 请求路径
HttpServlet.doGet
→ FrameworkServlet.processRequest
→ FrameworkServlet.doService
→ DispatcherServlet.doService
→ DispatcherServlet.doDispatch
(每个请求都会调用这个方法)
15.2. 执行逻辑
15.2.1. handlerMappings
● 请求进来,挨个尝试所有的 HandlerMapping 看是否有请求信息。
○ 如果有就找到这个请求对应的 handler
○ 如果没有就是下一个 HandlerMapping
15.2.2. RequestMappingHeaderMapping
保存了所有@RequestMapping 和对应 handler 的所有映射关系
15.2.3. 获取 handler 逻辑
15.2.4. mappingRegistry
16. 其他
16.1. jar 包引入方式
https://cloud.tencent.com/developer/article/1500334
16.1.1. spring-boot-starter-parent
16.1.2. spring-boot-dependencies
https://blog.csdn.net/haohao_g/article/details/99695535
使用场景:可能有自己的企业标准 parent
或者你可能只是比较喜欢明确声明所有的 Maven 配置
16.2. 多继承问题
https://www.jianshu.com/p/7145f01ac3ad
16.3. dependencyManagement
dependencyManagement 节点的作用是统一 maven 引入依赖 JAR 包的版本号,可以看出 spring-boot-dependencies 最重要的一个作用就是对 springboot 可能用到的依赖 JAR 包做了版本号的控制管理
17. 与 Spring 的不同之处
17.1. 动态代理
- Spring 是动态选择的,有接口就用 JDK 动态代理,否则使用 cglib 动态代理
可以通过exproxy-target-class
1、Spring-基础 - SpringBoot 默认是 cglib 动态代理
17.2. SpringMVC 容器⭐️🔴
- Spring 集成 SpringMVC 是通过 SPI 方式注册子容器来实现集成
- SpringBoot 通过@Bean 注册了 DispatcherServlet,并没有像 Spring 集成 SpringMVC 中那样,通过 SPI 的方式创建子容器
18. 参考与感谢
18.1. SpringBoot
18.1.1. 视频
01、P112–P198 为雷神 2021 版 springboot2 教程 <– 建议直接看新版 02、2021 版直达链接: https://www.bilibili.com/video/BV1Et411Y7tQ?p=112&spm_id_from=333.788.b_636f6d6d656e74.4
/Volumes/Seagate Bas/001-ArchitectureRoad/尚硅谷 Springboot 经典版(核心技术 and 整合篇)/核心技术篇/视频 3/视频 3 ^yfxc8z
18.1.2. 代码
/Users/taylor/Nutstore Files/Obsidian_data/pages/002-schdule/001-Arch/001-Subject/013-DemoCode/SpringBoot
18.1.3. 笔记
/Users/taylor/Nutstore Files/Obsidian_data/pages/002-schdule/001-Arch/001-Subject/002- 框架源码专题/000-Spring/SpringBoot
[[SpringBoot]]
18.2. SpringBoot2
18.2.1. 视频
https://www.bilibili.com/video/BV19K4y1L7MT?p=58&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204
18.2.2. 笔记
语雀: https://www.yuque.com/atguigu/springboot
18.2.3. 代码
/Users/taylor/Nutstore Files/Obsidian_data/pages/002-schdule/001-Arch/001-Subject/013-DemoCode/springboot2
18.3. 汇总
01、P112–P198 为雷神 2021 版 springboot2 教程 <– 建议直接看新版 02、2021 版直达链接: https://www.bilibili.com/video/BV1Et411Y7tQ?p=112&spm_id_from=333.788.b_636f6d6d656e74.4
2021 版配套笔记及源码:
配套源码 - 雷神码云地址: https://gitee.com/leifengyang/springboot2
配套笔记 - 语雀地址: https://yuque.com/atguigu/springboot
旧版配套源码、文档等:
尚硅谷 springboot 核心篇 + 整合篇配套资料
链接: https://pan.baidu.com/s/1Yfv05ncJoP_gOHB6cm9jdg 提取码: 9h5i
18.4. 其他
https://www.bilibili.com/video/BV1zh411H79h?p=4
18.5. Spring
1、Spring-基础18.6. 黑马⭐️🔴✅
18.6.1. 视频
18.6.2. 资料
1 |
|