01、基础入门-SpringBoot2课程介绍
SpringBoot2核心技术与响应式编程 · 语雀 (yuque.com)
Spring Boot 2核心技术
Spring Boot 2响应式编程
- 学习要求
-熟悉Spring基础
-熟悉Maven使用 - 环境要求
- Java8及以上
- Maven 3.3及以上
- 学习资料
02、基础入门-Spring生态圈
Spring能做什么
Spring的能力
Spring的生态
覆盖了:
- web开发
- 数据访问
- 安全控制
- 分布式
- 消息服务
- 移动开发
- 批处理
- ……
Spring5重大升级
- 响应式编程
- 内部源码设计
基于Java8的一些新特性,如:接口默认实现。重新设计源码架构。
为什么用SpringBoot
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.link
能快速创建出生产级别的Spring应用。
SpringBoot优点
Create stand-alone Spring applications
- 创建独立Spring应用
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
- 内嵌web服务器
Provide opinionated ‘starter’ dependencies to simplify your build configuration
- 自动starter依赖,简化构建配置
Automatically configure Spring and 3rd party libraries whenever possible
- 自动配置Spring以及第三方功能
Provide production-ready features such as metrics, health checks, and externalized configuration
- 提供生产级别的监控、健康检查及外部化配置
Absolutely no code generation and no requirement for XML configuration
- 无代码生成、无需编写XML
SpringBoot是整合Spring技术栈的一站式框架
SpringBoot是简化Spring技术栈的快速开发脚手架
SpringBoot缺点
- 人称版本帝,迭代快,需要时刻关注变化
- 封装太深,内部原理复杂,不容易精通
03、基础入门-SpringBoot的大时代背景
微服务
In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.——James Lewis and Martin Fowler (2014)
- 微服务是一种架构风格
- 一个应用拆分为一组小型服务
- 每个服务运行在自己的进程内,也就是可独立部署和升级
- 服务之间使用轻量级HTTP交互
- 服务围绕业务功能拆分
- 可以由全自动部署机制独立部署
- 去中心化,服务自治。服务可以使用不同的语言、不同的存储技术
分布式
分布式的困难
- 远程调用
- 服务发现
- 负载均衡
- 服务容错
- 配置管理
- 服务监控
- 链路追踪
- 日志管理
- 任务调度
- ……
分布式的解决
- SpringBoot + SpringCloud
云原生
原生应用如何上云。 Cloud Native
上云的困难
- 服务自愈
- 弹性伸缩
- 服务隔离
- 自动化部署
- 灰度发布
- 流量治理
- ……
上云的解决
04、基础入门-SpringBoot官方文档架构
官网文档架构
05、基础入门-SpringBoot-HelloWorld
系统要求
- Java 8
- Maven 3.3+
- IntelliJ IDEA 2019.1.2
Maven配置文件
新添内容:
1 | <mirrors> |
HelloWorld项目
需求:浏览发送/hello请求,响应 “Hello,Spring Boot 2”
创建maven工程
引入依赖
1 | <parent> |
创建主程序
1 | import org.springframework.boot.SpringApplication; |
编写业务
1 | import org.springframework.web.bind.annotation.RequestMapping; |
运行&测试
- 运行
MainApplication
类 - 浏览器输入
http://localhost:8888/hello
,将会输出Hello, Spring Boot 2!
。
设置配置
maven工程的resource文件夹中创建application.properties文件。
1 | # 设置端口号 |
打包部署
在pom.xml添加
1 | <build> |
在IDEA的Maven插件上点击运行 clean 、package,把helloworld工程项目的打包成jar包,
打包好的jar包被生成在helloworld工程项目的target文件夹内。
用cmd运行java -jar boot-01-helloworld-1.0-SNAPSHOT.jar
,既可以运行helloworld工程项目。
将jar包直接在目标服务器执行即可。
出现卡顿–>cmd属性关闭快速编辑模式
06、基础入门-SpringBoot-依赖管理特性
- 父项目做依赖管理
1 | 依赖管理 |
- 开发导入starter场景启动器
- 见到很多 spring-boot-starter-* : *代表就某种场景
- 只要引入starter,这个场景的所有常规需要的依赖我们都会自动引入
- 更多SpringBoot所有支持的场景
- 见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
1 | 所有场景启动器最底层的依赖 |
无需关注版本号,自动版本仲裁
- 引入依赖默认都可以不写版本
- 引入非版本仲裁的jar,要写版本号。
可以修改默认版本号
- 查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。
- 在当前项目里面重写配置,如下面的代码。
1 | <properties> |
IDEA快捷键:
ctrl + shift + alt + U
:以图的方式显示项目中依赖之间的关系。alt + ins
:相当于Eclipse的 Ctrl + N,创建新类,新包等。- ctrl+f查找
- ctrl+R替换
- shift两下查找
07、基础入门-SpringBoot-自动配置特性
- 自动配好Tomcat
- 引入Tomcat依赖。
- 配置Tomcat
1 | <dependency> |
自动配好SpringMVC
- 引入SpringMVC全套组件
- 自动配好SpringMVC常用组件(功能)
自动配好Web常见功能,如:字符编码问题
- SpringBoot帮我们配置好了所有web开发的常见场景
1 | public static void main(String[] args) { |
- 默认的包结构
- 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
- 无需以前的包扫描配置
- 想要改变扫描路径
- @SpringBootApplication(scanBasePackages=”com.lun”)
- @ComponentScan 指定扫描路径
1 |
|
各种配置拥有默认值
- 默认配置最终都是映射到某个类上,如:
MultipartProperties
- 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
- 默认配置最终都是映射到某个类上,如:
按需加载所有自动配置项
- 非常多的starter
- 引入了哪些场景这个场景的自动配置才会开启
- SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面……
08、底层注解-@Configuration详解
- 基本使用
- Full模式与Lite模式
- 示例
1 | /** |
@Configuration测试代码如下:
1 |
|
- 最佳实战
- 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
- 配置 类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式(默认)
lite 英 [laɪt] 美 [laɪt]
adj. 低热量的,清淡的(light的一种拼写方法);类似…的劣质品
IDEA快捷键:
Alt + Ins
:生成getter,setter、构造器等代码。Ctrl + Alt + B
:查看类的具体实现代码。
09、底层注解-@Import导入组件
@Bean、@Component、@Controller、@Service、@Repository,它们是Spring的基本标签,在Spring Boot中并未改变它们原来的功能。
@ComponentScan 在07、基础入门-SpringBoot-自动配置特性有用例。
@Import({User.class, DBHelper.class})给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
1 |
|
测试类:
1 |
|
10、底层注解-@Conditional条件装配
条件装配:满足Conditional指定的条件,则进行组件注入
用@ConditionalOnMissingBean举例说明
1 |
|
11、底层注解-@ImportResource导入Spring配置文件
比如,公司使用bean.xml文件生成配置bean,然而你为了省事,想继续复用bean.xml,@ImportResource粉墨登场。
bean.xml:
1 |
|
使用方法:
1 |
|
测试类:
1 | public static void main(String[] args) { |
12、底层注解-@ConfigurationProperties配置绑定
如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用
传统方法:
1 | public class getProperties { |
Spring Boot一种配置配置绑定:
@ConfigurationProperties + @Component
假设有配置文件application.properties
1 | mycar.brand=BYD |
只有在容器中的组件,才会拥有SpringBoot提供的强大功能
1 |
|
Spring Boot另一种配置配置绑定:
@EnableConfigurationProperties + @ConfigurationProperties
- 开启Car配置绑定功能
- 把这个Car这个组件自动注册到容器中
1 |
|
1 |
|
13、自动配置【源码分析】-自动包规则原理
Spring Boot应用的启动类:
1 |
|
分析下@SpringBootApplication
1 |
|
重点分析@SpringBootConfiguration
,@EnableAutoConfiguration
,@ComponentScan
。
@SpringBootConfiguration
1 |
|
@Configuration
代表当前是一个配置类。
@ComponentScan
指定扫描哪些Spring注解。
@ComponentScan 在07、基础入门-SpringBoot-自动配置特性有用例。
@EnableAutoConfiguration
1 |
|
重点分析@AutoConfigurationPackage
,@Import(AutoConfigurationImportSelector.class)
。
@AutoConfigurationPackage
标签名直译为:自动配置包,指定了默认的包规则。
1 |
|
- 利用Registrar给容器中导入一系列组件
- 得到主启动类所在包名,并将该指定的一个包下的所有组件导入进MainApplication所在包下。
14、自动配置【源码分析】-初始加载自动配置类
@Import(AutoConfigurationImportSelector.class)
- 在AutoConfigurationImportSelector类中利用
getAutoConfigurationEntry(annotationMetadata);
给容器中批量导入一些组件 - 调用
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)
获取到所有需要导入到容器中的配置类 - 利用工厂加载
Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);
得到所有的组件 - 从
META-INF/spring.factories
位置来加载一个文件。- 默认扫描我们当前系统里面所有
META-INF/spring.factories
位置的文件 spring-boot-autoconfigure-2.3.4.RELEASE.jar
包里面也有META-INF/spring.factories
- 默认扫描我们当前系统里面所有
1 | # 文件里面写死了spring-boot一启动就要给容器中加载的所有配置类 |
虽然我们127个场景的所有自动配置启动的时候默认全部加载,但是xxxxAutoConfiguration
按照条件装配规则(@Conditional
),最终会按需配置。
如AopAutoConfiguration
类:
1 |
|
15、自动配置【源码分析】-自动配置流程
以DispatcherServletAutoConfiguration
的内部类DispatcherServletConfiguration
为例子:
1 |
|
SpringBoot默认会在底层配好所有的组件,但是如果用户自己配置了以用户的优先。
总结:
- SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。(xxxxProperties里面读取,xxxProperties和配置文件进行了绑定)
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。
xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 —-> application.properties
16、最佳实践-SpringBoot应用如何编写
- 引入场景依赖
- 查看自动配置了哪些(选做)
- 自己分析,引入场景对应的自动配置一般都生效了
- 配置文件中debug=true开启自动配置报告。
- Negative(不生效)
- Positive(生效)
- 是否需要修改
- 参照文档修改配置项
- 官方文档
- 自己分析。xxxxProperties绑定了配置文件的哪些。
- 自定义加入或者替换组件
- @Bean、@Component…
- 自定义器 XXXXXCustomizer;
- ……
- 参照文档修改配置项
17、最佳实践-Lombok简化开发
Lombok用标签方式代替构造器、getter/setter、toString()等鸡肋代码。
spring boot已经管理Lombok。引入依赖:
1 | <dependency> |
IDEA中File->Settings->Plugins,搜索安装Lombok插件。
1 |
|
简化日志开发
1 |
|
18、最佳实践-dev-tools
Spring Boot includes an additional set of tools that can make the application development experience a little more pleasant. The
spring-boot-devtools
module can be included in any project to provide additional development-time features.——linkApplications that use
spring-boot-devtools
automatically restart whenever files on the classpath change. This can be a useful feature when working in an IDE, as it gives a very fast feedback loop for code changes. By default, any entry on the classpath that points to a directory is monitored for changes. Note that certain resources, such as static assets and view templates, do not need to restart the application.——linkTriggering a restart
As DevTools monitors classpath resources, the only way to trigger a restart is to update the classpath. The way in which you cause the classpath to be updated depends on the IDE that you are using:
- In Eclipse, saving a modified file causes the classpath to be updated and triggers a restart.
- In IntelliJ IDEA, building the project (
Build -> Build Project
)(shortcut: Ctrl+F9) has the same effect.
添加依赖:
1 | <dependencies> |
在IDEA中,项目或者页面修改以后:Ctrl+F9。
19、最佳实践-Spring Initailizr
Spring Initailizr是创建Spring Boot工程向导。
在IDEA中,菜单栏New -> Project -> Spring Initailizr。
已经帮助我们引入了依赖,创建好了项目结构
20、配置文件-yaml的用法
同以前的properties用法
YAML 是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:”Yet Another Markup Language”(仍是一种标记语言)。
非常适合用来做以数据为中心的配置文件。
基本语法
- key: value;kv之间有空格
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- ‘#’表示注释
- 字符串无需加引号,如果要加,单引号’’、双引号””表示字符串内容会被 转义、不转义
数据类型
- 字面量:单个的、不可再分的值。date、boolean、string、number、null
1 | k: v |
- 对象:键值对的集合。map、hash、set、object
1 | #行内写法: |
- 数组:一组按次序排列的值。array、list、queue
1 | #行内写法: |
实例
1 |
|
用yaml表示以上对象
1 | person: |
21、配置文件-自定义类绑定的配置提示
You can easily generate your own configuration metadata file from items annotated with
@ConfigurationProperties
by using thespring-boot-configuration-processor
jar. The jar includes a Java annotation processor which is invoked as your project is compiled.——link
自定义的类和配置文件绑定一般没有提示。若要提示,添加如下依赖:
1 | <dependency> |
22、web场景-web开发简介
springMVC自动配置概览
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)
The auto-configuration adds the following features on top of Spring’s defaults:
Inclusion of
ContentNegotiatingViewResolver
andBeanNameViewResolver
beans.- 内容协商视图解析器和BeanName视图解析器
Support for serving static resources, including support for WebJars (covered later in this document)).
- 静态资源(包括webjars)
Automatic registration of
Converter
,GenericConverter
, andFormatter
beans.- 自动注册
Converter,GenericConverter,Formatter
- 自动注册
Support for
HttpMessageConverters
(covered later in this document).- 支持
HttpMessageConverters
(后来我们配合内容协商理解原理)
- 支持
Automatic registration of
MessageCodesResolver
(covered later in this document).- 自动注册
MessageCodesResolver
(国际化用)
- 自动注册
Static
index.html
support.- 静态index.html 页支持
Custom
Favicon
support (covered later in this document).- 自定义
Favicon
- 自定义
Automatic use of a
ConfigurableWebBindingInitializer
bean (covered later in this document).- 自动使用
ConfigurableWebBindingInitializer
,(DataBinder负责将请求数据绑定到JavaBean上)
- 自动使用
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 typeWebMvcConfigurer
but without@EnableWebMvc
.不用@EnableWebMvc注解。使用
@Configuration
+WebMvcConfigurer
自定义规则
If you want to provide custom instances of
RequestMappingHandlerMapping
,RequestMappingHandlerAdapter
, orExceptionHandlerExceptionResolver
, and still keep the Spring Boot MVC customizations, you can declare a bean of typeWebMvcRegistrations
and use it to provide custom instances of those components.声明
WebMvcRegistrations
改变默认底层组件
If you want to take complete control of Spring MVC, you can add your own
@Configuration
annotated with@EnableWebMvc
, or alternatively add your own@Configuration
-annotatedDelegatingWebMvcConfiguration
as described in the Javadoc of@EnableWebMvc
.使用
@EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC
23、web场景-静态资源规则与定制化
静态资源目录
只要静态资源放在类路径下: called /static
(or /public
or /resources
or /META-INF/resources
访问 : 当前项目根路径/ + 静态资源名
原理: 静态映射/**。
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面。
也可以改变默认的静态资源路径,/static
,/public
,/resources
, /META-INF/resources
失效
1 | resources: |
静态资源访问前缀
1 | spring: |
当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找
webjar
可用jar方式添加css,js等资源文件,
例如,添加jquery
1 | <dependency> |
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径。
24、web场景-welcome与favicon功能
欢迎页支持
静态资源路径下 index.html。
- 可以配置静态资源路径
- 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
1 | spring: |
- controller能处理/index。
自定义Favicon
指网页标签上的小图标。
favicon.ico 放在静态资源目录下即可。
1 | spring: |
25、web场景-【源码分析】-静态资源原理
- SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)
- SpringMVC功能的自动配置类
WebMvcAutoConfiguration
,生效
1 |
|
- 给容器中配置的内容:
- 配置文件的相关属性的绑定:WebMvcProperties==spring.mvc、ResourceProperties==spring.resources
1 |
|
配置类只有一个有参构造器
1 | ////有参构造器所有参数的值都会从容器中确定 |
- ResourceProperties resourceProperties;获取和spring.resources绑定的所有的值的对象
- WebMvcProperties mvcProperties 获取和spring.mvc绑定的所有的值的对象
- ListableBeanFactory beanFactory Spring的beanFactory
- HttpMessageConverters 找到所有的HttpMessageConverters
- ResourceHandlerRegistrationCustomizer 找到 资源处理器的自定义器。
- DispatcherServletPath
- ServletRegistrationBean 给应用注册Servlet、Filter….
资源处理的默认规则
1 | ... |
根据上述代码,我们可以同过配置禁止所有静态资源规则。
1 | spring: |
静态资源规则:
1 |
|
欢迎页的处理规则
1 | ... |
WelcomePageHandlerMapping
的构造方法如下:
1 | WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, |
这构造方法内的代码也解释了web场景-welcome与favicon功能中配置static-path-pattern
了,welcome页面和小图标失效的问题。
26、请求处理-【源码分析】-Rest映射及源码解析
请求映射
@xxxMapping;
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
- 以前:
- /getUser 获取用户
- /deleteUser 删除用户
- /editUser 修改用户
- /saveUser保存用户
- 现在: /user
- GET-获取用户
- DELETE-删除用户
- PUT-修改用户
- POST-保存用户
- 核心Filter;HiddenHttpMethodFilter
- 以前:
用法
- 开启页面表单的Rest功能
- 页面 form的属性method=post,隐藏域 _method=put、delete等(如果直接get或post,无需隐藏域)
- 编写请求映射
1 | spring: |
1 | <form action="/user" method="get"> |
1 |
|
- Rest原理(表单提交要使用REST的时候)
- 表单提交会带上
\_method=PUT
- 请求过来被
HiddenHttpMethodFilter
拦截- 请求是否正常,并且是POST
- 获取到
\_method
的值。 - 兼容以下请求;PUT.DELETE.PATCH
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
- 获取到
- 请求是否正常,并且是POST
- 表单提交会带上
1 | public class HiddenHttpMethodFilter extends OncePerRequestFilter { |
- Rest使用客户端工具。
- 如PostMan可直接发送put、delete等方式请求。
27、请求处理-【源码分析】-怎么改变默认的_method
1 |
|
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
意味着在没有HiddenHttpMethodFilter
时,才执行hiddenHttpMethodFilter()
。因此,我们可以自定义filter,改变默认的\_method
。例如:
1 |
|
将\_method
改成_m
。
1 | <form action="/user" method="post"> |
28、请求处理-【源码分析】-请求映射原理
SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet
-> doDispatch()
1 | protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { |
getHandler()
方法如下:
1 |
|
this.handlerMappings
在Debug模式下展现的内容:
其中,RequestMappingHandlerMapping保存了所有@RequestMapping
和handler
的映射规则。
所有的请求映射都在HandlerMapping中:
SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
- 如果有就找到这个请求对应的handler
- 如果没有就是下一个 HandlerMapping
我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping
IDEA快捷键:
- Ctrl + Alt + U : 以UML的类图展现类有哪些继承类,派生类以及实现哪些接口。
- Crtl + Alt + Shift + U : 同上,区别在于上条快捷键结果在新页展现,而本条快捷键结果在弹窗展现。
- Ctrl + H : 以树形方式展现类层次结构图。
29、请求处理-常用参数注解使用
注解:
@PathVariable
路径变量@RequestHeader
获取请求头@RequestParam
获取请求参数(指问号后的参数,url?a=1&b=2)@CookieValue
获取Cookie值@RequestAttribute
获取request域属性@RequestBody
获取请求体[POST]@MatrixVariable
矩阵变量@ModelAttribute
使用用例:
1 |
|
30、请求处理-@RequestAttribute
用例:
1 |
|
31、请求处理-@MatrixVariable与UrlPathHelper
语法: 请求路径:
/cars/sell;low=34;brand=byd,audi,yd
SpringBoot默认是禁用了矩阵变量的功能
- 手动开启:原理。对于路径的处理。UrlPathHelper的removeSemicolonContent设置为false,让其支持矩阵变量的。
矩阵变量必须有url路径变量才能被解析
手动开启矩阵变量:
- 实现
WebMvcConfigurer
接口:
1 |
|
- 创建返回
WebMvcConfigurer
Bean:
1 |
|
@MatrixVariable
的用例
1 |
|
32、请求处理-【源码分析】-各种类型参数解析原理
这要从DispatcherServlet
开始说起:
1 | public class DispatcherServlet extends FrameworkServlet { |
HandlerMapping
中找到能处理请求的Handler
(Controller.method())。- 为当前Handler 找一个适配器
HandlerAdapter
,用的最多的是RequestMappingHandlerAdapter。 - 适配器执行目标方法并确定方法参数的每一个值。
HandlerAdapter
默认会加载所有HandlerAdapter
1 | public class DispatcherServlet extends FrameworkServlet { |
有这些HandlerAdapter
:
支持方法上标注
@RequestMapping
支持函数式编程的
…
…
执行目标方法
1 | public class DispatcherServlet extends FrameworkServlet { |
HandlerAdapter
接口实现类RequestMappingHandlerAdapter
(主要用来处理@RequestMapping
)
1 | public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter |
参数解析器
确定将要执行的目标方法的每一个参数的值是什么;
SpringMVC目标方法能写多少种参数类型。取决于参数解析器argumentResolvers。
1 |
|
this.argumentResolvers
在afterPropertiesSet()
方法内初始化
1 | public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter |
HandlerMethodArgumentResolverComposite
类如下:(众多参数解析器argumentResolvers的包装类)。
1 | public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { |
我们看看HandlerMethodArgumentResolver
的源码:
1 | public interface HandlerMethodArgumentResolver { |
返回值处理器
ValueHandler
1 |
|
this.returnValueHandlers
在afterPropertiesSet()
方法内初始化
1 | public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter |
HandlerMethodReturnValueHandlerComposite
类如下:
1 | public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler { |
HandlerMethodReturnValueHandler
接口:
1 | public interface HandlerMethodReturnValueHandler { |
回顾执行目标方法
1 | public class DispatcherServlet extends FrameworkServlet { |
RequestMappingHandlerAdapter
的handle()
方法:
1 | public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter |
RequestMappingHandlerAdapter
的invokeHandlerMethod()
方法:
1 | public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter |
invokeAndHandle()
方法如下:
1 | public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { |
如何确定目标方法每一个参数的值
重点分析ServletInvocableHandlerMethod
的getMethodArgumentValues
方法
1 | public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { |
this.resolvers
的类型为HandlerMethodArgumentResolverComposite
(在参数解析器章节提及)
1 | public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { |
小结
本节描述,一个请求发送到DispatcherServlet后的具体处理流程,也就是SpringMVC的主要原理。
本节内容较多且硬核,对日后编程很有帮助,需耐心对待。
可以运行一个示例,打断点,在Debug模式下,查看程序流程。
33、请求处理-【源码分析】-Servlet API参数解析原理
- WebRequest
- ServletRequest
- MultipartRequest
- HttpSession
- javax.servlet.http.PushBuilder
- Principal
- InputStream
- Reader
- HttpMethod
- Locale
- TimeZone
- ZoneId
ServletRequestMethodArgumentResolver用来处理以上的参数
1 | public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver { |
用例:
1 |
|
34、请求处理-【源码分析】-Model、Map原理
复杂参数:
Map
Model(map、model里面的数据会被放在request的请求域 request.setAttribute)
Errors/BindingResult
RedirectAttributes( 重定向携带数据)
ServletResponse(response)
SessionStatus
UriComponentsBuilder
ServletUriComponentsBuilder
用例:
1 |
|
Map<String,Object> map
Model model
HttpServletRequest request
上面三位都是可以给request域中放数据,用request.getAttribute()
获取
接下来我们看看,Map<String,Object> map
与Model model
用什么参数处理器。
Map<String,Object> map
参数用MapMethodProcessor
处理:
1 | public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler { |
mavContainer.getModel()
如下:
1 | public class ModelAndViewContainer { |
Model model
用ModelMethodProcessor
处理:
1 | public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler { |
return mavContainer.getModel();
这跟MapMethodProcessor
的一致
Model
也是另一种意义的Map
。
接下来看看Map<String,Object> map
与Model model
值是如何做到用request.getAttribute()
获取的。
众所周知,所有的数据都放在 ModelAndView包含要去的页面地址View,还包含Model数据。
先看ModelAndView接下来是如何处理的?
1 | public class DispatcherServlet extends FrameworkServlet { |
在Debug模式下,view
属为InternalResourceView
类。
1 | public class InternalResourceView extends AbstractUrlBasedView { |
exposeModelAsRequestAttributes
方法看出,Map<String,Object> map
,Model model
这两种类型数据可以给request域中放数据,用request.getAttribute()
获取。
35、请求处理-【源码分析】-自定义参数绑定原理
1 |
|
1 | /** |
封装过程用到ServletModelAttributeMethodProcessor
1 | public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor { |
WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
在过程当中,用到GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型
36、请求处理-【源码分析】-自定义Converter原理
未来我们可以给WebDataBinder里面放自己的Converter;
下面演示将字符串“啊猫,3”
转换成Pet
对象。
1 | //1、WebMvcConfigurer定制化SpringMVC的功能 |
37、响应处理-【源码分析】-ReturnValueHandler原理
假设给前端自动返回json数据,需要引入相关的依赖
1 | <dependency> |
控制层代码如下:
1 |
|
32、请求处理-【源码分析】-各种类型参数解析原理 - 返回值处理器有讨论ReturnValueHandler。现在直接看看重点:
1 | public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter |
1 | public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { |
1 | public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler { |
@ResponseBody
注解,即RequestResponseBodyMethodProcessor
,它实现HandlerMethodReturnValueHandler
接口
1 | public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { |
38、响应处理-【源码分析】-HTTPMessageConverter原理
返回值处理器ReturnValueHandler
原理:
- 返回值处理器判断是否支持这种类型返回值
supportsReturnType
- 返回值处理器调用
handleReturnValue
进行处理 RequestResponseBodyMethodProcessor
可以处理返回值标了@ResponseBody
注解的。- 利用
MessageConverters
进行处理 将数据写为json- 内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
- 服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
- SpringMVC会挨个遍历所有容器底层的
HttpMessageConverter
,看谁能处理?- 得到
MappingJackson2HttpMessageConverter
可以将对象写为json - 利用
MappingJackson2HttpMessageConverter
将对象转为json再写出去。
- 得到
- 利用
1 |
|
HTTPMessageConverter
接口:
1 | /** |
HttpMessageConverter
: 看是否支持将 此 Class
类型的对象,转为MediaType
类型的数据。
例子:Person
对象转为JSON,或者 JSON转为Person
,这将用到MappingJackson2HttpMessageConverter
1 | public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter { |
关于MappingJackson2HttpMessageConverter
的实例化请看下节。
关于HttpMessageConverters的初始化
DispatcherServlet
的初始化时会调用initHandlerAdapters(ApplicationContext context)
1 | public class DispatcherServlet extends FrameworkServlet { |
上述代码会加载ApplicationContext
的所有HandlerAdapter
,用来处理@RequestMapping
的RequestMappingHandlerAdapter
实现HandlerAdapter
接口,RequestMappingHandlerAdapter
也被实例化。
1 | public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter |
在构造器中看到一堆HttpMessageConverter
。接着,重点查看AllEncompassingFormHttpMessageConverter
类:
1 | public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConverter { |
在AllEncompassingFormHttpMessageConverter
类构造器看到MappingJackson2HttpMessageConverter
类的实例化,AllEncompassingFormHttpMessageConverter
包含MappingJackson2HttpMessageConverter
。
ReturnValueHandler
是怎么与MappingJackson2HttpMessageConverter
关联起来?请看下节。
ReturnValueHandler与MappingJackson2HttpMessageConverter关联
再次回顾RequestMappingHandlerAdapter
1 | public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter |
应用中WebMvcAutoConfiguration
(底层是WebMvcConfigurationSupport
实现)传入更多messageConverters
,其中就包含MappingJackson2HttpMessageConverter
。
39、响应处理-【源码分析】-内容协商原理
根据客户端接收能力不同,返回不同媒体类型的数据。
引入XML依赖:
1 | <dependency> |
可用Postman软件分别测试返回json和xml:只需要改变请求头中Accept字段(application/json、application/xml)。
Http协议中规定的,Accept字段告诉服务器本客户端可以接收的数据类型。
内容协商原理:
- 判断当前响应头中是否已经有确定的媒体类型
MediaType
。 - 获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段application/xml)(这一步在下一节有详细介绍)
contentNegotiationManager
内容协商管理器 默认使用基于请求头的策略HeaderContentNegotiationStrategy
确定客户端可以接收的内容类型
- 遍历循环所有当前系统的
MessageConverter
,看谁支持操作这个对象(Person) - 找到支持操作Person的converter,把converter支持的媒体类型统计出来。
- 客户端需要application/xml,服务端有10种MediaType。
- 进行内容协商的最佳匹配媒体类型
- 用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。
1 |
|
40、响应处理-【源码分析】-基于请求参数的内容协商原理
上一节内容协商原理的第二步:
获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段application/xml)
contentNegotiationManager
内容协商管理器 默认使用基于请求头的策略HeaderContentNegotiationStrategy
确定客户端可以接收的内容类型
1 | //RequestResponseBodyMethodProcessor继承这类 |
1 | public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver { |
1 | //基于请求头的策略 |
开启浏览器参数方式内容协商功能
为了方便内容协商,开启基于请求参数的内容协商功能。
1 | spring: |
内容协商管理器,就会多了一个ParameterContentNegotiationStrategy
(由Spring容器注入)
1 | public class ParameterContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy { |
然后,浏览器地址输入带format参数的URL:
1 | http://localhost:8080/test/person?format=json |
这样,后端会根据参数format的值,返回对应json或xml格式的数据。
41、响应处理-【源码分析】-自定义MessageConverter
实现多协议数据兼容。json、xml、x-guigu(这个是自创的)
@ResponseBody
响应数据出去 调用RequestResponseBodyMethodProcessor
处理Processor 处理方法返回值。通过
MessageConverter
处理所有
MessageConverter
合起来可以支持各种媒体类型数据的操作(读、写)内容协商找到最终的
messageConverter
SpringMVC的什么功能,一个入口给容器中添加一个 WebMvcConfigurer
1 |
|
1 |
|
1 | import java.util.Date; |
用Postman发送/test/person
(请求头Accept:application/x-guigu
),将返回自定义协议数据的写出。
42、响应处理-【源码分析】-浏览器与PostMan内容协商完全适配
假设你想基于自定义请求参数的自定义内容协商功能。
换句话,在地址栏输入http://localhost:8080/test/person?format=gg
返回数据,跟http://localhost:8080/test/person
且请求头参数Accept:application/x-guigu
的返回自定义协议数据的一致。
1 |
|
日后开发要注意,有可能我们添加的自定义的功能会覆盖默认很多功能,导致一些默认的功能失效。
43、视图解析-Thymeleaf初体验
Thymeleaf is a modern server-side Java template engine for both web and standalone environments.
Thymeleaf’s main goal is to bring elegant natural templates to your development workflow — HTML that can be correctly displayed in browsers and also work as static prototypes, allowing for stronger collaboration in development teams.
With modules for Spring Framework, a host of integrations with your favourite tools, and the ability to plug in your own functionality, Thymeleaf is ideal for modern-day HTML5 JVM web development — although there is much more it can do.——Link
thymeleaf使用
引入Starter
1 | <dependency> |
自动配置好了thymeleaf
1 |
|
自动配好的策略
所有thymeleaf的配置值都在 ThymeleafProperties
配置好了 SpringTemplateEngine
配好了 ThymeleafViewResolver
我们只需要直接开发页面
1 | public static final String DEFAULT_PREFIX = "classpath:/templates/";//模板放置处 |
编写一个控制层:
1 |
|
/templates/success.html
:
1 |
|
1 | server: |
这个设置后,URL要插入/app
, 如http://localhost:8080/app/hello.html
。
基本语法
表达式
表达式名字 | 语法 | 用途 |
---|---|---|
变量取值 | ${…} | 获取请求域、session域、对象等值 |
选择变量 | *{…} | 获取上下文对象值 |
消息 | #{…} | 获取国际化等值 |
链接 | @{…} | 生成链接 |
片段表达式 | ~{…} | jsp:include 作用,引入公共页面片段 |
字面量
- 文本值: ‘one text’ , ‘Another one!’ ,…
- 数字: 0 , 34 , 3.0 , 12.3 ,…
- 布尔值: true , false
- 空值: null
- 变量: one,two,…. 变量不能有空格
文本操作
- 字符串拼接: +
- 变量替换: |The name is ${name}|
数学运算
- 运算符: + , - , * , / , %
布尔运算
- 运算符: and , or
- 一元运算: ! , not
比较运算
- 比较: > , <** **,** **>= , <= ( gt , lt , ge , le )
- 等式: == , != ( eq , ne )
条件运算
- If-then: (if) ? (then)
- If-then-else: (if) ? (then) : (else)
- Default: (value) ?: (defaultvalue)
特殊操作
- 无操作: _
设置属性值-th:attr
- 设置单个值
1 | <form action="subscribe.html" th:attr="action=@{/subscribe}"> |
- 设置多个值
1 | <img src="../../images/gtvglogo.png" |
官方文档 - 5 Setting Attribute Values
迭代
1 | <tr th:each="prod : ${prods}"> |
1 | <tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'"> |
条件运算
1 | <a href="comments.html" |
1 | <div th:switch="${user.role}"> |
属性优先级
Order | Feature | Attributes |
---|---|---|
1 | Fragment inclusion | th:insert th:replace |
2 | Fragment iteration | th:each |
3 | Conditional evaluation | th:if th:unless th:switch th:case |
4 | Local variable definition | th:object th:with |
5 | General attribute modification | th:attr th:attrprepend th:attrappend |
6 | Specific attribute modification | th:value th:href th:src ... |
7 | Text (tag body modification) | th:text th:utext |
8 | Fragment specification | th:fragment |
9 | Fragment removal | th:remove |
官方文档 - 10 Attribute Precedence
44、web实验-后台管理系统基本功能
项目创建
使用IDEA的Spring Initializr。
- thymeleaf、
- web-starter、
- devtools、
- lombok
登陆页面
/static
放置 css,js等静态资源/templates/login.html
登录页
1 | <html lang="en" xmlns:th="http://www.thymeleaf.org"><!-- 要加这玩意thymeleaf才能用 --> |
/templates/main.html
主页
thymeleaf内联写法:
1 | <p>Hello, [[${session.user.name}]]!</p> |
登录控制层
1 |
|
模型
1 |
|