当前位置: 首页 > news >正文

做片头片尾比较好的网站柳市做公司网站

做片头片尾比较好的网站,柳市做公司网站,教育app定制开发,公众号注册Spring Boot 2 学习笔记#xff08;2 / 2#xff09; ---01、基础入门-SpringBoot2课程介绍02、基础入门-Spring生态圈03、基础入门-SpringBoot的大时代背景04、基础入门-SpringBoot官方文档架构05、基础入门-SpringBoot-HelloWorld06、基础入门-SpringBoot-依赖管理特性07、…Spring Boot 2 学习笔记2 / 2 ---01、基础入门-SpringBoot2课程介绍02、基础入门-Spring生态圈03、基础入门-SpringBoot的大时代背景04、基础入门-SpringBoot官方文档架构05、基础入门-SpringBoot-HelloWorld06、基础入门-SpringBoot-依赖管理特性07、基础入门-SpringBoot-自动配置特性08、底层注解-Configuration详解09、底层注解-Import导入组件10、底层注解-Conditional条件装配11、底层注解-ImportResource导入Spring配置文件12、底层注解-ConfigurationProperties配置绑定13、自动配置【源码分析】-自动包规则原理14、自动配置【源码分析】-初始加载自动配置类15、自动配置【源码分析】-自动配置流程16、最佳实践-SpringBoot应用如何编写17、最佳实践-Lombok简化开发18、最佳实践-dev-tools19、最佳实践-Spring Initailizr20、配置文件-yaml的用法21、配置文件-自定义类绑定的配置提示22、web场景-web开发简介23、web场景-静态资源规则与定制化24、web场景-welcome与favicon功能25、web场景-【源码分析】-静态资源原理26、请求处理-【源码分析】-Rest映射及源码解析27、请求处理-【源码分析】-怎么改变默认的_method28、请求处理-【源码分析】-请求映射原理29、请求处理-常用参数注解使用30、请求处理-RequestAttribute31、请求处理-MatrixVariable与UrlPathHelper32、请求处理-【源码分析】-各种类型参数解析原理33、请求处理-【源码分析】-Servlet API参数解析原理34、请求处理-【源码分析】-Model、Map原理35、请求处理-【源码分析】-自定义参数绑定原理36、请求处理-【源码分析】-自定义Converter原理37、响应处理-【源码分析】-ReturnValueHandler原理38、响应处理-【源码分析】-HTTPMessageConverter原理39、响应处理-【源码分析】-内容协商原理40、响应处理-【源码分析】-基于请求参数的内容协商原理41、响应处理-【源码分析】-自定义MessageConverter42、响应处理-【源码分析】-浏览器与PostMan内容协商完全适配43、视图解析-Thymeleaf初体验44、web实验-后台管理系统基本功能- 01、基础入门-SpringBoot2课程介绍 Spring Boot 2核心技术 Spring Boot 2响应式编程 学习要求 -熟悉Spring基础 -熟悉Maven使用环境要求 Java8及以上Maven 3.3及以上 学习资料 Spring Boot官网Spring Boot官方文档本课程文档地址视频地址1、视频地址2源码地址 02、基础入门-Spring生态圈 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官方文档架构 Spring Boot官网Spring Boot官方文档 官网文档架构 查看版本新特性 05、基础入门-SpringBoot-HelloWorld 系统要求 Java 8Maven 3.3IntelliJ IDEA 2019.1.2 Maven配置文件 新添内容 mirrorsmirroridnexus-aliyun/idmirrorOfcentral/mirrorOfnameNexus aliyun/nameurlhttp://maven.aliyun.com/nexus/content/groups/public/url/mirror /mirrorsprofilesprofileidjdk-1.8/idactivationactiveByDefaulttrue/activeByDefaultjdk1.8/jdk/activationpropertiesmaven.compiler.source1.8/maven.compiler.sourcemaven.compiler.target1.8/maven.compiler.targetmaven.compiler.compilerVersion1.8/maven.compiler.compilerVersion/properties/profile /profilesHelloWorld项目 需求浏览发送/hello请求响应 “HelloSpring Boot 2” 创建maven工程 引入依赖 parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.4.RELEASE/version /parentdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency /dependencies创建主程序 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication public class MainApplication {public static void main(String[] args) {SpringApplication.run(MainApplication.class, args);} } 编写业务 import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;RestController public class HelloController {RequestMapping(/hello)public String handle01(){return Hello, Spring Boot 2!;} }运行测试 运行MainApplication类浏览器输入http://localhost:8888/hello将会输出Hello, Spring Boot 2!。 设置配置 maven工程的resource文件夹中创建application.properties文件。 # 设置端口号 server.port8888更多配置信息 打包部署 在pom.xml添加 buildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins /build在IDEA的Maven插件上点击运行 clean 、package把helloworld工程项目的打包成jar包 打包好的jar包被生成在helloworld工程项目的target文件夹内。 用cmd运行java -jar boot-01-helloworld-1.0-SNAPSHOT.jar既可以运行helloworld工程项目。 将jar包直接在目标服务器执行即可。 06、基础入门-SpringBoot-依赖管理特性 父项目做依赖管理 依赖管理 parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.4.RELEASE/version /parent上面项目的父项目如下 parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-dependencies/artifactIdversion2.3.4.RELEASE/version /parent它几乎声明了所有开发中常用的依赖的版本号自动版本仲裁机制开发导入starter场景启动器 见到很多 spring-boot-starter-* *就某种场景只要引入starter这个场景的所有常规需要的依赖我们都自动引入更多SpringBoot所有支持的场景见到的 *-spring-boot-starter 第三方为我们提供的简化开发的场景启动器。 所有场景启动器最底层的依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactIdversion2.3.4.RELEASE/versionscopecompile/scope /dependency无需关注版本号自动版本仲裁 引入依赖默认都可以不写版本引入非版本仲裁的jar要写版本号。 可以修改默认版本号 查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。在当前项目里面重写配置如下面的代码。 propertiesmysql.version5.1.43/mysql.version /propertiesIDEA快捷键 ctrl shift alt U以图的方式显示项目中依赖之间的关系。alt ins相当于Eclipse的 Ctrl N创建新类新包等。 07、基础入门-SpringBoot-自动配置特性 自动配好Tomcat 引入Tomcat依赖。配置Tomcat dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-tomcat/artifactIdversion2.3.4.RELEASE/versionscopecompile/scope /dependency自动配好SpringMVC 引入SpringMVC全套组件自动配好SpringMVC常用组件功能 自动配好Web常见功能如字符编码问题 SpringBoot帮我们配置好了所有web开发的常见场景 public static void main(String[] args) {//1、返回我们IOC容器ConfigurableApplicationContext run SpringApplication.run(MainApplication.class, args);//2、查看容器里面的组件String[] names run.getBeanDefinitionNames();for (String name : names) {System.out.println(name);} }默认的包结构 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来无需以前的包扫描配置想要改变扫描路径 SpringBootApplication(scanBasePackages“com.lun”)ComponentScan 指定扫描路径 SpringBootApplication 等同于 SpringBootConfiguration EnableAutoConfiguration ComponentScan(com.lun)各种配置拥有默认值 默认配置最终都是映射到某个类上如MultipartProperties配置文件的值最终会绑定每个类上这个类会在容器中创建对象 按需加载所有自动配置项 非常多的starter引入了哪些场景这个场景的自动配置才会开启SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面 … 08、底层注解-Configuration详解 基本使用 Full模式与Lite模式示例 /*** 1、配置类里面使用Bean标注在方法上给容器注册组件默认也是单实例的* 2、配置类本身也是组件* 3、proxyBeanMethods代理bean的方法* Full(proxyBeanMethods true)保证每个Bean方法被调用多少次返回的组件都是单实例的默认* Lite(proxyBeanMethods false)每个Bean方法被调用多少次返回的组件都是新创建的*/ Configuration(proxyBeanMethods false) //告诉SpringBoot这是一个配置类 配置文件 public class MyConfig {/*** Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象* return*/Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值就是组件在容器中的实例public User user01(){User zhangsan new User(zhangsan, 18);//user组件依赖了Pet组件zhangsan.setPet(tomcatPet());return zhangsan;}Bean(tom)public Pet tomcatPet(){return new Pet(tomcat);} }Configuration测试代码如下: SpringBootConfiguration EnableAutoConfiguration ComponentScan(com.atguigu.boot) public class MainApplication {public static void main(String[] args) {//1、返回我们IOC容器ConfigurableApplicationContext run SpringApplication.run(MainApplication.class, args);//2、查看容器里面的组件String[] names run.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}//3、从容器中获取组件Pet tom01 run.getBean(tom, Pet.class);Pet tom02 run.getBean(tom, Pet.class);System.out.println(组件(tom01 tom02));//4、com.atguigu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$51f1e1ca1654a892MyConfig bean run.getBean(MyConfig.class);System.out.println(bean);//如果Configuration(proxyBeanMethods true)代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。//保持组件单实例User user bean.user01();User user1 bean.user01();System.out.println(user user1);User user01 run.getBean(user01, User.class);Pet tom run.getBean(tom, Pet.class);System.out.println(用户的宠物(user01.getPet() tom));} }最佳实战 配置 类组件之间无依赖关系用Lite模式加速容器启动过程减少判断配置 类组件之间有依赖关系方法会被调用得到之前单实例组件用Full模式默认 lite 英 [laɪt] 美 [laɪt] adj. 低热量的清淡的(light的一种拼写方法);类似…的劣质品 IDEA快捷键 Alt Ins:生成gettersetter、构造器等代码。Ctrl Alt B:查看类的具体实现代码。 09、底层注解-Import导入组件 Bean、Component、Controller、Service、Repository它们是Spring的基本标签在Spring Boot中并未改变它们原来的功能。 ComponentScan 在07、基础入门-SpringBoot-自动配置特性有用例。 Import({User.class, DBHelper.class})给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名 Import({User.class, DBHelper.class}) Configuration(proxyBeanMethods false) //告诉SpringBoot这是一个配置类 配置文件 public class MyConfig { }测试类 //1、返回我们IOC容器 ConfigurableApplicationContext run SpringApplication.run(MainApplication.class, args);//...//5、获取组件 String[] beanNamesForType run.getBeanNamesForType(User.class);for (String s : beanNamesForType) {System.out.println(s); }DBHelper bean1 run.getBean(DBHelper.class); System.out.println(bean1);10、底层注解-Conditional条件装配 条件装配满足Conditional指定的条件则进行组件注入 用ConditionalOnMissingBean举例说明 Configuration(proxyBeanMethods false) ConditionalOnMissingBean(name tom)//没有tom名字的Bean时MyConfig类的Bean才能生效。 public class MyConfig {Beanpublic User user01(){User zhangsan new User(zhangsan, 18);zhangsan.setPet(tomcatPet());return zhangsan;}Bean(tom22)public Pet tomcatPet(){return new Pet(tomcat);} }public static void main(String[] args) {//1、返回我们IOC容器ConfigurableApplicationContext run SpringApplication.run(MainApplication.class, args);//2、查看容器里面的组件String[] names run.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}boolean tom run.containsBean(tom);System.out.println(容器中Tom组件tom);//falseboolean user01 run.containsBean(user01);System.out.println(容器中user01组件user01);//trueboolean tom22 run.containsBean(tom22);System.out.println(容器中tom22组件tom22);//true}11、底层注解-ImportResource导入Spring配置文件 比如公司使用bean.xml文件生成配置bean然而你为了省事想继续复用bean.xmlImportResource粉墨登场。 bean.xml ?xml version1.0 encodingUTF-8? beans ...bean idhaha classcom.lun.boot.bean.Userproperty namename valuezhangsan/propertyproperty nameage value18/property/beanbean idhehe classcom.lun.boot.bean.Petproperty namename valuetomcat/property/bean /beans使用方法 ImportResource(classpath:beans.xml) public class MyConfig { ... }测试类 public static void main(String[] args) {//1、返回我们IOC容器ConfigurableApplicationContext run SpringApplication.run(MainApplication.class, args);boolean haha run.containsBean(haha);boolean hehe run.containsBean(hehe);System.out.println(hahahaha);//trueSystem.out.println(hehehehe);//true }12、底层注解-ConfigurationProperties配置绑定 如何使用Java读取到properties文件中的内容并且把它封装到JavaBean中以供随时使用 传统方法 public class getProperties {public static void main(String[] args) throws FileNotFoundException, IOException {Properties pps new Properties();pps.load(new FileInputStream(a.properties));Enumeration enum1 pps.propertyNames();//得到配置文件的名字while(enum1.hasMoreElements()) {String strKey (String) enum1.nextElement();String strValue pps.getProperty(strKey);System.out.println(strKey strValue);//封装到JavaBean。}}}Spring Boot一种配置配置绑定 ConfigurationProperties Component 假设有配置文件application.properties mycar.brandBYD mycar.price100000只有在容器中的组件才会拥有SpringBoot提供的强大功能 Component ConfigurationProperties(prefix mycar) public class Car { ... }Spring Boot另一种配置配置绑定 EnableConfigurationProperties ConfigurationProperties 开启Car配置绑定功能把这个Car这个组件自动注册到容器中 EnableConfigurationProperties(Car.class) public class MyConfig { ... }ConfigurationProperties(prefix mycar) public class Car { ... }13、自动配置【源码分析】-自动包规则原理 Spring Boot应用的启动类 SpringBootApplication public class MainApplication {public static void main(String[] args) {SpringApplication.run(MainApplication.class, args);}}分析下SpringBootApplication Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Inherited SpringBootConfiguration EnableAutoConfiguration ComponentScan(excludeFilters {Filter(type FilterType.CUSTOM,classes {TypeExcludeFilter.class} ), Filter(type FilterType.CUSTOM,classes {AutoConfigurationExcludeFilter.class} )} ) public interface SpringBootApplication {... }重点分析SpringBootConfigurationEnableAutoConfigurationComponentScan。 SpringBootConfiguration Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Configuration public interface SpringBootConfiguration {AliasFor(annotation Configuration.class)boolean proxyBeanMethods() default true; }Configuration代表当前是一个配置类。 ComponentScan 指定扫描哪些Spring注解。 ComponentScan 在07、基础入门-SpringBoot-自动配置特性有用例。 EnableAutoConfiguration Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Inherited AutoConfigurationPackage Import(AutoConfigurationImportSelector.class) public interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY spring.boot.enableautoconfiguration;Class?[] exclude() default {};String[] excludeName() default {}; }重点分析AutoConfigurationPackageImport(AutoConfigurationImportSelector.class)。 AutoConfigurationPackage 标签名直译为自动配置包指定了默认的包规则。 Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Inherited Import(AutoConfigurationPackages.Registrar.class)//给容器中导入一个组件 public interface AutoConfigurationPackage {String[] basePackages() default {};Class?[] basePackageClasses() default {}; }利用Registrar给容器中导入一系列组件将指定的一个包下的所有组件导入进MainApplication所在包下。 14、自动配置【源码分析】-初始加载自动配置类 Import(AutoConfigurationImportSelector.class) 利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件调用ListString configurations getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类利用工厂加载 MapString, ListString loadSpringFactories(Nullable ClassLoader classLoader);得到所有的组件从META-INF/spring.factories位置来加载一个文件。 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories # 文件里面写死了spring-boot一启动就要给容器中加载的所有配置类 # spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ ...虽然我们127个场景的所有自动配置启动的时候默认全部加载但是xxxxAutoConfiguration按照条件装配规则Conditional最终会按需配置。 如AopAutoConfiguration类 Configuration(proxyBeanMethods false ) ConditionalOnProperty(prefix spring.aop,name auto,havingValue true,matchIfMissing true ) public class AopAutoConfiguration {public AopAutoConfiguration() {}... }15、自动配置【源码分析】-自动配置流程 以DispatcherServletAutoConfiguration的内部类DispatcherServletConfiguration为例子: Bean ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件 ConditionalOnMissingBean(name DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件 public MultipartResolver multipartResolver(MultipartResolver resolver) {//给Bean标注的方法传入了对象参数这个参数的值就会从容器中找。//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;//给容器中加入了文件上传解析器 }SpringBoot默认会在底层配好所有的组件但是如果用户自己配置了以用户的优先。 总结 SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration每个自动配置类按照条件进行生效默认都会绑定配置文件指定的值。xxxxProperties里面读取xxxProperties和配置文件进行了绑定生效的配置类就会给容器中装配很多组件只要容器中有这些组件相当于这些功能就有了定制化配置 用户直接自己Bean替换底层的组件用户去看这个组件是获取的配置文件什么值就去修改。 xxxxxAutoConfiguration — 组件 — xxxxProperties里面拿值 ---- application.properties 16、最佳实践-SpringBoot应用如何编写 引入场景依赖 官方文档 查看自动配置了哪些选做 自己分析引入场景对应的自动配置一般都生效了配置文件中debugtrue开启自动配置报告。 Negative不生效Positive生效 是否需要修改 参照文档修改配置项 官方文档自己分析。xxxxProperties绑定了配置文件的哪些。 自定义加入或者替换组件 Bean、Component… 自定义器 XXXXXCustomizer… 17、最佳实践-Lombok简化开发 Lombok用标签方式代替构造器、getter/setter、toString()等鸡肋代码。 spring boot已经管理Lombok。引入依赖 dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId /dependencyIDEA中File-Settings-Plugins搜索安装Lombok插件。 NoArgsConstructor //AllArgsConstructor Data ToString EqualsAndHashCode public class User {private String name;private Integer age;private Pet pet;public User(String name,Integer age){this.name name;this.age age;} }简化日志开发 Slf4j RestController public class HelloController {RequestMapping(/hello)public String handle01(RequestParam(name) String name){log.info(请求进来了....);return Hello, Spring Boot 2!你好name;} }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.——link Applications 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.——link Triggering 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: CtrlF9) has the same effect. 添加依赖 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-devtools/artifactIdoptionaltrue/optional/dependency /dependencies在IDEA中项目或者页面修改以后CtrlF9。 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: valuekv之间有空格大小写敏感使用缩进表示层级关系缩进不允许使用tab只允许空格缩进的空格数不重要只要相同层级的元素左对齐即可#表示注释字符串无需加引号如果要加单引号’、双引号表示字符串内容会被 转义、不转义 数据类型 字面量单个的、不可再分的值。date、boolean、string、number、null k: v对象键值对的集合。map、hash、set、object #行内写法 k: {k1:v1,k2:v2,k3:v3}#或k: k1: v1k2: v2k3: v3数组一组按次序排列的值。array、list、queue #行内写法 k: [v1,v2,v3]#或者k:- v1- v2- v3实例 Data public class Person {private String userName;private Boolean boss;private Date birth;private Integer age;private Pet pet;private String[] interests;private ListString animal;private MapString, Object score;private SetDouble salarys;private MapString, ListPet allPets; }Data public class Pet {private String name;private Double weight; }用yaml表示以上对象 person:userName: zhangsanboss: falsebirth: 2019/12/12 20:12:33age: 18pet: name: tomcatweight: 23.4interests: [篮球,游泳]animal: - jerry- marioscore:english: first: 30second: 40third: 50math: [131,140,148]chinese: {first: 128,second: 136}salarys: [3999,4999.98,5999.99]allPets:sick:- {name: tom}- {name: jerry,weight: 47}health: [{name: mario,weight: 47}]21、配置文件-自定义类绑定的配置提示 You can easily generate your own configuration metadata file from items annotated with ConfigurationProperties by using the spring-boot-configuration-processor jar. The jar includes a Java annotation processor which is invoked as your project is compiled.——link 自定义的类和配置文件绑定一般没有提示。若要提示添加如下依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-configuration-processor/artifactIdoptionaltrue/optional /dependency!-- 下面插件作用是工程打包时不将spring-boot-configuration-processor打进包内让其只在编码的时候有用 -- buildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdconfigurationexcludesexcludegroupIdorg.springframework.boot/groupIdartifactIdspring-boot-configuration-processor/artifactId/exclude/excludes/configuration/plugin/plugins /build22、web场景-web开发简介 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 and BeanNameViewResolver beans. 内容协商视图解析器和BeanName视图解析器 Support for serving static resources, including support for WebJars (covered later in this document)). 静态资源包括webjars Automatic registration of Converter, GenericConverter, and Formatter beans. 自动注册 ConverterGenericConverterFormatter 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 type WebMvcConfigurer but without EnableWebMvc. 不用EnableWebMvc注解。使用 Configuration WebMvcConfigurer 自定义规则 If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations 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-annotated DelegatingWebMvcConfiguration as described in the Javadoc of EnableWebMvc. 使用 EnableWebMvcConfigurationDelegatingWebMvcConfiguration 全面接管SpringMVC 23、web场景-静态资源规则与定制化 静态资源目录 只要静态资源放在类路径下 called /static (or /public or /resources or /META-INF/resources 访问 当前项目根路径/ 静态资源名 原理 静态映射/**。 请求进来先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面。 也可以改变默认的静态资源路径/static/public,/resources, /META-INF/resources失效 resources:static-locations: [classpath:/haha/]静态资源访问前缀 spring:mvc:static-path-pattern: /res/**当前项目 static-path-pattern 静态资源名 静态资源文件夹下找 webjar 可用jar方式添加cssjs等资源文件 https://www.webjars.org/ 例如添加jquery dependencygroupIdorg.webjars/groupIdartifactIdjquery/artifactIdversion3.5.1/version /dependency访问地址http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径。 24、web场景-welcome与favicon功能 官方文档 欢迎页支持 静态资源路径下 index.html。 可以配置静态资源路径但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问 spring: # mvc: # static-path-pattern: /res/** 这个会导致welcome page功能失效resources:static-locations: [classpath:/haha/]controller能处理/index。 自定义Favicon 指网页标签上的小图标。 favicon.ico 放在静态资源目录下即可。 spring: # mvc: # static-path-pattern: /res/** 这个会导致 Favicon 功能失效25、web场景-【源码分析】-静态资源原理 SpringBoot启动默认加载 xxxAutoConfiguration 类自动配置类SpringMVC功能的自动配置类WebMvcAutoConfiguration生效 Configuration(proxyBeanMethods false) ConditionalOnWebApplication(type Type.SERVLET) ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) ConditionalOnMissingBean(WebMvcConfigurationSupport.class) AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE 10) AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration {... }给容器中配置的内容 配置文件的相关属性的绑定WebMvcPropertiesspring.mvc、ResourcePropertiesspring.resources Configuration(proxyBeanMethods false) Import(EnableWebMvcConfiguration.class) EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) Order(0) public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {... }配置类只有一个有参构造器 有参构造器所有参数的值都会从容器中确定 public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties,ListableBeanFactory beanFactory, ObjectProviderHttpMessageConverters messageConvertersProvider,ObjectProviderResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizerProvider,ObjectProviderDispatcherServletPath dispatcherServletPath,ObjectProviderServletRegistrationBean? servletRegistrations) {this.mvcProperties mvcProperties;this.beanFactory beanFactory;this.messageConvertersProvider messageConvertersProvider;this.resourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizerProvider.getIfAvailable();this.dispatcherServletPath dispatcherServletPath;this.servletRegistrations servletRegistrations;this.mvcProperties.checkConfiguration(); }ResourceProperties resourceProperties获取和spring.resources绑定的所有的值的对象WebMvcProperties mvcProperties 获取和spring.mvc绑定的所有的值的对象ListableBeanFactory beanFactory Spring的beanFactoryHttpMessageConverters 找到所有的HttpMessageConvertersResourceHandlerRegistrationCustomizer 找到 资源处理器的自定义器。DispatcherServletPathServletRegistrationBean 给应用注册Servlet、Filter… 资源处理的默认规则 ... public class WebMvcAutoConfiguration {...public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {...Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {super.addResourceHandlers(registry);if (!this.resourceProperties.isAddMappings()) {logger.debug(Default resource handling disabled);return;}ServletContext servletContext getServletContext();addResourceHandler(registry, /webjars/**, classpath:/META-INF/resources/webjars/);addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) - {registration.addResourceLocations(this.resourceProperties.getStaticLocations());if (servletContext ! null) {registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));}});}...}... }根据上述代码我们可以同过配置禁止所有静态资源规则。 spring:resources:add-mappings: false #禁用所有静态资源规则静态资源规则 ConfigurationProperties(prefix spring.resources, ignoreUnknownFields false) public class ResourceProperties {private static final String[] CLASSPATH_RESOURCE_LOCATIONS { classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/ };/*** Locations of static resources. Defaults to classpath:[/META-INF/resources/,* /resources/, /static/, /public/].*/private String[] staticLocations CLASSPATH_RESOURCE_LOCATIONS;... } 欢迎页的处理规则 ... public class WebMvcAutoConfiguration {...public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {...Beanpublic WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {WelcomePageHandlerMapping welcomePageHandlerMapping new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),this.mvcProperties.getStaticPathPattern());welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());return welcomePageHandlerMapping;} WelcomePageHandlerMapping的构造方法如下 WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {if (welcomePage ! null /**.equals(staticPathPattern)) {//要用欢迎页功能必须是/**logger.info(Adding welcome page: welcomePage);setRootViewName(forward:index.html);}else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {//调用Controller /indexlogger.info(Adding welcome page template: index);setRootViewName(index);} }这构造方法内的代码也解释了web场景-welcome与favicon功能中配置static-path-pattern了welcome页面和小图标失效的问题。 26、请求处理-【源码分析】-Rest映射及源码解析 请求映射 xxxMapping; GetMappingPostMappingPutMappingDeleteMapping Rest风格支持使用HTTP请求方式动词来表示对资源的操作 以前 /getUser 获取用户/deleteUser 删除用户/editUser 修改用户/saveUser保存用户 现在 /user GET-获取用户DELETE-删除用户PUT-修改用户POST-保存用户 核心FilterHiddenHttpMethodFilter 用法 开启页面表单的Rest功能页面 form的属性methodpost隐藏域 _methodput、delete等如果直接get或post无需隐藏域编写请求映射 spring:mvc:hiddenmethod:filter:enabled: true #开启页面表单的Rest功能form action/user methodgetinput valueREST-GET提交 typesubmit / /formform action/user methodpostinput valueREST-POST提交 typesubmit / /formform action/user methodpostinput name_method typehidden valueDELETE/input valueREST-DELETE 提交 typesubmit/ /formform action/user methodpostinput name_method typehidden valuePUT /input valueREST-PUT提交typesubmit / formGetMapping(/user) //RequestMapping(value /user,method RequestMethod.GET) public String getUser(){return GET-张三; }PostMapping(/user) //RequestMapping(value /user,method RequestMethod.POST) public String saveUser(){return POST-张三; }PutMapping(/user) //RequestMapping(value /user,method RequestMethod.PUT) public String putUser(){return PUT-张三; }DeleteMapping(/user) //RequestMapping(value /user,method RequestMethod.DELETE) public String deleteUser(){return DELETE-张三; }Rest原理表单提交要使用REST的时候 表单提交会带上\_methodPUT请求过来被HiddenHttpMethodFilter拦截 请求是否正常并且是POST 获取到\_method的值。兼容以下请求PUT.DELETE.PATCH原生requestpost包装模式requesWrapper重写了getMethod方法返回的是传入的值。过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。 public class HiddenHttpMethodFilter extends OncePerRequestFilter {private static final ListString ALLOWED_METHODS Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));/** Default method parameter: {code _method}. */public static final String DEFAULT_METHOD_PARAM _method;private String methodParam DEFAULT_METHOD_PARAM;/*** Set the parameter name to look for HTTP methods.* see #DEFAULT_METHOD_PARAM*/public void setMethodParam(String methodParam) {Assert.hasText(methodParam, methodParam must not be empty);this.methodParam methodParam;}Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {HttpServletRequest requestToUse request;if (POST.equals(request.getMethod()) request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) null) {String paramValue request.getParameter(this.methodParam);if (StringUtils.hasLength(paramValue)) {String method paramValue.toUpperCase(Locale.ENGLISH);if (ALLOWED_METHODS.contains(method)) {requestToUse new HttpMethodRequestWrapper(request, method);}}}filterChain.doFilter(requestToUse, response);}/*** Simple {link HttpServletRequest} wrapper that returns the supplied method for* {link HttpServletRequest#getMethod()}.*/private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {private final String method;public HttpMethodRequestWrapper(HttpServletRequest request, String method) {super(request);this.method method;}Overridepublic String getMethod() {return this.method;}}}Rest使用客户端工具。 如PostMan可直接发送put、delete等方式请求。 27、请求处理-【源码分析】-怎么改变默认的_method Configuration(proxyBeanMethods false) ConditionalOnWebApplication(type Type.SERVLET) ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) ConditionalOnMissingBean(WebMvcConfigurationSupport.class) AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE 10) AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration {...BeanConditionalOnMissingBean(HiddenHttpMethodFilter.class)ConditionalOnProperty(prefix spring.mvc.hiddenmethod.filter, name enabled, matchIfMissing false)public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();}... } ConditionalOnMissingBean(HiddenHttpMethodFilter.class)意味着在没有HiddenHttpMethodFilter时才执行hiddenHttpMethodFilter()。因此我们可以自定义filter改变默认的\_method。例如 Configuration(proxyBeanMethods false) public class WebConfig{//自定义filterBeanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter new HiddenHttpMethodFilter();methodFilter.setMethodParam(_m);return methodFilter;} }将\_method改成_m。 form action/user methodpostinput name_m typehidden valueDELETE/input valueREST-DELETE 提交 typesubmit/ /form28、请求处理-【源码分析】-请求映射原理 SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet - doDispatch() protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest request;HandlerExecutionChain mappedHandler null;boolean multipartRequestParsed false;WebAsyncManager asyncManager WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv null;Exception dispatchException null;try {processedRequest checkMultipart(request);multipartRequestParsed (processedRequest ! request);// 找到当前请求使用哪个HandlerController的方法处理mappedHandler getHandler(processedRequest);//HandlerMapping处理器映射。/xxx-xxxx... }getHandler()方法如下 Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings ! null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler mapping.getHandler(request);if (handler ! null) {return handler;}}}return null; }this.handlerMappings在Debug模式下展现的内容 其中保存了所有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?a1b2CookieValue 获取Cookie值RequestAttribute 获取request域属性RequestBody 获取请求体[POST]MatrixVariable 矩阵变量ModelAttribute 使用用例 RestController public class ParameterTestController {// car/2/owner/zhangsanGetMapping(/car/{id}/owner/{username})public MapString,Object getCar(PathVariable(id) Integer id,PathVariable(username) String name,PathVariable MapString,String pv,RequestHeader(User-Agent) String userAgent,RequestHeader MapString,String header,RequestParam(age) Integer age,RequestParam(inters) ListString inters,RequestParam MapString,String params,CookieValue(_ga) String _ga,CookieValue(_ga) Cookie cookie){MapString,Object map new HashMap();// map.put(id,id); // map.put(name,name); // map.put(pv,pv); // map.put(userAgent,userAgent); // map.put(headers,header);map.put(age,age);map.put(inters,inters);map.put(params,params);map.put(_ga,_ga);System.out.println(cookie.getName()cookie.getValue());return map;}PostMapping(/save)public Map postMethod(RequestBody String content){MapString,Object map new HashMap();map.put(content,content);return map;} }30、请求处理-RequestAttribute 用例 Controller public class RequestController {GetMapping(/goto)public String goToPage(HttpServletRequest request){request.setAttribute(msg,成功了...);request.setAttribute(code,200);return forward:/success; //转发到 /success请求}GetMapping(/params)public String testParam(MapString,Object map,Model model,HttpServletRequest request,HttpServletResponse response){map.put(hello,world666);model.addAttribute(world,hello666);request.setAttribute(message,HelloWorld);Cookie cookie new Cookie(c1,v1);response.addCookie(cookie);return forward:/success;}///-----------------主角RequestAttribute在这个方法ResponseBodyGetMapping(/success)public Map success(RequestAttribute(value msg,required false) String msg,RequestAttribute(value code,required false)Integer code,HttpServletRequest request){Object msg1 request.getAttribute(msg);MapString,Object map new HashMap();Object hello request.getAttribute(hello);Object world request.getAttribute(world);Object message request.getAttribute(message);map.put(reqMethod_msg,msg1);map.put(annotation_msg,msg);map.put(hello,hello);map.put(world,world);map.put(message,message);return map;} }31、请求处理-MatrixVariable与UrlPathHelper 语法 请求路径/cars/sell;low34;brandbyd,audi,yd SpringBoot默认是禁用了矩阵变量的功能 手动开启原理。对于路径的处理。UrlPathHelper的removeSemicolonContent设置为false让其支持矩阵变量的。 矩阵变量必须有url路径变量才能被解析 手动开启矩阵变量 实现WebMvcConfigurer接口 Configuration(proxyBeanMethods false) public class WebConfig implements WebMvcConfigurer {Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper new UrlPathHelper();// 不移除后面的内容。矩阵变量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);} }创建返回WebMvcConfigurerBean Configuration(proxyBeanMethods false) public class WebConfig{Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper new UrlPathHelper();// 不移除后面的内容。矩阵变量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}}} }MatrixVariable的用例 RestController public class ParameterTestController {///cars/sell;low34;brandbyd,audi,ydGetMapping(/cars/{path})public Map carsSell(MatrixVariable(low) Integer low,MatrixVariable(brand) ListString brand,PathVariable(path) String path){MapString,Object map new HashMap();map.put(low,low);map.put(brand,brand);map.put(path,path);return map;}// /boss/1;age20/2;age10GetMapping(/boss/{bossId}/{empId})public Map boss(MatrixVariable(value age,pathVar bossId) Integer bossAge,MatrixVariable(value age,pathVar empId) Integer empAge){MapString,Object map new HashMap();map.put(bossAge,bossAge);map.put(empAge,empAge);return map;}}32、请求处理-【源码分析】-各种类型参数解析原理 这要从DispatcherServlet开始说起 public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest request;HandlerExecutionChain mappedHandler null;boolean multipartRequestParsed false;WebAsyncManager asyncManager WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv null;Exception dispatchException null;try {processedRequest checkMultipart(request);multipartRequestParsed (processedRequest ! request);// Determine handler for the current request.mappedHandler getHandler(processedRequest);if (mappedHandler null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.HandlerAdapter ha getHandlerAdapter(mappedHandler.getHandler());...HandlerMapping中找到能处理请求的HandlerController.method()。为当前Handler 找一个适配器 HandlerAdapter用的最多的是RequestMappingHandlerAdapter。适配器执行目标方法并确定方法参数的每一个值。 HandlerAdapter 默认会加载所有HandlerAdapter public class DispatcherServlet extends FrameworkServlet {/** Detect all HandlerAdapters or just expect handlerAdapter bean?. */private boolean detectAllHandlerAdapters true;...private void initHandlerAdapters(ApplicationContext context) {this.handlerAdapters null;if (this.detectAllHandlerAdapters) {// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.MapString, HandlerAdapter matchingBeans BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerAdapters new ArrayList(matchingBeans.values());// We keep HandlerAdapters in sorted order.AnnotationAwareOrderComparator.sort(this.handlerAdapters);}}...有这些HandlerAdapter 支持方法上标注RequestMapping 支持函数式编程的 … … 执行目标方法 public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {ModelAndView mv null;...// Determine handler for the current request.mappedHandler getHandler(processedRequest);if (mappedHandler null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.HandlerAdapter ha getHandlerAdapter(mappedHandler.getHandler());...//本节重点// Actually invoke the handler.mv ha.handle(processedRequest, response, mappedHandler.getHandler());HandlerAdapter接口实现类RequestMappingHandlerAdapter主要用来处理RequestMapping public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {...//AbstractHandlerMethodAdapter类的方法RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapterpublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);}Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;//handleInternal的核心mav invokeHandlerMethod(request, response, handlerMethod);//解释看下节//...return mav;} }参数解析器 确定将要执行的目标方法的每一个参数的值是什么; SpringMVC目标方法能写多少种参数类型。取决于参数解析器argumentResolvers。 Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory getDataBinderFactory(handlerMethod);ModelFactory modelFactory getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers ! null) {//-----关注点invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}...this.argumentResolvers在afterPropertiesSet()方法内初始化 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {Nullableprivate HandlerMethodArgumentResolverComposite argumentResolvers;Overridepublic void afterPropertiesSet() {...if (this.argumentResolvers null) {//初始化argumentResolversListHandlerMethodArgumentResolver resolvers getDefaultArgumentResolvers();this.argumentResolvers new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}...}//初始化了一堆的实现HandlerMethodArgumentResolver接口的private ListHandlerMethodArgumentResolver getDefaultArgumentResolvers() {ListHandlerMethodArgumentResolver resolvers new ArrayList(30);// Annotation-based argument resolutionresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));resolvers.add(new RequestHeaderMapMethodArgumentResolver());resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());// Type-based argument resolutionresolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RedirectAttributesMethodArgumentResolver());resolvers.add(new ModelMethodProcessor());resolvers.add(new MapMethodProcessor());resolvers.add(new ErrorsMethodArgumentResolver());resolvers.add(new SessionStatusMethodArgumentResolver());resolvers.add(new UriComponentsBuilderMethodArgumentResolver());if (KotlinDetector.isKotlinPresent()) {resolvers.add(new ContinuationHandlerMethodArgumentResolver());}// Custom argumentsif (getCustomArgumentResolvers() ! null) {resolvers.addAll(getCustomArgumentResolvers());}// Catch-allresolvers.add(new PrincipalMethodArgumentResolver());resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));resolvers.add(new ServletModelAttributeMethodProcessor(true));return resolvers;}}HandlerMethodArgumentResolverComposite类如下众多参数解析器argumentResolvers的包装类。 public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {private final ListHandlerMethodArgumentResolver argumentResolvers new ArrayList();...public HandlerMethodArgumentResolverComposite addResolvers(Nullable HandlerMethodArgumentResolver... resolvers) {if (resolvers ! null) {Collections.addAll(this.argumentResolvers, resolvers);}return this;}... }我们看看HandlerMethodArgumentResolver的源码 public interface HandlerMethodArgumentResolver {//当前解析器是否支持解析这种参数boolean supportsParameter(MethodParameter parameter);Nullable//如果支持就调用 resolveArgumentObject resolveArgument(MethodParameter parameter, Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, Nullable WebDataBinderFactory binderFactory) throws Exception;} 返回值处理器 ValueHandler Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory getDataBinderFactory(handlerMethod);ModelFactory modelFactory getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers ! null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers ! null) {//---关注点invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}... this.returnValueHandlers在afterPropertiesSet()方法内初始化 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {Nullableprivate HandlerMethodReturnValueHandlerComposite returnValueHandlers;Overridepublic void afterPropertiesSet() {...if (this.returnValueHandlers null) {ListHandlerMethodReturnValueHandler handlers getDefaultReturnValueHandlers();this.returnValueHandlers new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}//初始化了一堆的实现HandlerMethodReturnValueHandler接口的private ListHandlerMethodReturnValueHandler getDefaultReturnValueHandlers() {ListHandlerMethodReturnValueHandler handlers new ArrayList(20);// Single-purpose return value typeshandlers.add(new ModelAndViewMethodReturnValueHandler());handlers.add(new ModelMethodProcessor());handlers.add(new ViewMethodReturnValueHandler());handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));handlers.add(new StreamingResponseBodyReturnValueHandler());handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));handlers.add(new HttpHeadersReturnValueHandler());handlers.add(new CallableMethodReturnValueHandler());handlers.add(new DeferredResultMethodReturnValueHandler());handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));// Annotation-based return value typeshandlers.add(new ServletModelAttributeMethodProcessor(false));handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));// Multi-purpose return value typeshandlers.add(new ViewNameMethodReturnValueHandler());handlers.add(new MapMethodProcessor());// Custom return value typesif (getCustomReturnValueHandlers() ! null) {handlers.addAll(getCustomReturnValueHandlers());}// Catch-allif (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));}else {handlers.add(new ServletModelAttributeMethodProcessor(true));}return handlers;} }HandlerMethodReturnValueHandlerComposite类如下 public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {private final ListHandlerMethodReturnValueHandler returnValueHandlers new ArrayList();...public HandlerMethodReturnValueHandlerComposite addHandlers(Nullable List? extends HandlerMethodReturnValueHandler handlers) {if (handlers ! null) {this.returnValueHandlers.addAll(handlers);}return this;}}HandlerMethodReturnValueHandler接口 public interface HandlerMethodReturnValueHandler {boolean supportsReturnType(MethodParameter returnType);void handleReturnValue(Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;}回顾执行目标方法 public class DispatcherServlet extends FrameworkServlet {...protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {ModelAndView mv null;...mv ha.handle(processedRequest, response, mappedHandler.getHandler());RequestMappingHandlerAdapter的handle()方法 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {...//AbstractHandlerMethodAdapter类的方法RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapterpublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);}Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;//handleInternal的核心mav invokeHandlerMethod(request, response, handlerMethod);//解释看下节//...return mav;} }RequestMappingHandlerAdapter的invokeHandlerMethod()方法 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest new ServletWebRequest(request, response);try {...ServletInvocableHandlerMethod invocableMethod createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers ! null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers ! null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}...//关注点执行目标方法invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}}invokeAndHandle()方法如下 public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {Object returnValue invokeForRequest(webRequest, mavContainer, providedArgs);...try {//returnValue存储起来this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {...}}Nullable//InvocableHandlerMethod类的ServletInvocableHandlerMethod类继承InvocableHandlerMethod类public Object invokeForRequest(NativeWebRequest request, Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {获取方法的参数值Object[] args getMethodArgumentValues(request, mavContainer, providedArgs);...return doInvoke(args);}Nullableprotected Object doInvoke(Object... args) throws Exception {Method method getBridgedMethod();//RequestMapping的方法ReflectionUtils.makeAccessible(method);try {if (KotlinDetector.isSuspendingFunction(method)) {return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);}//通过反射调用return method.invoke(getBean(), args);//getBean()指RequestMapping的方法所在类的对象。}catch (IllegalArgumentException ex) {...}catch (InvocationTargetException ex) {...}}} 如何确定目标方法每一个参数的值 重点分析ServletInvocableHandlerMethod的getMethodArgumentValues方法 public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {...Nullable//InvocableHandlerMethod类的ServletInvocableHandlerMethod类继承InvocableHandlerMethod类public Object invokeForRequest(NativeWebRequest request, Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {获取方法的参数值Object[] args getMethodArgumentValues(request, mavContainer, providedArgs);...return doInvoke(args);}//本节重点获取方法的参数值protected Object[] getMethodArgumentValues(NativeWebRequest request, Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {MethodParameter[] parameters getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}Object[] args new Object[parameters.length];for (int i 0; i parameters.length; i) {MethodParameter parameter parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] findProvidedArgument(parameter, providedArgs);if (args[i] ! null) {continue;}//查看resolvers是否有支持if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, No suitable resolver));}try {//支持的话就开始解析吧args[i] this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}catch (Exception ex) {....}}return args;}}this.resolvers的类型为HandlerMethodArgumentResolverComposite在参数解析器章节提及 public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {Overridepublic boolean supportsParameter(MethodParameter parameter) {return getArgumentResolver(parameter) ! null;}OverrideNullablepublic Object resolveArgument(MethodParameter parameter, Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, Nullable WebDataBinderFactory binderFactory) throws Exception {HandlerMethodArgumentResolver resolver getArgumentResolver(parameter);if (resolver null) {throw new IllegalArgumentException(Unsupported parameter type [ parameter.getParameterType().getName() ]. supportsParameter should be called first.);}return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}Nullableprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result this.argumentResolverCache.get(parameter);if (result null) {//挨个判断所有参数解析器那个支持解析这个参数for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) {result resolver;this.argumentResolverCache.put(parameter, result);//找到了resolver就缓存起来方便稍后resolveArgument()方法使用break;}}}return result;} }小结 本节描述一个请求发送到DispatcherServlet后的具体处理流程也就是SpringMVC的主要原理。 本节内容较多且硬核对日后编程很有帮助需耐心对待。 可以运行一个示例打断点在Debug模式下查看程序流程。 33、请求处理-【源码分析】-Servlet API参数解析原理 WebRequestServletRequestMultipartRequestHttpSessionjavax.servlet.http.PushBuilderPrincipalInputStreamReaderHttpMethodLocaleTimeZoneZoneId ServletRequestMethodArgumentResolver用来处理以上的参数 public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {Nullableprivate static Class? pushBuilder;static {try {pushBuilder ClassUtils.forName(javax.servlet.http.PushBuilder,ServletRequestMethodArgumentResolver.class.getClassLoader());}catch (ClassNotFoundException ex) {// Servlet 4.0 PushBuilder not found - not supported for injectionpushBuilder null;}}Overridepublic boolean supportsParameter(MethodParameter parameter) {Class? paramType parameter.getParameterType();return (WebRequest.class.isAssignableFrom(paramType) ||ServletRequest.class.isAssignableFrom(paramType) ||MultipartRequest.class.isAssignableFrom(paramType) ||HttpSession.class.isAssignableFrom(paramType) ||(pushBuilder ! null pushBuilder.isAssignableFrom(paramType)) ||(Principal.class.isAssignableFrom(paramType) !parameter.hasParameterAnnotations()) ||InputStream.class.isAssignableFrom(paramType) ||Reader.class.isAssignableFrom(paramType) ||HttpMethod.class paramType ||Locale.class paramType ||TimeZone.class paramType ||ZoneId.class paramType);}Overridepublic Object resolveArgument(MethodParameter parameter, Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, Nullable WebDataBinderFactory binderFactory) throws Exception {Class? paramType parameter.getParameterType();// WebRequest / NativeWebRequest / ServletWebRequestif (WebRequest.class.isAssignableFrom(paramType)) {if (!paramType.isInstance(webRequest)) {throw new IllegalStateException(Current request is not of type [ paramType.getName() ]: webRequest);}return webRequest;}// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequestif (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {return resolveNativeRequest(webRequest, paramType);}// HttpServletRequest required for all further argument typesreturn resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));}private T T resolveNativeRequest(NativeWebRequest webRequest, ClassT requiredType) {T nativeRequest webRequest.getNativeRequest(requiredType);if (nativeRequest null) {throw new IllegalStateException(Current request is not of type [ requiredType.getName() ]: webRequest);}return nativeRequest;}Nullableprivate Object resolveArgument(Class? paramType, HttpServletRequest request) throws IOException {if (HttpSession.class.isAssignableFrom(paramType)) {HttpSession session request.getSession();if (session ! null !paramType.isInstance(session)) {throw new IllegalStateException(Current session is not of type [ paramType.getName() ]: session);}return session;}else if (pushBuilder ! null pushBuilder.isAssignableFrom(paramType)) {return PushBuilderDelegate.resolvePushBuilder(request, paramType);}else if (InputStream.class.isAssignableFrom(paramType)) {InputStream inputStream request.getInputStream();if (inputStream ! null !paramType.isInstance(inputStream)) {throw new IllegalStateException(Request input stream is not of type [ paramType.getName() ]: inputStream);}return inputStream;}else if (Reader.class.isAssignableFrom(paramType)) {Reader reader request.getReader();if (reader ! null !paramType.isInstance(reader)) {throw new IllegalStateException(Request body reader is not of type [ paramType.getName() ]: reader);}return reader;}else if (Principal.class.isAssignableFrom(paramType)) {Principal userPrincipal request.getUserPrincipal();if (userPrincipal ! null !paramType.isInstance(userPrincipal)) {throw new IllegalStateException(Current user principal is not of type [ paramType.getName() ]: userPrincipal);}return userPrincipal;}else if (HttpMethod.class paramType) {return HttpMethod.resolve(request.getMethod());}else if (Locale.class paramType) {return RequestContextUtils.getLocale(request);}else if (TimeZone.class paramType) {TimeZone timeZone RequestContextUtils.getTimeZone(request);return (timeZone ! null ? timeZone : TimeZone.getDefault());}else if (ZoneId.class paramType) {TimeZone timeZone RequestContextUtils.getTimeZone(request);return (timeZone ! null ? timeZone.toZoneId() : ZoneId.systemDefault());}// Should never happen...throw new UnsupportedOperationException(Unknown parameter type: paramType.getName());}/*** Inner class to avoid a hard dependency on Servlet API 4.0 at runtime.*/private static class PushBuilderDelegate {Nullablepublic static Object resolvePushBuilder(HttpServletRequest request, Class? paramType) {PushBuilder pushBuilder request.newPushBuilder();if (pushBuilder ! null !paramType.isInstance(pushBuilder)) {throw new IllegalStateException(Current push builder is not of type [ paramType.getName() ]: pushBuilder);}return pushBuilder;}} }用例 Controller public class RequestController {GetMapping(/goto)public String goToPage(HttpServletRequest request){request.setAttribute(msg,成功了...);request.setAttribute(code,200);return forward:/success; //转发到 /success请求} }34、请求处理-【源码分析】-Model、Map原理 复杂参数 Map Modelmap、model里面的数据会被放在request的请求域 request.setAttribute Errors/BindingResult RedirectAttributes 重定向携带数据 ServletResponseresponse SessionStatus UriComponentsBuilder ServletUriComponentsBuilder 用例 GetMapping(/params) public String testParam(MapString,Object map,Model model,HttpServletRequest request,HttpServletResponse response){//下面三位都是可以给request域中放数据map.put(hello,world666);model.addAttribute(world,hello666);request.setAttribute(message,HelloWorld);Cookie cookie new Cookie(c1,v1);response.addCookie(cookie);return forward:/success; }ResponseBody GetMapping(/success) public Map success(RequestAttribute(value msg,required false) String msg,RequestAttribute(value code,required false)Integer code,HttpServletRequest request){Object msg1 request.getAttribute(msg);MapString,Object map new HashMap();Object hello request.getAttribute(hello);//得出testParam方法赋予的值 world666Object world request.getAttribute(world);//得出testParam方法赋予的值 hello666Object message request.getAttribute(message);//得出testParam方法赋予的值 HelloWorldmap.put(reqMethod_msg,msg1);map.put(annotation_msg,msg);map.put(hello,hello);map.put(world,world);map.put(message,message);return map; }MapString,Object map Model model HttpServletRequest request 上面三位都是可以给request域中放数据用request.getAttribute()获取 接下来我们看看MapString,Object map与Model model用什么参数处理器。 MapString,Object map参数用MapMethodProcessor处理 public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {Overridepublic boolean supportsParameter(MethodParameter parameter) {return (Map.class.isAssignableFrom(parameter.getParameterType()) parameter.getParameterAnnotations().length 0);}OverrideNullablepublic Object resolveArgument(MethodParameter parameter, Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, Nullable WebDataBinderFactory binderFactory) throws Exception {Assert.state(mavContainer ! null, ModelAndViewContainer is required for model exposure);return mavContainer.getModel();}...}mavContainer.getModel()如下 public class ModelAndViewContainer {...private final ModelMap defaultModel new BindingAwareModelMap();Nullableprivate ModelMap redirectModel;...public ModelMap getModel() {if (useDefaultModel()) {return this.defaultModel;}else {if (this.redirectModel null) {this.redirectModel new ModelMap();}return this.redirectModel;}}private boolean useDefaultModel() {return (!this.redirectModelScenario || (this.redirectModel null !this.ignoreDefaultModelOnRedirect));}...}Model model用ModelMethodProcessor处理 public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {Overridepublic boolean supportsParameter(MethodParameter parameter) {return Model.class.isAssignableFrom(parameter.getParameterType());}OverrideNullablepublic Object resolveArgument(MethodParameter parameter, Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, Nullable WebDataBinderFactory binderFactory) throws Exception {Assert.state(mavContainer ! null, ModelAndViewContainer is required for model exposure);return mavContainer.getModel();}... }return mavContainer.getModel();这跟MapMethodProcessor的一致 Model也是另一种意义的Map。 接下来看看MapString,Object map与Model model值是如何做到用request.getAttribute()获取的。 众所周知所有的数据都放在 ModelAndView包含要去的页面地址View还包含Model数据。 先看ModelAndView接下来是如何处理的 public class DispatcherServlet extends FrameworkServlet {...protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {...try {ModelAndView mv null;...// Actually invoke the handler.mv ha.handle(processedRequest, response, mappedHandler.getHandler());...}catch (Exception ex) {dispatchException ex;}catch (Throwable err) {// As of 4.3, were processing Errors thrown from handler methods as well,// making them available for ExceptionHandler methods and other scenarios.dispatchException new NestedServletException(Handler dispatch failed, err);}//处理分发结果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}...}private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,Nullable HandlerExecutionChain mappedHandler, Nullable ModelAndView mv,Nullable Exception exception) throws Exception {...// Did the handler return a view to render?if (mv ! null !mv.wasCleared()) {render(mv, request, response);...}...}protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {...View view;String viewName mv.getViewName();if (viewName ! null) {// We need to resolve the view name.view resolveViewName(viewName, mv.getModelInternal(), locale, request);if (view null) {throw new ServletException(Could not resolve view with name mv.getViewName() in servlet with name getServletName() );}}else {// No need to lookup: the ModelAndView object contains the actual View object.view mv.getView();if (view null) {throw new ServletException(ModelAndView [ mv ] neither contains a view name nor a View object in servlet with name getServletName() );}}view.render(mv.getModelInternal(), request, response);...}}在Debug模式下view属为InternalResourceView类。 public class InternalResourceView extends AbstractUrlBasedView {Override//该方法在AbstractViewAbstractUrlBasedView继承了AbstractViewpublic void render(Nullable MapString, ? model, HttpServletRequest request,HttpServletResponse response) throws Exception {...MapString, Object mergedModel createMergedOutputModel(model, request, response);prepareResponse(request, response);//看下一个方法实现renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);}Overrideprotected void renderMergedOutputModel(MapString, Object model, HttpServletRequest request, HttpServletResponse response) throws Exception {// Expose the model object as request attributes.// 暴露模型作为请求域属性exposeModelAsRequestAttributes(model, request);//---重点// Expose helpers as request attributes, if any.exposeHelpers(request);// Determine the path for the request dispatcher.String dispatcherPath prepareForRendering(request, response);// Obtain a RequestDispatcher for the target resource (typically a JSP).RequestDispatcher rd getRequestDispatcher(request, dispatcherPath);...}//该方法在AbstractViewAbstractUrlBasedView继承了AbstractViewprotected void exposeModelAsRequestAttributes(MapString, Object model,HttpServletRequest request) throws Exception {model.forEach((name, value) - {if (value ! null) {request.setAttribute(name, value);}else {request.removeAttribute(name);}});}}exposeModelAsRequestAttributes方法看出MapString,Object mapModel model这两种类型数据可以给request域中放数据用request.getAttribute()获取。 35、请求处理-【源码分析】-自定义参数绑定原理 RestController public class ParameterTestController {/*** 数据绑定页面提交的请求数据GET、POST都可以和对象属性进行绑定* param person* return*/PostMapping(/saveuser)public Person saveuser(Person person){return person;} }/*** 姓名 input nameuserName/ br/* 年龄 input nameage/ br/* 生日 input namebirth/ br/* 宠物姓名input namepet.name/br/* 宠物年龄input namepet.age/*/ Data public class Person {private String userName;private Integer age;private Date birth;private Pet pet;}Data public class Pet {private String name;private String age;}封装过程用到ServletModelAttributeMethodProcessor public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {Override//本方法在ModelAttributeMethodProcessor类public boolean supportsParameter(MethodParameter parameter) {return (parameter.hasParameterAnnotation(ModelAttribute.class) ||(this.annotationNotRequired !BeanUtils.isSimpleProperty(parameter.getParameterType())));}OverrideNullable//本方法在ModelAttributeMethodProcessor类public final Object resolveArgument(MethodParameter parameter, Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, Nullable WebDataBinderFactory binderFactory) throws Exception {...String name ModelFactory.getNameForParameter(parameter);ModelAttribute ann parameter.getParameterAnnotation(ModelAttribute.class);if (ann ! null) {mavContainer.setBinding(name, ann.binding());}Object attribute null;BindingResult bindingResult null;if (mavContainer.containsAttribute(name)) {attribute mavContainer.getModel().get(name);}else {// Create attribute instancetry {attribute createAttribute(name, parameter, binderFactory, webRequest);}catch (BindException ex) {...}}if (bindingResult null) {// Bean property binding and validation;// skipped in case of binding failure on construction.WebDataBinder binder binderFactory.createBinder(webRequest, attribute, name);if (binder.getTarget() ! null) {if (!mavContainer.isBindingDisabled(name)) {//web数据绑定器将请求参数的值绑定到指定的JavaBean里面**bindRequestParameters(binder, webRequest);}validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() isBindExceptionRequired(binder, parameter)) {throw new BindException(binder.getBindingResult());}}// Value type adaptation, also covering java.util.Optionalif (!parameter.getParameterType().isInstance(attribute)) {attribute binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);}bindingResult binder.getBindingResult();}// Add resolved attribute and BindingResult at the end of the modelMapString, Object bindingResultModel bindingResult.getModel();mavContainer.removeAttributes(bindingResultModel);mavContainer.addAllAttributes(bindingResultModel);return attribute;} }WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中 在过程当中用到GenericConversionService在设置每一个值的时候找它里面的所有converter那个可以将这个数据类型request带来参数的字符串转换到指定的类型 36、请求处理-【源码分析】-自定义Converter原理 未来我们可以给WebDataBinder里面放自己的Converter 下面演示将字符串“啊猫,3”转换成Pet对象。 //1、WebMvcConfigurer定制化SpringMVC的功能Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(new ConverterString, Pet() {Overridepublic Pet convert(String source) {// 啊猫,3if(!StringUtils.isEmpty(source)){Pet pet new Pet();String[] split source.split(,);pet.setName(split[0]);pet.setAge(Integer.parseInt(split[1]));return pet;}return null;}});}};}37、响应处理-【源码分析】-ReturnValueHandler原理 假设给前端自动返回json数据需要引入相关的依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId /dependency!-- web场景自动引入了json场景 -- dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-json/artifactIdversion2.3.4.RELEASE/versionscopecompile/scope /dependency控制层代码如下 Controller public class ResponseTestController {ResponseBody //利用返回值处理器里面的消息转换器进行处理GetMapping(value /test/person)public Person getPerson(){Person person new Person();person.setAge(28);person.setBirth(new Date());person.setUserName(zhangsan);return person;}}32、请求处理-【源码分析】-各种类型参数解析原理 - 返回值处理器有讨论ReturnValueHandler。现在直接看看重点 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {...Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest new ServletWebRequest(request, response);try {...ServletInvocableHandlerMethod invocableMethod createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers ! null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers ! null) {//----关注点invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}...invocableMethod.invokeAndHandle(webRequest, mavContainer);//看下块代码if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}}public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {Object returnValue invokeForRequest(webRequest, mavContainer, providedArgs);...try {//看下块代码this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {...}}public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {...Overridepublic void handleReturnValue(Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {//selectHandler()实现在下面HandlerMethodReturnValueHandler handler selectHandler(returnValue, returnType);if (handler null) {throw new IllegalArgumentException(Unknown return value type: returnType.getParameterType().getName());}//开始处理handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}Nullableprivate HandlerMethodReturnValueHandler selectHandler(Nullable Object value, MethodParameter returnType) {boolean isAsyncValue isAsyncReturnValue(value, returnType);for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {if (isAsyncValue !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {continue;}if (handler.supportsReturnType(returnType)) {return handler;}}return null;} ResponseBody 注解即RequestResponseBodyMethodProcessor它实现HandlerMethodReturnValueHandler接口 public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {...Overridepublic void handleReturnValue(Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage createInputMessage(webRequest);ServletServerHttpResponse outputMessage createOutputMessage(webRequest);// 使用消息转换器进行写出操作本方法下一章节介绍// Try even with null return value. ResponseBodyAdvice could get involved.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}} 38、响应处理-【源码分析】-HTTPMessageConverter原理 返回值处理器ReturnValueHandler原理 返回值处理器判断是否支持这种类型返回值 supportsReturnType返回值处理器调用 handleReturnValue 进行处理RequestResponseBodyMethodProcessor 可以处理返回值标了ResponseBody 注解的。 利用 MessageConverters 进行处理 将数据写为json 内容协商浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型服务器最终根据自己自身的能力决定服务器能生产出什么样内容类型的数据SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter 看谁能处理 得到MappingJackson2HttpMessageConverter可以将对象写为json利用MappingJackson2HttpMessageConverter将对象转为json再写出去。 //RequestResponseBodyMethodProcessor继承这类 public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolverimplements HandlerMethodReturnValueHandler {...//承接上一节内容protected T void writeWithMessageConverters(Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {Object body;Class? valueType;Type targetType;if (value instanceof CharSequence) {body value.toString();valueType String.class;targetType String.class;}else {body value;valueType getReturnValueType(body, returnType);targetType GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());}...//内容协商浏览器默认会以请求头(参数Accept)的方式告诉服务器他能接受什么样的内容类型MediaType selectedMediaType null;MediaType contentType outputMessage.getHeaders().getContentType();boolean isContentTypePreset contentType ! null contentType.isConcrete();if (isContentTypePreset) {if (logger.isDebugEnabled()) {logger.debug(Found Content-Type: contentType in response);}selectedMediaType contentType;}else {HttpServletRequest request inputMessage.getServletRequest();ListMediaType acceptableTypes getAcceptableMediaTypes(request);//服务器最终根据自己自身的能力决定服务器能生产出什么样内容类型的数据ListMediaType producibleTypes getProducibleMediaTypes(request, valueType, targetType);if (body ! null producibleTypes.isEmpty()) {throw new HttpMessageNotWritableException(No converter found for return value of type: valueType);}ListMediaType mediaTypesToUse new ArrayList();for (MediaType requestedType : acceptableTypes) {for (MediaType producibleType : producibleTypes) {if (requestedType.isCompatibleWith(producibleType)) {mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));}}}if (mediaTypesToUse.isEmpty()) {if (body ! null) {throw new HttpMediaTypeNotAcceptableException(producibleTypes);}if (logger.isDebugEnabled()) {logger.debug(No match for acceptableTypes , supported: producibleTypes);}return;}MediaType.sortBySpecificityAndQuality(mediaTypesToUse);//选择一个MediaTypefor (MediaType mediaType : mediaTypesToUse) {if (mediaType.isConcrete()) {selectedMediaType mediaType;break;}else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {selectedMediaType MediaType.APPLICATION_OCTET_STREAM;break;}}if (logger.isDebugEnabled()) {logger.debug(Using selectedMediaType , given acceptableTypes and supported producibleTypes);}}if (selectedMediaType ! null) {selectedMediaType selectedMediaType.removeQualityValue();//本节主角HttpMessageConverterfor (HttpMessageConverter? converter : this.messageConverters) {GenericHttpMessageConverter genericConverter (converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter?) converter : null);//判断是否可写if (genericConverter ! null ?((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :converter.canWrite(valueType, selectedMediaType)) {body getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class? extends HttpMessageConverter?) converter.getClass(),inputMessage, outputMessage);if (body ! null) {Object theBody body;LogFormatUtils.traceDebug(logger, traceOn -Writing [ LogFormatUtils.formatValue(theBody, !traceOn) ]);addContentDispositionHeader(inputMessage, outputMessage);//开始写入if (genericConverter ! null) {genericConverter.write(body, targetType, selectedMediaType, outputMessage);}else {((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);}}else {if (logger.isDebugEnabled()) {logger.debug(Nothing to write: null body);}}return;}}}...}HTTPMessageConverter接口 /*** Strategy interface for converting from and to HTTP requests and responses.*/ public interface HttpMessageConverterT {/*** Indicates whether the given class can be read by this converter.*/boolean canRead(Class? clazz, Nullable MediaType mediaType);/*** Indicates whether the given class can be written by this converter.*/boolean canWrite(Class? clazz, Nullable MediaType mediaType);/*** Return the list of {link MediaType} objects supported by this converter.*/ListMediaType getSupportedMediaTypes();/*** Read an object of the given type from the given input message, and returns it.*/T read(Class? extends T clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException;/*** Write an given object to the given output message.*/void write(T t, Nullable MediaType contentType, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException;} HttpMessageConverter: 看是否支持将 此 Class类型的对象转为MediaType类型的数据。 例子Person对象转为JSON或者 JSON转为Person这将用到MappingJackson2HttpMessageConverter public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {... }关于MappingJackson2HttpMessageConverter的实例化请看下节。 关于HttpMessageConverters的初始化 DispatcherServlet的初始化时会调用initHandlerAdapters(ApplicationContext context) public class DispatcherServlet extends FrameworkServlet {...private void initHandlerAdapters(ApplicationContext context) {this.handlerAdapters null;if (this.detectAllHandlerAdapters) {// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.MapString, HandlerAdapter matchingBeans BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerAdapters new ArrayList(matchingBeans.values());// We keep HandlerAdapters in sorted order.AnnotationAwareOrderComparator.sort(this.handlerAdapters);}}...上述代码会加载ApplicationContext的所有HandlerAdapter用来处理RequestMapping的RequestMappingHandlerAdapter实现HandlerAdapter接口RequestMappingHandlerAdapter也被实例化。 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {...private ListHttpMessageConverter? messageConverters;...public RequestMappingHandlerAdapter() {this.messageConverters new ArrayList(4);this.messageConverters.add(new ByteArrayHttpMessageConverter());this.messageConverters.add(new StringHttpMessageConverter());if (!shouldIgnoreXml) {try {this.messageConverters.add(new SourceHttpMessageConverter());}catch (Error err) {// Ignore when no TransformerFactory implementation is available}}this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());}在构造器中看到一堆HttpMessageConverter。接着重点查看AllEncompassingFormHttpMessageConverter类 public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConverter {/*** Boolean flag controlled by a {code spring.xml.ignore} system property that instructs Spring to* ignore XML, i.e. to not initialize the XML-related infrastructure.* pThe default is false.*/private static final boolean shouldIgnoreXml SpringProperties.getFlag(spring.xml.ignore);private static final boolean jaxb2Present;private static final boolean jackson2Present;private static final boolean jackson2XmlPresent;private static final boolean jackson2SmilePresent;private static final boolean gsonPresent;private static final boolean jsonbPresent;private static final boolean kotlinSerializationJsonPresent;static {ClassLoader classLoader AllEncompassingFormHttpMessageConverter.class.getClassLoader();jaxb2Present ClassUtils.isPresent(javax.xml.bind.Binder, classLoader);jackson2Present ClassUtils.isPresent(com.fasterxml.jackson.databind.ObjectMapper, classLoader) ClassUtils.isPresent(com.fasterxml.jackson.core.JsonGenerator, classLoader);jackson2XmlPresent ClassUtils.isPresent(com.fasterxml.jackson.dataformat.xml.XmlMapper, classLoader);jackson2SmilePresent ClassUtils.isPresent(com.fasterxml.jackson.dataformat.smile.SmileFactory, classLoader);gsonPresent ClassUtils.isPresent(com.google.gson.Gson, classLoader);jsonbPresent ClassUtils.isPresent(javax.json.bind.Jsonb, classLoader);kotlinSerializationJsonPresent ClassUtils.isPresent(kotlinx.serialization.json.Json, classLoader);}public AllEncompassingFormHttpMessageConverter() {if (!shouldIgnoreXml) {try {addPartConverter(new SourceHttpMessageConverter());}catch (Error err) {// Ignore when no TransformerFactory implementation is available}if (jaxb2Present !jackson2XmlPresent) {addPartConverter(new Jaxb2RootElementHttpMessageConverter());}}if (jackson2Present) {addPartConverter(new MappingJackson2HttpMessageConverter());//----重点看这里}else if (gsonPresent) {addPartConverter(new GsonHttpMessageConverter());}else if (jsonbPresent) {addPartConverter(new JsonbHttpMessageConverter());}else if (kotlinSerializationJsonPresent) {addPartConverter(new KotlinSerializationJsonHttpMessageConverter());}if (jackson2XmlPresent !shouldIgnoreXml) {addPartConverter(new MappingJackson2XmlHttpMessageConverter());}if (jackson2SmilePresent) {addPartConverter(new MappingJackson2SmileHttpMessageConverter());}}}public class FormHttpMessageConverter implements HttpMessageConverterMultiValueMapString, ? {...private ListHttpMessageConverter? partConverters new ArrayList();...public void addPartConverter(HttpMessageConverter? partConverter) {Assert.notNull(partConverter, partConverter must not be null);this.partConverters.add(partConverter);}... } 在AllEncompassingFormHttpMessageConverter类构造器看到MappingJackson2HttpMessageConverter类的实例化AllEncompassingFormHttpMessageConverter包含MappingJackson2HttpMessageConverter。 ReturnValueHandler是怎么与MappingJackson2HttpMessageConverter关联起来请看下节。 ReturnValueHandler与MappingJackson2HttpMessageConverter关联 再次回顾RequestMappingHandlerAdapter public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {...Nullableprivate HandlerMethodReturnValueHandlerComposite returnValueHandlers;//我们关注的returnValueHandlersOverrideNullable//本方法在AbstractHandlerMethodAdapterpublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);}Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;...mav invokeHandlerMethod(request, response, handlerMethod);...return mav;}Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory getDataBinderFactory(handlerMethod);ModelFactory modelFactory getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers ! null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers ! null) {//---我们关注的returnValueHandlersinvocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}...invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}}Overridepublic void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beans...if (this.returnValueHandlers null) {//赋值returnValueHandlersListHandlerMethodReturnValueHandler handlers getDefaultReturnValueHandlers();this.returnValueHandlers new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}private ListHandlerMethodReturnValueHandler getDefaultReturnValueHandlers() {ListHandlerMethodReturnValueHandler handlers new ArrayList(20);...// Annotation-based return value types//这里就是 ReturnValueHandler与 MappingJackson2HttpMessageConverter关联 的关键点handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),//---MessageConverters也就传参传进来的this.contentNegotiationManager, this.requestResponseBodyAdvice));//...return handlers;}//------public ListHttpMessageConverter? getMessageConverters() {return this.messageConverters;}//RequestMappingHandlerAdapter构造器已初始化部分messageConverterspublic RequestMappingHandlerAdapter() {this.messageConverters new ArrayList(4);this.messageConverters.add(new ByteArrayHttpMessageConverter());this.messageConverters.add(new StringHttpMessageConverter());if (!shouldIgnoreXml) {try {this.messageConverters.add(new SourceHttpMessageConverter());}catch (Error err) {// Ignore when no TransformerFactory implementation is available}}this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());}...}应用中WebMvcAutoConfiguration底层是WebMvcConfigurationSupport实现传入更多messageConverters其中就包含MappingJackson2HttpMessageConverter。 39、响应处理-【源码分析】-内容协商原理 根据客户端接收能力不同返回不同媒体类型的数据。 引入XML依赖 dependencygroupIdcom.fasterxml.jackson.dataformat/groupIdartifactIdjackson-dataformat-xml/artifactId /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。调用它进行转化 。 //RequestResponseBodyMethodProcessor继承这类 public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolverimplements HandlerMethodReturnValueHandler {...//跟上一节的代码一致protected T void writeWithMessageConverters(Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {Object body;Class? valueType;Type targetType;if (value instanceof CharSequence) {body value.toString();valueType String.class;targetType String.class;}else {body value;valueType getReturnValueType(body, returnType);targetType GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());}...//本节重点//内容协商浏览器默认会以请求头(参数Accept)的方式告诉服务器他能接受什么样的内容类型MediaType selectedMediaType null;MediaType contentType outputMessage.getHeaders().getContentType();boolean isContentTypePreset contentType ! null contentType.isConcrete();if (isContentTypePreset) {if (logger.isDebugEnabled()) {logger.debug(Found Content-Type: contentType in response);}selectedMediaType contentType;}else {HttpServletRequest request inputMessage.getServletRequest();ListMediaType acceptableTypes getAcceptableMediaTypes(request);//服务器最终根据自己自身的能力决定服务器能生产出什么样内容类型的数据ListMediaType producibleTypes getProducibleMediaTypes(request, valueType, targetType);if (body ! null producibleTypes.isEmpty()) {throw new HttpMessageNotWritableException(No converter found for return value of type: valueType);}ListMediaType mediaTypesToUse new ArrayList();for (MediaType requestedType : acceptableTypes) {for (MediaType producibleType : producibleTypes) {if (requestedType.isCompatibleWith(producibleType)) {mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));}}}if (mediaTypesToUse.isEmpty()) {if (body ! null) {throw new HttpMediaTypeNotAcceptableException(producibleTypes);}if (logger.isDebugEnabled()) {logger.debug(No match for acceptableTypes , supported: producibleTypes);}return;}MediaType.sortBySpecificityAndQuality(mediaTypesToUse);//选择一个MediaTypefor (MediaType mediaType : mediaTypesToUse) {if (mediaType.isConcrete()) {selectedMediaType mediaType;break;}else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {selectedMediaType MediaType.APPLICATION_OCTET_STREAM;break;}}if (logger.isDebugEnabled()) {logger.debug(Using selectedMediaType , given acceptableTypes and supported producibleTypes);}}if (selectedMediaType ! null) {selectedMediaType selectedMediaType.removeQualityValue();//本节主角HttpMessageConverterfor (HttpMessageConverter? converter : this.messageConverters) {GenericHttpMessageConverter genericConverter (converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter?) converter : null);//判断是否可写if (genericConverter ! null ?((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :converter.canWrite(valueType, selectedMediaType)) {body getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class? extends HttpMessageConverter?) converter.getClass(),inputMessage, outputMessage);if (body ! null) {Object theBody body;LogFormatUtils.traceDebug(logger, traceOn -Writing [ LogFormatUtils.formatValue(theBody, !traceOn) ]);addContentDispositionHeader(inputMessage, outputMessage);//开始写入if (genericConverter ! null) {genericConverter.write(body, targetType, selectedMediaType, outputMessage);}else {((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);}}else {if (logger.isDebugEnabled()) {logger.debug(Nothing to write: null body);}}return;}}}...}40、响应处理-【源码分析】-基于请求参数的内容协商原理 上一节内容协商原理的第二步 获取客户端PostMan、浏览器支持接收的内容类型。获取客户端Accept请求头字段application/xml contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型 //RequestResponseBodyMethodProcessor继承这类 public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolverimplements HandlerMethodReturnValueHandler {...//跟上一节的代码一致protected T void writeWithMessageConverters(Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {Object body;Class? valueType;Type targetType;...//本节重点//内容协商浏览器默认会以请求头(参数Accept)的方式告诉服务器他能接受什么样的内容类型MediaType selectedMediaType null;MediaType contentType outputMessage.getHeaders().getContentType();boolean isContentTypePreset contentType ! null contentType.isConcrete();if (isContentTypePreset) {if (logger.isDebugEnabled()) {logger.debug(Found Content-Type: contentType in response);}selectedMediaType contentType;}else {HttpServletRequest request inputMessage.getServletRequest();ListMediaType acceptableTypes getAcceptableMediaTypes(request);//服务器最终根据自己自身的能力决定服务器能生产出什么样内容类型的数据ListMediaType producibleTypes getProducibleMediaTypes(request, valueType, targetType);...}//在AbstractMessageConverterMethodArgumentResolver类内private ListMediaType getAcceptableMediaTypes(HttpServletRequest request)throws HttpMediaTypeNotAcceptableException {//内容协商管理器 默认使用基于请求头的策略return this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));}}public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver {...public ContentNegotiationManager() {this(new HeaderContentNegotiationStrategy());//内容协商管理器 默认使用基于请求头的策略}Overridepublic ListMediaType resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {for (ContentNegotiationStrategy strategy : this.strategies) {ListMediaType mediaTypes strategy.resolveMediaTypes(request);if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) {continue;}return mediaTypes;}return MEDIA_TYPE_ALL_LIST;}...}//基于请求头的策略 public class HeaderContentNegotiationStrategy implements ContentNegotiationStrategy {/*** {inheritDoc}* throws HttpMediaTypeNotAcceptableException if the Accept header cannot be parsed*/Overridepublic ListMediaType resolveMediaTypes(NativeWebRequest request)throws HttpMediaTypeNotAcceptableException {String[] headerValueArray request.getHeaderValues(HttpHeaders.ACCEPT);if (headerValueArray null) {return MEDIA_TYPE_ALL_LIST;}ListString headerValues Arrays.asList(headerValueArray);try {ListMediaType mediaTypes MediaType.parseMediaTypes(headerValues);MediaType.sortBySpecificityAndQuality(mediaTypes);return !CollectionUtils.isEmpty(mediaTypes) ? mediaTypes : MEDIA_TYPE_ALL_LIST;}catch (InvalidMediaTypeException ex) {throw new HttpMediaTypeNotAcceptableException(Could not parse Accept header headerValues : ex.getMessage());}}}开启浏览器参数方式内容协商功能 为了方便内容协商开启基于请求参数的内容协商功能。 spring:mvc:contentnegotiation:favor-parameter: true #开启请求参数内容协商模式内容协商管理器就会多了一个ParameterContentNegotiationStrategy由Spring容器注入 public class ParameterContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy {private String parameterName format;///*** Create an instance with the given map of file extensions and media types.*/public ParameterContentNegotiationStrategy(MapString, MediaType mediaTypes) {super(mediaTypes);}/*** Set the name of the parameter to use to determine requested media types.* pBy default this is set to {code format}.*/public void setParameterName(String parameterName) {Assert.notNull(parameterName, parameterName is required);this.parameterName parameterName;}public String getParameterName() {return this.parameterName;}OverrideNullableprotected String getMediaTypeKey(NativeWebRequest request) {return request.getParameter(getParameterName());}//---以下方法在AbstractMappingContentNegotiationStrategy类Overridepublic ListMediaType resolveMediaTypes(NativeWebRequest webRequest)throws HttpMediaTypeNotAcceptableException {return resolveMediaTypeKey(webRequest, getMediaTypeKey(webRequest));}/*** An alternative to {link #resolveMediaTypes(NativeWebRequest)} that accepts* an already extracted key.* since 3.2.16*/public ListMediaType resolveMediaTypeKey(NativeWebRequest webRequest, Nullable String key)throws HttpMediaTypeNotAcceptableException {if (StringUtils.hasText(key)) {MediaType mediaType lookupMediaType(key);if (mediaType ! null) {handleMatch(key, mediaType);return Collections.singletonList(mediaType);}mediaType handleNoMatch(webRequest, key);if (mediaType ! null) {addMapping(key, mediaType);return Collections.singletonList(mediaType);}}return MEDIA_TYPE_ALL_LIST;}}然后浏览器地址输入带format参数的URL http://localhost:8080/test/person?formatjson 或 http://localhost:8080/test/person?formatxml这样后端会根据参数format的值返回对应json或xml格式的数据。 41、响应处理-【源码分析】-自定义MessageConverter 实现多协议数据兼容。json、xml、x-guigu这个是自创的 ResponseBody 响应数据出去 调用 RequestResponseBodyMethodProcessor 处理 Processor 处理方法返回值。通过 MessageConverter处理 所有 MessageConverter 合起来可以支持各种媒体类型数据的操作读、写 内容协商找到最终的 messageConverter SpringMVC的什么功能一个入口给容器中添加一个 WebMvcConfigurer Configuration(proxyBeanMethods false) public class WebConfig {Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {Overridepublic void extendMessageConverters(ListHttpMessageConverter? converters) {converters.add(new GuiguMessageConverter());}}} }/*** 自定义的Converter*/ public class GuiguMessageConverter implements HttpMessageConverterPerson {Overridepublic boolean canRead(Class? clazz, MediaType mediaType) {return false;}Overridepublic boolean canWrite(Class? clazz, MediaType mediaType) {return clazz.isAssignableFrom(Person.class);}/*** 服务器要统计所有MessageConverter都能写出哪些内容类型** application/x-guigu* return*/Overridepublic ListMediaType getSupportedMediaTypes() {return MediaType.parseMediaTypes(application/x-guigu);}Overridepublic Person read(Class? extends Person clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {return null;}Overridepublic void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {//自定义协议数据的写出String data person.getUserName();person.getAge();person.getBirth();//写出去OutputStream body outputMessage.getBody();body.write(data.getBytes());} }import java.util.Date;Controller public class ResponseTestController {/*** 1、浏览器发请求直接返回 xml [application/xml] jacksonXmlConverter* 2、如果是ajax请求 返回 json [application/json] jacksonJsonConverter* 3、如果硅谷app发请求返回自定义协议数据 [appliaction/x-guigu] xxxxConverter* 属性值1;属性值2;** 步骤* 1、添加自定义的MessageConverter进系统底层* 2、系统底层就会统计出所有MessageConverter能操作哪些类型* 3、客户端内容协商 [guigu---guigu]** 作业如何以参数的方式进行内容协商* return*/ResponseBody //利用返回值处理器里面的消息转换器进行处理GetMapping(value /test/person)public Person getPerson(){Person person new Person();person.setAge(28);person.setBirth(new Date());person.setUserName(zhangsan);return person;}}用Postman发送/test/person请求头Accept:application/x-guigu)将返回自定义协议数据的写出。 42、响应处理-【源码分析】-浏览器与PostMan内容协商完全适配 假设你想基于自定义请求参数的自定义内容协商功能。 换句话在地址栏输入http://localhost:8080/test/person?formatgg返回数据跟http://localhost:8080/test/person且请求头参数Accept:application/x-guigu的返回自定义协议数据的一致。 Configuration(proxyBeanMethods false) public class WebConfig /*implements WebMvcConfigurer*/ {//1、WebMvcConfigurer定制化SpringMVC的功能Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {/*** 自定义内容协商策略* param configurer*/Overridepublic void configureContentNegotiation(ContentNegotiationConfigurer configurer) {//MapString, MediaType mediaTypesMapString, MediaType mediaTypes new HashMap();mediaTypes.put(json,MediaType.APPLICATION_JSON);mediaTypes.put(xml,MediaType.APPLICATION_XML);//自定义媒体类型mediaTypes.put(gg,MediaType.parseMediaType(application/x-guigu));//指定支持解析哪些参数对应的哪些媒体类型ParameterContentNegotiationStrategy parameterStrategy new ParameterContentNegotiationStrategy(mediaTypes); // parameterStrategy.setParameterName(ff);//还需添加请求头处理策略否则accept:application/json、application/xml则会失效HeaderContentNegotiationStrategy headeStrategy new HeaderContentNegotiationStrategy();configurer.strategies(Arrays.asList(parameterStrategy, headeStrategy));}}}...}日后开发要注意有可能我们添加的自定义的功能会覆盖默认很多功能导致一些默认的功能失效。 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官方文档 thymeleaf使用 引入Starter dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-thymeleaf/artifactId /dependency自动配置好了thymeleaf Configuration(proxyBeanMethods false) EnableConfigurationProperties(ThymeleafProperties.class) ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class }) AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class }) public class ThymeleafAutoConfiguration {... }自动配好的策略 所有thymeleaf的配置值都在 ThymeleafProperties 配置好了 SpringTemplateEngine 配好了 ThymeleafViewResolver 我们只需要直接开发页面 public static final String DEFAULT_PREFIX classpath:/templates/;//模板放置处 public static final String DEFAULT_SUFFIX .html;//文件的后缀名编写一个控制层 Controller public class ViewTestController {GetMapping(/hello)public String hello(Model model){//model中的数据会被放在请求域中 request.setAttribute(a,aa)model.addAttribute(msg,一定要大力发展工业文化);model.addAttribute(link,http://www.baidu.com);return success;} }/templates/success.html !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8titleTitle/title /head body h1 th:text${msg}nice/h1 h2a hrefwww.baidu.com th:href${link}去百度/a br/a hrefwww.google.com th:href{/link}去百度/a /h2 /body /htmlserver:servlet:context-path: /app #设置应用名这个设置后URL要插入/app, 如http://localhost:8080/app/hello.html。 基本语法 表达式 表达式名字语法用途变量取值${…}获取请求域、session域、对象等值选择变量*{…}获取上下文对象值消息#{…}获取国际化等值链接{…}生成链接片段表达式~{…}jsp:include 作用引入公共页面片段 字面量 文本值: ‘one text’ , ‘Another one!’ ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false空值: null变量 onetwo… 变量不能有空格 文本操作 字符串拼接: 变量替换: |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 设置单个值 form actionsubscribe.html th:attraction{/subscribe}fieldsetinput typetext nameemail /input typesubmit valueSubscribe! th:attrvalue#{subscribe.submit}//fieldset /form设置多个值 img src../../images/gtvglogo.png th:attrsrc{/images/gtvglogo.png},title#{logo},alt#{logo} /官方文档 - 5 Setting Attribute Values 迭代 tr th:eachprod : ${prods}td th:text${prod.name}Onions/tdtd th:text${prod.price}2.41/tdtd th:text${prod.inStock}? #{true} : #{false}yes/td /trtr th:eachprod,iterStat : ${prods} th:class${iterStat.odd}? oddtd th:text${prod.name}Onions/tdtd th:text${prod.price}2.41/tdtd th:text${prod.inStock}? #{true} : #{false}yes/td /tr条件运算 a hrefcomments.htmlth:href{/product/comments(prodId${prod.id})}th:if${not #lists.isEmpty(prod.comments)}view/adiv th:switch${user.role}p th:caseadminUser is an administrator/pp th:case#{roles.manager}User is a manager/pp th:case*User is some other thing/p /div属性优先级 OrderFeatureAttributes1Fragment inclusionth:insert th:replace2Fragment iterationth:each3Conditional evaluationth:if th:unless th:switch th:case4Local variable definitionth:object th:with5General attribute modificationth:attr th:attrprepend th:attrappend6Specific attribute modificationth:value th:href th:src ...7Text (tag body modification)th:text th:utext8Fragment specificationth:fragment9Fragment removalth:remove 官方文档 - 10 Attribute Precedence 44、web实验-后台管理系统基本功能 项目创建 使用IDEA的Spring Initializr。 thymeleaf、web-starter、devtools、lombok 登陆页面 /static 放置 cssjs等静态资源 /templates/login.html 登录页 html langen xmlns:thhttp://www.thymeleaf.org!-- 要加这玩意thymeleaf才能用 --form classform-signin actionindex.html methodpost th:action{/login}...!-- 消息提醒 --label stylecolor: red th:text${msg}/labelinput typetext nameuserName classform-control placeholderUser ID autofocusinput typepassword namepassword classform-control placeholderPasswordbutton classbtn btn-lg btn-login btn-block typesubmiti classfa fa-check/i/button.../form/templates/main.html 主页 thymeleaf内联写法 pHello, [[${session.user.name}]]!/p登录控制层 Controller public class IndexController {/*** 来登录页* return*/GetMapping(value {/,/login})public String loginPage(){return login;}PostMapping(/login)public String main(User user, HttpSession session, Model model){ //RedirectAttributesif(StringUtils.hasLength(user.getUserName()) 123456.equals(user.getPassword())){//把登陆成功的用户保存起来session.setAttribute(loginUser,user);//登录成功重定向到main.html; 重定向防止表单重复提交return redirect:/main.html;}else {model.addAttribute(msg,账号密码错误);//回到登录页面return login;}}/*** 去main页面* return*/GetMapping(/main.html)public String mainPage(HttpSession session, Model model){//最好用拦截器,过滤器Object loginUser session.getAttribute(loginUser);if(loginUser ! null){return main;}else {//session过期没有登陆过//回到登录页面model.addAttribute(msg,请重新登录);return login;}}}模型 AllArgsConstructor NoArgsConstructor Data public class User {private String userName;private String password; }
http://www.huolong8.cn/news/89421/

相关文章:

  • 有什么网站可以做六级题目嘛肥城网站建设方案
  • 上海网站seo优化wordpress自定义代码在哪里设置
  • 网站建设优化建站台州优化官方网站
  • 哈尔滨住房建设发展集团网站深圳龙华区邮政编码
  • 品牌宝网站认证网页设计心得体会正文
  • 网站建设公司crm系统个人兼职网站制作
  • 网站免费模版代码南京seo公司哪家
  • 费县网站建设百度一下知道首页
  • 网站建设公司有前途吗企业信息查询公示系统
  • 营销型网站头部布局的元素黑客钓鱼网站的制作
  • 个人网站可以做论坛吗?psd转wordpress主题
  • 高端网站建设南京个人备案的网站内容
  • 个人网站要有什么seo算法培训
  • 网站外部链接添加方式山东神华网站建设
  • 如何建立虚拟架构网站容桂网站制作信息
  • 相亲网站用什么做的网站开发框架的工具
  • 海口网站建设方案策划wordpress链接重建
  • 建设网站预期效果怎么写达州网站建设qinsanw
  • 广东网站开发搭建湛江建设局网站
  • 腾讯云免费建站pc端和移动端的网站区别是什么意思
  • 零陵区住房和城乡建设局网站网站装修的代码怎么做
  • 网站seo优化免费zhicms系统wordpress
  • 网站规划与建设实验心得体会织梦门户网站源码下载
  • 网站外网怎么做如何自定义wordpress的登录页面
  • 建立网站的流程多少钱上网行为管理
  • 基于html做电商网站论文商标注册查询系统官网
  • 网站搬家内页打不开东莞seo网络营销策划
  • 医程通 网站做的太网站服务器怎么做的
  • 做网站算软件行业吗广西建设厅网站专家申请表
  • 某网站开发项目成本估计别人帮做的网站到期续费