前置知识:
学习视频:https://www.bilibili.com/video/BV1PE411i7CV?spm_id_from=333.337.search-card.all.click
自动装配原理
SpringBoot是企业级开发的整体整合解决方案,特别用于快速构建微服务应用,旨在用最简单的方式让开发人员适应各种开发场景
本视频着重介绍SpringBoot的使用和内部原理;
内容包含微服务概念、配置文件、日志框架的使用、web开发、Thymeleaf模板引擎、Docker容器技术教程由springboot核心技术+整合篇组成
SpringBoot
graph LR A[SpringBoot]-->是什么 A-->配置如何编写 A-->自动装配原理:重要 A-->继承web开发:业务开发 A-->继承数据库Druid A-->c["分布式开发:Dubbo(RPC接口风格)+zookeeper"] A-->swagger:接口文档 A-->任务调度 A-->SpringSecurity,拦截器,shiro
简介
SpringBoot:简化Spring应用的配置文件,使用嵌入式Tomcat,含诸多开箱即用的微服务组件
Spring Cloud:为开发者提供了微服务动态访问与控制的开发工具包
一个SpringBoot项目就是让Spring启动的项目。过去要启动一个Spring项目,需要配置很多的xml文件,使用SpringBoot之后,可以极大地简化配置过程。
SpringBoot将所有功能场景封装为一个个的启动器,在开发中只需要导入相应的启动器,启动器就会帮我们导入该场景所需要的带有默认配置的组件,让开发过程专注于业务逻辑。
类比一下,Maven管理所以的jar包,docker整合了所有环境,SpringBoot整合了所有框架
启动器
1 | <!--启动器--> |
启动器:Springboot的启动场景,需要什么功能,找对应的启动器即可
eg:
spring-boot-starter-web
:会自动导入web环境相关的所有依赖
两大特点
开箱即用
在开发过程中,通过maven项目的pom配置文件添加相关依赖包,然后通过注解代替xml配置以管理Bean的生命周期
约定大于配置
由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的结构标准
优点
- 快速创建独立运行的Spring项目以及主流框架集成
- 使用嵌入式Servlet容器,应用无需打成war包,jar包即可
- 由starters完成自动依赖与版本控制
- 大量的自动配置,简化开发,也可修改默认值
- 无需配置XML,无代码生成,开箱即用
- 准生产环境的运行时应用监控
- 与云计算天然继承
Spring如何构建微服务
- 通过SpringBoot快速构建一个个功能独立的微服务应用单元
- 大新分布式网络服务的调用,由SpringCloud完成,实现分布式
- 在分布式中间,进行流式数据计算,批处理,spring cloud data flow
Hello程序
0. 环境
修改主类位置
重启idea后解决
Maven
1 | <profiles> |
1. 新建Project
1 | <dependencies> |
<parent>
: 继承spring-boot-starter-parent
的依赖管理,控制版本与打包等内容即继承spring-boot-dependencies:所有依赖版本控制
引入SpringBoot依赖时,不需要指定版本,因为有版本仓库
项目元数据信息:创建时的Project Metadata部分,包括groupid,artifactedId,version,name,description等
dependencies:项目具体依赖
spring-boot-starter-web
用于实现web应用- SpringMVC
- RESTful
- Tomcat作为默认的嵌入式容器
spring-boot-starter-test
用于编写单元测试的依赖包
build:项目构建配置
spring-boot-maven-plugin
:配合Spring-boot-starter-parent
将SpringBoot应用打包成JAR直接运行
2.标注主程序类
1 |
|
@SpringBootApplication
,说明这个类是SpringBoot的主配置类
3. 导入web依赖
1 | <dependency> |
1 |
|
4. 改端口号
application.properties
1 | #更改端口号 |
5.部署
将应用打包成可执行的jar包
1 | <build> |
自动装配
自动装配示例
需求:定义一个注解,让使用了这个注解的应用程序自动化地注入一些类或者做一些底层的事情
在应用程序的入口加上 @EnableMyConfig
注解。这样的话,MyConfig就被注入进来了
SpringBoot也就是用这个完成的。只不过它用了更加高级点的ImportSelector。
什么是自动装配
SpringBoot启动时会根据启动器
starter
加载自动配置类,就是将带有默认配置信息的组件导入Spring容器只要自动配置的组件满足场景需求,则无需手动配置
若应用场景所需的环境没有对应的组件,再尝试手动配置
可以在配置文件
application.yml
中修改这些自动装配的组件的信息,可以配置的属性封装在了与XXXAutoConfiguration
对应的XXXProperties
@SpringBootApplication
将这个类标注为Springboot主配置类;导入并启动类下的所有资源
@SpringBootConfiguration
表示这是一个Spring的配置类,以组件的方式加入到Spring容器中
相当于XML配置文件
<xml></xml>
@ComponentScan
自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
相当于扫描并加载XML配置文件中的组件
<bean></bean>
@EnableAutoConfiguration
开启自动而配置功能
@AutoconfigurationPackage
导入组件
Registrar
将主启动类的所在包及包下的所有子包中的配置类注册到Spring容器
@Import({AutoConfigurationImportSelector.class})
导入
自动配置导入选择器
组件用于配置导入哪些自动配置类
@EnableAutoConfiguration
注解中使用了 AutoConfigurationImportSelector
(自动配置类的导入选择器),用于选择自动配置类,导入Spring容器后,以这些自动配置类全限定名数组的形式返回给主配置类。
第1层
selectImports()
AnnotationMetadata指向的是主配置类,也就是
AutoConfigurationImportSelector
会为标注了主配置类(标注了@SpringBootApplication
)导入自动配置类,并将这些自动配置类(也就是可以被自动导入的组件)的全限定名以字符串数组的形式返回给主配置类
那这些自动配置类哪里来的
由一个与 selectImports()
方法同一层的 getAutoConfigurationEntry()
选择
在这个方法中,调用了 getCandidateConfigurations()
方法,将候选的配置类以字符串列表的形式返回,再经过一系列的排除,过滤操作,才将最终的自动配置类的全限定名数组返回给主配置类。
getCandidateConfigurations()
方法将获取到的所有配置类的全限定名,保存到 List<String> configurations
字符列表中
那这些候选的自动配置类又从哪里来,看 getCandidateConfiguration()
方法的执行流程
第二层:getCandidateConfigurations()
显然,是调用了 SpringFactoriesLoader.loadFactoryName
获取的全限定名列表
第三层:SpringFactoriesLoader执行流程
上面这些代码片段的意思就是,SpringFactoriesLoader
会从类路径中查找 spring.factories
的文件,将文件中的全限定名逐一封装为 Properties
类的实例
自动装配核心配置——XXXAutoConfiguration
eg:
getCandidateConfigurations 方法中的 getSpringFactoriesLoaderFactoryClass 方法返回的是EnableAutoConfiguration.class
,所以会过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值。
下面这段配置代码就是autoconfigure这个jar包里的spring.factories文件的一部分内容(有个key为org.springframework.boot.autoconfigure.EnableAutoConfiguration,所以会得到这些AutoConfiguration)
1 | # Auto Configure |
当然了,这些AutoConfiguration不是所有都会加载的,会根据AutoConfiguration上的
@ConditionalOnClass
经过exclude和filter等操作,最终确定要装配的类。
@ConditionalOnXXX
@Conditional 扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中是否存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean |
@ContidionalOnProperty | 系统中指定的属性是否有指定的值 |
@ContidionalOnResource | 类路径下是否存在指定资源文件 |
@ContidionalOnWebApplication | 当前是web环境 |
@ContidionalOnNotWebApplication | 当前不是Web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
总结
- 自动装配利用了
SpringFactoriesLoader
会根据FACTORIES_RESOURCE_LOCATION
这个静态变量指出spring.factories
的路径,然后从所有的jar包中读取根据这个文件内容指出的自动配置类。将类信息封装为propreties
类型的对象 - 在若干
@ConditionalOnXXX
的约束下,经过exclude和filter等操作,确定待装配的类是否生效 - 所有需要导入的组件,以类名的方式返回,这些组件在
starter
作用下,自动装配到相应的环境
SpringApplication.run(Springboot01Application.class, args);
将SpringBoot启动
- _推断应用类型是普通项目还是web项目_
- 查找并加载所有可用的初始化器,设置到initializers属性中
- 找出所有的而应用程序监听器,设置到listeners属性中
- _推断并设置main方法的定义类,找出运行的主类_
总结
1. 什么会被自动装配
我只知道如何选择自动装配的组件这块,至于这些组件在Spring容器生命周期的哪个阶段加入就没有了解了。
选择哪些组件会被自动配置,与 @SpringBootApplication
这个注解有关。他有三个重要的注解
第一个是 @SpringBootConfiguration
,表示启动类也是一个配置类,会以Bean的形式注册到 Spring 容器中
第二个是 @ComponentScan
,这个注解本身的作用是指出包扫描路径。由于没有配置,默认是扫描主配置类所在包及子包下的组件
第三个是 @EnableAutoConfiguration
表示开启自动装配功能。他有两个功能,第一个是选择支持自动装配的组件,然后将这些组件的全限定名以字符串数组的方式返回给主配置类,第二个是将需要自动装配组件导入到Spring容器中。这个两个功能分别由两个注解完成,@Import({AutoConfigurationImportSelector.class})
和 @AutoConfigurationPackage
@Import({})
就是通过反射机制导入 AutoConfigurationImportSelector
,这个选择器中有一个 selectImports()
方法,完成自动配置类的选择和全限定名的返回
具体做法是调用 getAutoConfigurationEntry()
方法筛选主配置类需要的组件,然后返回全限定名。而待筛选的自动配置类,由 getCandidateAutoConfiguration()
返回,这些自动配置类的全限定名是由 SpringFactoriesLoader
加载 spring.factories
文件得到的
@AutoConfigurationPackage
:会导入一个 Registrar
注册器,将启动类所在包及其子包下的组件注册到Spring容器中,就是把自己写的Bean导入到Spring容器中
而我们平时在 application.yml
主配置文件中的配置项,是与相应的 XXXProperties
中的属性绑定的。 XXXAutoConfiguration
这些可以自动装配的组件,会通过 XXXProperties
获取到配置信息,然后在 @ConditionOn()
注解的作用下,这些可以被自动装配的组件有选择的注册称为Spring容器中的Bean
2.XXXAutoConfiguration
在application.yaml中能配置的属性,这些属性的默认值都在对应的 XXXProperties
中 :
- 一定会存在对应的
XXXAutoConfiguration
类,由@EnableAutoContiguration
将这些配置类封装为 Properties 类加载到运行主类中
3. 每一个XXXAutoConfiguration进行自动配置功能
1 |
|
- 根据
@ConditionalOnXXX
判断当前XXXAutoConfiguration
是否生效 - 一旦
XXXAutoconfiguration
生效,这个配置类就会给容器中添加各种组件 (由@Bean
标注) ; - 这些组件的属性是从对应的XXXProperties类中获取
- XXXAutoconfiguration的每一个属性都是和配置文件绑定,并可通过配置文件修改
4. 配置文件中能配置的属性都在XXXProperties中封装
1 |
|
在 application.yaml
中修改的是这些配置类的属性值
检测自动配置类是否生效
1 | debug: true |
生效的
没生效的:
排除的和不满足条件的:
配置文件
yaml
yaml基本语法
以数据为中心
_空格控制层级_
属性和值,大小写敏感
“[内容]”:不转义,输出特殊字符表示的内容
name: “hello \n !” _>hello [换行] !
‘[内容]’:转义,将特殊字符以字符串形式输出
name: ‘hello \n !’ _> hello \n !
1 | <server> |
1 | student.name=qinjiang |
1 | server: |
配置文件占位符
properties
文件和yaml
文件都适用
$ 查找的是当前yaml中及其支持的变量
随机数${random.XXXX}
- ${random.int}
- ${random.uuid}
获取之前的值,指定默认值
- ${[A]:[B]}:存在用A,不存在用B
1 | person: |
注入,给实体类赋值
之前
1 |
|
相关依赖
1 | <dependency> |
1 | person: |
@ConfigurationProperties
@ConfigurationProperties(prefix=)
将本类的属性与配置文件中的相关配置绑定
- prefix(“”):指定配置文件中哪个变量进行映射
- 只有在 IoC容器中的组件才能使用这个注解
1 |
|
@PropertySource
@PropertySource("classpath:myconfig.xml")
加载指定的配置文件
1 |
|
区别
@ConfigurationProperties
与 @Value
@ConfigurationProperties | @Value | ||
---|---|---|---|
功能 | 从全局配置文件中,批量注入属性 | 一个个指定 | |
松散绑定(松散语法) | 支持 | 不支持 | last-name_>lastName |
SpEL | 不支持 | 支持 | #{11*2} |
JSR303数据校验 | 支持 | 不支持 | @Validated |
复杂类型封装 | 支持 | 不支持 | ${maps.key} |
松散绑定
yml中横线命名 : java中驼峰命名
last-name : lastName
JSR303校验
@Validated
_>开启数据验证
1 |
|
结论
- 在某个业务中,只需要配置某个属性的值:
@Value
- 专门编写了一个JavaBean来和配置文件映射:
`@ConfigurationProperties
全局配置文件
配置文件名是固定的
- application.properties
- 语法:key=value
- application.yml
- 语法:key: value
可以修改SpringBoot自动配置的默认值
全局配置文件的加载位置
项目内
- ./
./config
classpath:/
- classpath:/config
命令行修改配置文件加载位置
springjava -jar [打包后包名] —spring.config.location=F:/application.properties
优先级
- 项目路径/config/application.yml
- 项目路径/application.yml
- 资源路径/config/application.yml
- 资源路径/applicatin.yml
外部配置加载顺序
原则
- 互补配置
- 高优先级覆盖低优先级
其他配置文件的加载
@PropertySource(value={"",""})
读取指定的配置文件
@ImportResource(location={"",""})
在一个配置类上标注,导入Spring配置文件,让其生效
SpringBoot推荐使用配置类编写配置
以前
1 |
|
配置类
1 | /* |
多环境切换——Profile
profiles
是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境;
我们在主配置文件编写的时候,文件名可以是 application-{profiles}.properties/yml
, 用来指定多个环境版本;
但是Springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件;
激活方式
配置文件
1 |
命令行
java -jar [打包后包名] —spring.profiles.active=dev
jvm参数
-Dspring.profiles.active=dev
yaml多文档块
日志
日志门面(日志抽象层) | 日志实现 |
---|---|
Log4j,Logback(同一人) log4j2, |
springboot:SLF4J+Logback
默认日志依赖
1 | <dependency> |
- SpringBoot底层也是使用SLF4J+logback进行日志记录
- SpringBoot通过中间替换包把其他日志替换成了SLF4J
- 使用其他框架,一定要把这个框架默认的日志依赖移除
1 | <dependency> |
日志文档与框架关系
SLF4J可以由Log4j,logback实现,也可以通过适配层,将其他实现框架转化为SLF4J框架。即一个应用内的所有日志抽象层都是用SLF4J框架
其他框架的统一转换为SLF4J
不同层使用不同的日志框架,通过适配层统一转化为SLF4J框架
- 将系统中其他日志框架先排除出去
- 用中间包替换原有日志框架
- 导入SLF4J的其他实现
框架每一个日志的实现实框架都有自己的配置文件,使用SLF4J后,配置文件为实现日志实现框架的配置文件
切换日志实现框架
根据SLF4J的日志适配图,进行相关切换
SpringBoot默认配置
0.配置原理
获得配置文件中的属性值
1. 导包
1 | import org.slf4j.Logger; |
1 | Logger logger = LoggerFactory.getLogger(getClass()); |
2. 日志级别
调整需要输出的日志级别
trace < debug < info < warn < error
1 | logging: |
3. 指定日志信息的输出格式
4. 日志输出路径
1.在当前路径下输出
1 | logging: |
2. 指定输出到某一目录下的文件
1 | logging: |
3. 本项目下的目录,默认的文件名为spring.log
1 | logging: |
SpringBoot自定义日志配置
每一个日志的实现实框架都有自己的配置文件,使用SLF4J后,配置文件为实现日志实现框架的配置文件
将 [实现框架名].xml
放在在类路径下,即替换
Logging System | Customization |
---|---|
Logback | logback-spring.xml , logback-spring.groovy , logback.xml , or logback.groovy |
Log4j2 | log4j2-spring.xml or log4j2.xml |
JDK (Java Util Logging) | logging.properties |
logback.xml:直接就被日志框架识别
logback-spring.xml:由spring识别,可以使用 profile
多环境日志输出
1 | <!--可以根据环境调整输出--> |
1 | spring: |
日志使用
1. 自定义日志配置文件
1 | # application.properties |
2. 日志配置
在resources下面创建一个官方推荐:logback-spring.xml
带spring后缀的可以使用
<springProfile >
这个标签来指定环境
根节点\
日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出
- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
1 | 总体说明:根节点下有2个属性,三个节点 |
<root>
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。
默认是DEBUG。
可以包含零个或多个元素,表示这个appender将会添加到这个loger。
<contextName>
设置上下文名称
每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改,可以通过
%contextName
来打印日志上下文名称,一般来说我们不用这个属性,可有可无。
<property>
设置变量
用来定义变量值的标签, 有两个属性,name和value;其中name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。
<appender>
appender用来格式化日志输出节点,有两个属性name和class
- class用来指定哪种输出策略,常用就是控制台输出策略和文件输出策略。
控制台输出ConsoleAppender
1 | <appender name="consoleLog1" class="ch.qos.logback.core.ConsoleAppender"> |
都可以将事件转换为格式化后的日志记录,但是控制台输出使用layout
,文件输出使用encoder
<encoder>
表示对日志进行编码:
1 | %d{HH: mm:ss.SSS}——日志输出时间 |
ThresholdFilter为系统定义的拦截器,例如我们用ThresholdFilter来过滤掉ERROR级别以下的日志不输出到文件中。如果不用记得注释掉,不然你控制台会发现没日志~
输出到文件 RollingFileAppender
随着应用的运行时间越来越长,日志也会增长的越来越多,将他们输出到同一个文件并非一个好办法。RollingFileAppender
用于切分文件日志:
<loger>
<loger>
用来设置某一个包或者具体的某一个类的日志打印级别、以及指定 <appender>
。
name:用来指定受此loger约束的某一个包或者具体的某一个类。
level
用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特殊值INHERITED或者同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前loger将会继承上级的级别。
addtivity:是否向上级loger传递打印信息。默认是true
1 |
|
3. 使用
SLF4J的jar和Logback的实现jar
1 | import org.slf4j.Logger; |
AOP实现自定义日志
定义切点注解
定义切面
在切点切入
web
- 导入静态资源
- 首页
- jsp,模板引擎Thymeleaf
- 装配扩展SpringMVC
- CRUD
- Inteceptor
- 国家化
使用SpringBoot:
- 创建SpringBoot应用,选中模块
- SpringBoot默认将这些场景配置好,只需要在配置文件中指定少量配置即可
- 自己编写业务代码
自动配置原理:
- 这个场景SpringBoot帮我们配置了什么?能不能修改!
1 | XXXAutoconfiguration |
导入静态资源
1 |
|
1. webjars
webjars:以jar包的方式导入静态资源
所有 /webjars
的静态资源请求,都被映射到 classpath:/META-INF/resources/webjars/
下
1 | <dependency> |
2. 静态资源的默认查找位置
总结
- wabjars :一般不用
- rescources :上传的文件
- static :静态资源,图片
- public :公共资源
localhost:8080/webjars/**
优先级
rescources>static>public
首页
位置
首页 从静态资源路径
CLASSPATH_RESOURCE_LOCATIONS
+index.gtml中查找
在Templates目录下的所有页面只能通过Controller跳转,相当于 WEB-INF
需要TemplateEngine
模板引擎支持
模板引擎——ThymeLeaf
为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。
模板引擎的实现方式有很多,最简单的是“置换型”模板引擎,这类模板引擎只是将指定模板内容(字符串)中的特定标记(子字符串)替换一下便生成了最终需要的业务数据(比如网页)。
类比:JSP
启动器
spring-boot-starter-thymeleaf
1 | <dependency> |
默认位置
页面默认位置为classpath:/templates/
导入thymeleaf约束
1 | <html xmlns:th="http://www.thymeleaf.org"> |
基本语法
所有的html元素都可被 thymeleaf替换接管, th:元素名
属性优先级
JSP | Feature | Attributes |
---|---|---|
jsp:include | Fragment inclusion | th:insert th:replace |
c:forEach | Fragment iteration | th:each |
流程控制 | Conditional evaluation | th:if th:unless th:switch th:case |
声明变量 c:set | Local variable definition | th:object th:with |
属性修改,支持 prepend,append | General attribute modification | th:attr th:attrprepend th:attrappend |
修改指定属性默认值 | Specific attribute modification | th:value th:href th:src ... |
修改标签体内容 | Text (tag body modification)是否转义 | th:text 转义特殊字符th:utext 不转义特殊字符 |
声明片段 | Fragment specification | th:fragment |
Fragment removal | th:remove |
表达式
Simple expressions:
- Variable Expressions:
${...}
:获取变量值
1 | /* |
内置的基本对象
1
2
3
4
5
6
7
8
9#ctx: the context object.
#vars: the context variables.
#locale: the context locale.
<!--(only in Web Contexts)-->
#request: the HttpServletRequest object.
#response: the HttpServletResponse object.
#session: the HttpSession object.
#servletContext: the ServletContext object.
内置的工具对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#execInfo: information about the template being processed.
#messages: methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
#uris: methods for escaping parts of URLs/URIs
#conversions: methods for executing the configured conversion service (if any).
#dates: methods for java.util.Date objects: formatting, component extraction, etc.
#calendars: analogous to #dates, but for java.util.Calendar objects.
#numbers: methods for formatting numeric objects.
#strings: methods for String objects: contains, startsWith, prepending/appending, etc.
#objects: methods for objects in general.
#bools: methods for boolean evaluation.
#arrays: methods for arrays.
#lists: methods for lists.
#sets: methods for sets.
#maps: methods for maps.
#aggregates: methods for creating aggregates on arrays or collections.
#ids: methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
Selection Variable Expressions:
*{...}
:配合th:object使用Message Expressions:
#{...}
获取国际化内容Link URL Expressions:
@{...}
:定义URL
- @{/order/process(execId=${execId},execType=’FAST’)}
- Fragment Expressions:
~{...}
Literals 字面量
- Text literals:
'one text'
,'Another one!'
,… - Number literals:
0
,34
,3.0
,12.3
,… - Boolean literals:
true
,false
- Null literal:
null
- Literal tokens:
one
,sometext
,main
,…
Text operations:文本拼接
- String concatenation:
+
- Literal substitutions:
|The name is ${name}|
Arithmetic operations:数学运算
- Binary operators:
+
,-
,*
,/
,%
- Minus sign (unary operator):
-
Boolean operations:布尔运算
- Binary operators:
and
,or
- Boolean negation (unary operator):
!
,not
Comparisons and equality:比较运算
- Comparators:
>
,<
,>=
,<=
(gt
,lt
,ge
,le
) - Equality operators:
_
,!=
(eq
,ne
)
Conditional operators:条件运算
- If-then:
(if) ? (then)
- If-then-else:
(if) ? (then) : (else)
- Default:
(value) ?: (defaultvalue)
Special tokens:
- No-Operation:
_
All these features can be combined and nested:
1 | 'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown')) |
Thymeleaf请求转发实现机制
redirect
底层也是response.sendredirect 实现页面重定向
渲染页面,并将model中属性转为request参数,并重定向到目标页面
request
1 | /** |
MVC
SpringBoot Web MVC Framework
1. Inclusion of ContentNegotiatingViewResolver
and BeanNameViewResolver
beans.
自动配置了视图解析器:根据方法返回值得到视图对象,视图对象决定页面如何渲染
ContentNegotiatingViewResolver:用于组合所有视图解析器
- 将所有的viewResolvers装配到servletcontext中
可以自己给容器中添加一个ViewResolver;自动将其组合进IoC容器
2. Support for serving static resources, including support for WebJars .
静态资源路径,支持webjars
3. Static index.html
support.
静态首页访问
4. Automatic registration of Converter
, GenericConverter
, and Formatter
beans.
converter(转换器)
:前端提交的数据自动封装为相应的POJO
Formatter(格式化器)
:字符串_>Date类型
自己添加的 formatter,只需要注册到容器中即可
5. Support for HttpMessageConverters
.
SpringMVC用来转换Http请求和响应
POJO-User_>JSON
只有一个有参构造器,其参数都是从容器中获取
自己添加的 HttpMessageConverters,只需要注册到容器中即可
1 | import org.springframework.boot.autoconfigure.http.HttpMessageConverters; |
6. Automatic registration of MessageCodesResolver
.
消息编码解析器;定义错误代码生成规则
7. Automatic use of a ConfigurableWebBindingInitializer
bean.
从容器中获取一个组件
初始化WebDataBinder
请求数据__JavaBean
扩展MVC功能
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration
class of interface WebMvcConfigurer
but without @EnableWebMvc
.
实现页面跳转+拦截器
1 |
|
1 | <!--拦截器配置--> |
1 | public class MyInteceptor implements HandlerInterceptor{ |
扩展SpringMVC,_互补配置_
1 |
|
配置原则
SpringBoot自动配置组件,先看用户是否自己配置,若用户自定义用户配置,则用用户的;若没有则使用自动配置的;
对于同时可存在多个的组件(ViewResolver),则组合使用。
自动配置生效原理
WebMvcAutoConfiguration是SpringMVC的自动配置类
@Import(EnableWebMvcConfiguration.class)
addWebMvcConfigurers
将所有实现了WebMvcConfigurer的configuration一起调用
可见SpringBoot实现的自动配置会与自定义的自动配置一起调用形成_互补配置_
第一个MVC项目
1. 环境搭建
POJO
1 |
|
Dao
1 | public class DepartmentDao { |
1 |
|
2. 首页配置
所有的静态资源都要使用 Thymeleaf
接管
Thymeleaf依赖
1 | <dependency> |
Thymeleaf约束
1 | xmlns:th="http://www.thymeleaf.org" |
关闭模板引擎的缓存
1 | spring: |
设置首页
不推荐
1
2
3
4
5
6
7
8
9
10
11
12
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
};
return adapter;
}
- 【推荐使用】
1 |
|
设置路径前缀
1 | server: |
将静态资源交给Thymeleaf统一管理
3. 国际化
之前
- 编写国际化配置文件
- 使用ResourceBUndleMessageSource管理国际化资源
- 在页面使用fmt:message取出国际化内容
SpringBoot原理
国际化Locale(区域信息对象)
LocaleResolver(获取区域信息对象)
- 给容器中添加LocaleResolver组件
1 | /** |
1 |
|
默认的就是根据请求头带来的区域信息获取Locale进行国际化
自定义组件只需要实现
LocaleResolver
接口即可
步骤
1. 编写到国际化配置文件中,抽取页面需要显示的国际化消息
2. SpringBoot自动配置好了管理国际化的组件
1 |
|
3. 修改配置文件路径
1 | spring: |
4. 修改国际化的属性
5. 自定义国际化组件
1 | import org.springframework.web.servlet.LocaleResolver; |
1 |
|
4. 登录+拦截器
登录
- 前端表单传入数据,LoginController处理,验证正确,则返回后台页面(dashboard);验证错误,携带信息返回登录页面
- 开发期间,使用模板引擎的注意点
- 禁用Thymeleaf缓存
- 页面修改完后,重新编译
- 登录失败后,显示错误提示信息
- Thymeleaf属性优先级
- th:if语法
实现:
1 | <form class="form-signin" th:action="@{/user/login}" method="post"> |
1 |
|
考虑到刷新页面
重新提交表单
问题
使用 return “redirect:/dashboard.html”
防止表单重复提交——重定向
1 |
|
新问题:直接输入地址,会跳转到目标页面
解决:使用拦截器,拦截非法请求
拦截器进行登录检查
将登录状态加入到session中
拦截的情况:
- 没有登录_>session中,user为null
放行的情况:
- 用户已经登录
注册拦截器
WebMvcConfigurerAdapter.addInterceptor
使用拦截器的请求
~.addPathPatterns()
所有请求
不使用拦截器的请求
~.excludePathPatterns()
首页、登录提交表单
实现:
1 |
|
1 | public class MyInterceptor implements HandlerInterceptor { |
1 |
|
5. CRUD
要求
restfulCRUD:CRUD满足Rest风格
URI:资源名称/资源表示
HTTP请求方式区分对资源的CRUD操作
普通CRUD(URI区分资源操作) | RestfulCRUD() | |
---|---|---|
查询 | getEmp | emp————Get方式 |
添加 | addEmp?XXX | emp————POST方式 |
修改 | updateEmp?XXX | emp/{id}——PUT方式 |
删除 | deleteEmp?id= | emp/{id}——DELETE方式 |
- 实验的请求架构
请求URI | 请求方式 | |
---|---|---|
查询所有员工 | emps | GET |
查询某个员工 | emp/{id} | GET |
跳转添加页面 | emp | GET |
添加员工 | emp | POST |
跳转修改页面(查出员工进行信息回显) | emp/{id} | GET |
修改员工 | emp | PUT |
删除员工 | emp/{id} | GET |
R——列表页面
前端点击 员工信息 _> 发送emps请求
EmpController处理,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class EmployeeController {
EmployeeDao employeeDao;
public String emps(Model model){
Collection<Employee> employees = employeeDao.getEmployees();
//将信息放在请求域中返回
model.addAttribute("emps",employees);
//thymeleaf默认拼串
//classpath:/templates/xxx.html
return "emp/list";
}
}公共元素抽取
公共片段抽取 th:fragment
1
2
3<div th:fragment="copy">
</div>引入公共片段
1
2
3
4<div th:insert="~{footer::copy}"></div>
~{templatename::selector} 模板名::选择器
~{templatename::fragmentname} 模板名::片段名
三种引入功能片段的th属性
th:insert 将公共片段整个插入到声明引入的元素中
th:replace 将声明引入的元素替换为公共片段
th:include 将被引入的片段的内容包含进声明引入的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<footer th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</footer>
<!---->
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>
<!---->
<div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
<div>
© 2011 The Good Thymes Virtual Grocery
</div>导航栏+侧边栏
链接高亮——页面块参数
- 抽取公共块到一个文件下
Thymeleaf遍历取出数据+数据格式调整+列表界面
1 | <h2> |
C——新增
跳转到新增页面+表单
1 | <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> |
新增行为
1 |
|
添加数据+日期类型格式转换
1 |
|
提交的数据格式不对 :生日:日期;
日期格式化;SpringMVC将页面提交的值需要转换为指定类型
类型转换,格式化
默认日期是按照 2021/02/28
方式
1 | spring: |
UD有天坑——开启HiddenHttpMethodFilter
对于POST请求,接收 _method
参数,判断URI类型
springBoot装配SpringMVC时,默认关闭请求过滤器,所以需要在配置文件中开启
虽然请求方式仍为POST,但会转发为PUT请求方式
U——修改+Thymeleaf条件判断
- 参数跳转
1 | <td> |
到修改页面,信息回显
1
2
3
4
5
6
7
8
9
10
11
12
public String toupdate({ Integer id,Model model)
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments",departments);
Employee employee = employeeDao.getEmployeeById(id);
model.addAttribute("emp",employee);
//回到新增页面。复用
return "emp/add";
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30<div class="form-group">
<label>LastName</label>
<input name="lastName" type="text" class="form-control" th:value="${emp!=null}?${emp.lastName}:''" placeholder="auspicetian" required/>
</div>
<div class="form-group">
<label>Email</label>
<input name="email" type="text" class="form-control" placeholder="auspicetian@email.com" th:value="${emp!=null}?${emp.email}:''" required/>
</div>
<div class="form-group">
<label>Gender</label><br/>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" checked th:value="1" th:checked="${emp!=null}?${emp.gender}_1">
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" th:value="0" th:checked="${emp!=null}?${emp.gender}_0">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label>Department</label>
<select class="form-control" name="department.id">
<option th:selected="${emp!=null}?${depart.id_emp.id}" th:each="depart:${departments}" th:value="${depart.id}" th:text="${depart.departmentName}"></option>
</select>
</div>
<div class="form-group">
<label>Birth</label>
<input th:value="${emp!=null}?${#dates.format(emp.birth,'yyyy-MM-dd HH-mm-ss')}" name="birth" type="text" class="form-control" placeholder="2021-02-28" required/>
</div>
<button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修改':'添加'"></button>修改请求方式
之前的
PutMapping
1
2
3
4
5<!--发送put请求修改员工数据
1. SpringMVC中配置HiddenHttpMethodFilter(SpringBoot自动配置)
2. 页面创建一个post表单
3. 创建一个input项,name="_method",值就是指定的请求方式
-->1
2
3
4
5
6
7<form method="post" th:action="@{/emps/au}">
<!--发送put请求修改员工数据
1. SpringMVC中配置HiddenHttpMethodFilter(SpringBoot自动配置)
2. 页面创建一个post表单
3. 创建一个input项,name="_method",值就是指定的请求方式
-->
<input type="hidden" name="_method" value="put" th:if="${emp!=null}" />1
2
3
4
5
6
public String update(Employee employee){
System.out.println("修改的员工数据"+employee);
employeeDao.save(employee);
return "redirect:/emps";
}参数为
id
,避免重复添加
D——Thymeleaf自定义属性值
1 |
|
问题:每一个表项都会创建一个表单
Thymeleaf自定义属性值
1 | <img src="../../images/gtvglogo.png" |
1 | <script type="text/javascript"> |
1 |
|
错误处理机制
- 返回一个默认错误页面
- 其他客户端,默认响应一个JSON数据
处理机制
参照
ErrorMvcAutoConfiguration
步骤:
一旦系统出现4XX或者5XX,ErrorPageCustomizer
就会生效(定制错误的响应规则); 发送/error
请求,由 BasicErrorController
处理 /error 请求;响应处理中携带的信息由DefaultErrorAttriute
指定
返回结果_>
响应页面:去哪个页面由
DefaultErrorViewResolver
解析得到,将错误信息(status,model)发送给所有的ErrorViewResolver,得到ModelAndView- 若有 ErrorViewResolver ,使用自定义的
- 若没有ErrorViewResolver,则时候用
DefaultErrorViewResolver
响应JSON数据
涉及到的组件
ErrorPageCustomizer
系统出现错误以后,来到
/error请求
进行处理BasicErrorController
- 浏览器发来的请求用
errorHtml()
方法处理,产生html类型的数据 - 其他客户端发来的数据用
ResponseEntity()
,产生JSON类型的数据
- 浏览器发来的请求用
DefaultErroViewResolver
DefaultErrorAttributes
由errorAattributes指定返回给页面的错误信息
如何定制错误页面?
_1. 有模板引擎_
- 将错误页面命名为
错误状态.html
,放在模板引擎文件文件夹下的error文件夹下 - 发生哪个错误状态码,跳转到哪个页面
error/状态码
可以使用4XX,和5XX作为错误页面的文件名用于匹配这种类型的所有错误
优先寻找精确的状态码.html
页面能获取的信息
- timestamp
- status
- error错误提示
- exception:异常对象
- message:异常消息
- errors:JSR303数据校验错误
Thymeleaf开启接收异常信息
1 | server: |
2. 模板引擎找不到错误页面
静态资源文件夹下
3. 以上都没有
SpringBoot默认的错误提示页面
默认空白页内容:
如何定制错误的JSON数据?
1. @ExceptionHandler(XXX.class)
自定义异常
1 | public class UserNotExistException extends Exception{ |
异常处理器
1 |
|
异常验证
1 |
|
问题:没有自适应效果
- 浏览器,客户端的返回都是JSON
_>如何变成错误请求自适应响应
2. 转发到 /error 请求
由 BasicErrorController
处理
响应的的是默认空白页面,想要的是跳转到自定义的错误页面
原因
状态码,2开头表示请求成功,之前的错误视图都是4开头
响应页面设置状态码
从HttpRequest中获取状态码
传入自己的错误状态码
1 |
|
问题:没有将定制属性携带出去
_3. 携带自定义属性实现自适应响应处理_
原理
出现错误后,请求转发的方式转发到 /error,会被BasicErrorController处理,响应出去可以获取的数据都是通过 getErrorAttributes()
方式添加到返回信息中,而getErrorAttributes()是类AbstractErrorController
中的方法(implements ErrorController)
所以将自定义属性添加到返回信息中,有两种思路
- 完全编写一个ErrorController的实现类【或者编写AbstractErrorController】,放在容器中
- 返回的数据,都是通过getErrorAttributes获取。容器中
DefaultErrorAttributes
进行数据处理
实现
1 |
|
1 |
|
Servlet容器
SpringBoot默认是用嵌入式Servlet容器(Tomcat)
SpringBoot默认是将应用打包为jar包,启动嵌入式的Servlet容器,进而启动SpringBoot的web应用。
1. 如何定制和修改Servet容器的相关配置
- 修改和
server
有关的配置
1 | #通用的Servlet容器配置 |
编写一个EmbeddedServletContainerCustomizer:嵌入式Servlet容器的定制器;用于修改Servlet默认配置
1
2
3
4
5
6
7
8
9
10
11
12
public class MyServerConfig {
public WebServerFactoryCustomizer webServerFactoryCustomizer(){
return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8081);
}
};
}
}
2. 注册三大组件——Servlet、Filter、Listener
ServletRegistrationBean
1 | public class MyServlet extends HttpServlet { |
1 |
|
DispatcherServlet的注册
SpringBoot绑我们自动注册SpringMVC时,自动注册SpringMVC的前端控制器DispatcherServlet
默认拦截: /所有请求,包括静态资源
可以通过server.servletPath来修改SpringMVC前端控制器默认拦截的请求路径
FilterRegistrationBean
1 | public class MyFilter implements Filter { |
1 |
|
Listener
1 | public class MyListener implements ServletContextListener { |
1 |
|
3. 切换嵌入式Servlet容器
jetty
- 长连接应用——聊天
Undertow(不支持JSP)
- 高性能非阻塞,并发性性能好
嵌入式工厂:创建嵌入式容器
选中需要排除的容器 shift+delete
,引入其他Servlet容器
1 | <dependency> |
4. 嵌入式Servlet容器自动配置原理
步骤:
SpringBoot根据依赖的导入情况,给容器中添加相应的
XXXServletWebServerFactory
只要是ServletWebServerFactory创建组件,就会触发后置处理器
WebServerFactoryCustomizerBeanPostProcessor
只要是嵌入式的Servlet容器工厂,后置处理器就工作
后置处理器是从容器中获取所有的
WebServerFactoryCustomizer
,调用定制器的定制方法给工厂添加配置
自动配置原理
ServletWebServerFactoryAutoConfiguration——Servlet容器自动配置类
1 |
|
ServletWebServerFactory
1 | //判断当前是否引入了Tomcat依赖; |
ServletWebServerFactory 作用是返回ServletWebSercer
1 |
|
可见 xxxServletWebServerFactory
主要作用是返回一个 WebServer
换言之,只要实现了
ServletWebServerFactory
的类都可以作为 servlet 容器的工厂类
SpringBoot已经帮我们实现了三个Servlet容器工厂
实现了
WebServer
的类可以作为Servlet容器
SpringBoot已经实现的Servlet容器
eg:EmbeddedTomcatWebServerFactory
1 |
|
配置修改原理
在自动装配Servlet容器时,还注册了一个组件 BeanPostProcessorsRegistrar
BeanPostProcessorsRegistrar
:后置处理器注册器(也是给容器注入一些组件)
1 | public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware { |
webServerFactoryCustomizerBeanPostProcessor
:
1 | public class WebServerFactoryCustomizerBeanPostProcessor { |
而 appplication.yaml中的配置由注解注入
2.xx以下
EmbeddedServletContainerAutoConfiguration
EmbeddedServletContainerFactory(嵌入式Servlet容器工厂)
EmbeddedServletContainer(嵌入式Servlet容器)
移除Tomcat依赖,导入Jetty依赖
- 以嵌入式Tomcat容器工厂为例
对嵌入式容器配置的修改
修改 serverProperties属性
EmbeddedServletContainerCustomizer:定制器帮我们修改了Servlet容器的一些配置(端口号)
怎么修改
- 容器中导入了EmbeddedServletContainerCustomizerBeanPostProcessor
application.yaml中的属性怎么配置上的
5. 嵌入式Servlet容器启动原理
嵌入式容器工厂什么时候创建Servlet容器
什么时候获取嵌入是Servlet容器,并启动Servlet容器
SpringBoot应用启动运行run方法
创建IoC容器对象,并初始化容器(包括创建容器中的每一个组件)
如果是web应用,创建AnnotationConfigEmbeddedWebApplicationContext,否则创建默认的IoC容器AnnotationConfigApplicationContext
refresh(context),刷新刚才创建好的ioc容器
onRefresh(),web的IoC容器重写了onRefresh()方法
webIoC容器会创建嵌入式Servlet容器 createEmbeddedServletContainer()
获取嵌入式 Servlet 容器工厂
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
从IoC容器中获取 EmbeddedServletContainerFactory 组件
TomcatEmbeddedServletContainerFactory 创建对象,后置处理器获取所有定制器,先处理定制ServletContainerFactory的相关配置
使用容器工厂获取嵌入式Servlet容器
this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());
嵌入式Servlet容器创建对象并启动servlet容器
先启动Servlet容器,再将IoC容器中剩下没有创建出来的对象获取出来
IoC容器启动,创建Servlet容器
6. 切换外部Servlet容器
嵌入式Servlet容器:应用打包方式jar
缺点:
- 默认不支持JSP
- 优化定制复杂
- 使用定制器EmbeddedServletContainerCustomizer
- ServerProperties
- 自己编写嵌入式ServletContainerFactory
外置Servlet容器:应用打包方式war
外部Servlet容器启动SpringBoot应用原理
jar:执行SpringBoot主类的main方法,启动IoC容器,创建嵌入式的servlet容器
war:启动服务器,服务器启动SpringBoot应用[
SpringBootServletInitializer
],启动IoC容器
Servlet3规则:Shared Lobraries/runntimes pluggability
- 服务器启动时(web应用启动)会创建当前web应用里面每一个jar包里的ServletContainerInitializer实例
- ServletContainerInitializer实现类的全类名放在jar包的 META/INF/services文件夹下,有一个名为 javax.servlet.ServletContainerInitializer的文件
- 还可以使用 @HandlesTypes,在应用启动的时候加载我们需要的类
流程
启动外部Tomcat,根据Servlet3.0标准:服务器启动时(web应用启动)会创建当前web应用里面每一个jar包里的ServletContainerInitializer实例
ServletContainerInitializer实现类的全类名放在 org\springframework\spring-web\5.3.4\spring-web-5.3.4.jar\META-INF\services\javax.servlet.ServletContainerInitializer
ServletContainerInitializer扫描由@HandlesTypes标注的类,存到 onStartup方法中的
Set<Class<?>>
,并为这些WebApplicationInitializer类型的类创建实例每一个WebApplicationInitializer都调用自己的onStartUp:
相当于我们创建的ServletInitializer(继承了SpringBootServletInitializer的类)会被创建对象,并执行onStartUp方法
会调用createRootApplicationContext方法,创建应用容器
createRootApplicationContext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
//创建SpringApplicationBuilder
SpringApplicationBuilder builder = createSpringApplicationBuilder();
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
builder.contextFactory((webApplicationType) -> new AnnotationConfigServletWebServerApplicationContext());
//调用configure方法,子类重写了这个方法,将SpringBoot主程序传进来
builder = configure(builder);
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
//创建一个Spring应用
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty()
&& MergedAnnotations.from(getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) {
application.addPrimarySources(Collections.singleton(getClass()));
}
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
}
application.setRegisterShutdownHook(false);
//启动Spring应用
return run(application);
}加载@SpringBootApplication主类,启动IoC容器
操作
必须创建一个 war 项目
将嵌入式的Tomcat指定为provided
1
2
3
4
5<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>将当前项目改造为一个web项目
必须编写一个SpringBootServletInitializer的子类,并调用configure方法
1
2
3
4
5
6
7
8public class ServletInitializer extends SpringBootServletInitializer {
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
//传入SpringBoot应用的主程序
return application.sources(Springboot03WarwebApplication.class);
}
}启用服务器即可
配置Tomcat
可以指定前后缀
1 | spring.mvc.view.prefix=/WEB-INF/jsp/ |
数据访问
- JDBC
- MyBatis
- Spring Data JPA
SpringBoot数据访问都是基于Spring Data
JDBC
环境搭建
新建项目
导入依赖
1
2
3
4
5
6
7
8
9<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>配置数据源
1
2
3
4
5
6spring:
datasource:
username: root
password: Aa12345+
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://8.140.130.91:3306/jdbc?characterEncoding=utf8&useSSL=false&serverTimezone=UTC测试连接
1
2
3
4
5
6
7
8
9
10
11
12
13
DataSource dataSource;
void contextLoads() throws SQLException {
//com.zaxxer.hikari.HikariDataSource
System.out.println(dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
默认使用 com.zaxxer.hikari.HikariDataSource
作为数据源
数据源的相关配置在 DataSourceProperties
原理
数据源配置原理
参考
DataSourceConfiguration
,根据配置创建数据源,默认使用Hikari
连接池,可以使用spring.datasource.type
指定默认数据源类型1
2
3
4
5支持的数据源类型
oracle.ucp.jdbc.PoolDataSource
org.apache.commons.dbcp2.BasicDataSource
com.zaxxer.hikari.HikariDataSource
org.apache.tomcat.jdbc.pool.DataSource自定义数据源类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14/**
* Generic DataSource configuration.
*/
static class Generic {
DataSource dataSource(DataSourceProperties properties) {
//使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性
return properties.initializeDataSourceBuilder().build();
}
}
sql语句执行原理
在 DataSourceInitializationConfiguration
中注册了 DataSourceInitializerPostProcessor
1 | DataSourceInitializationConfiguration |
所以SpringBoot在创建连接池后还会运行预定义的SQL脚本文件
如果用户指定脚本路径,则直接去找用户的脚本
如果没有在配置文件中配置脚本的具体位置,就会在classpath下找
schema-all.sql
和schema.sql
,platform获取的是all,platform可以在配置文件中修改
默认只需要将待执行的sql脚本命名为 schema-*.sql、data-*.sql
可以使用
1 | spring: |
指定脚本名
执行sql语句
程序启动后发现表并没有被创建,DEBUG查看以下,发现在运行之前会有一个判断
在配置文件中改为 true
1 | spring: |
操作数据库
自动配置了 JDBCTemplate
操作数据库
1 |
|