spring boot and mvc

This commit is contained in:
yinkanglong
2023-10-29 22:21:57 +08:00
parent cf81eeddfa
commit 874f5af287
31 changed files with 1212 additions and 84 deletions

View File

@@ -1,9 +1,10 @@
## Java学习路线
> 生命的长度是有限的但Java的知识是无限的
## Java 学习路线
> 生命的长度是有限的,但 Java 的知识是无限的!
总共包括六个主要的部分。学完就能毕业啦。开始吧。
### Java的学习路线视频打卡系列
### Java 的学习路线(视频打卡系列)
- [ ] 基础知识(学习方式——阅读书籍)
- [ ] 数据库
@@ -11,30 +12,29 @@
- [ ] 计算机网络
- [ ] 数据结构与算法
- [ ] 编译原理
- [ ] Java基础教程Java的基本语法和使用及原理晚上自学,第一周学完
- [X] Java语言基础。语言语法。(分五个阶段完成把,当前第一阶段已经完成)
- [ ] Java高级操作。JDK IO操作/并发编程/网络编程
- [ ] Javaweb开发。ServletJSP相关的老技术。知道就行
- [ ] Java基本原理。JVM底层的原理和技术
- [ ] Java架构模式。面向对象和设计模式
- [ ] Java网站开发JavaWeb相关的技术知识。
- [ ] Java 基础教程Java 的基本语法和使用及原理,分五个阶段完成
- [x] Java 语言基础。语言语法。
- [ ] Java 高级操作。JDK 标准库/集合类/IO 操作/并发编程/网络编程
- [ ] Javaweb 开发。ServletJSP 相关的老技术。知道就行
- [ ] Java 基本原理。JVM 底层的原理和技术
- [ ] Java 架构模式。面向对象和设计模式
- [ ] Java 网站开发JavaWeb 相关的技术知识。)
- [ ] MySQL
- [ ] JDBC
- [ ] lombak
- [ ] mybatis
- [ ] Java工具教程Java使用的关键工具白天学习一下
- [X] maven教程
- [X] idea教程
- [ ] Java框架教程Spring全家桶白天自学
- [X] Spring
- [ ] Java 工具教程Java 使用的关键工具,白天学习一下)
- [x] maven 教程
- [x] idea 教程
- [ ] Java 框架教程Spring 全家桶,白天自学)
- [x] Spring
- [ ] Springboot
- [ ] Spring MVC
- [ ] SpringCloud
- [ ] Java云计算和微服务
- [ ] Java云原生
- [ ] docker
- [ ] k8s
- [ ] servicemesh
- [ ] Java性能优化
- [ ] Java 性能优化
- [ ] 高可用
- [ ] 双机架构
- [ ] 异地多活
@@ -44,49 +44,82 @@
- [ ] 高并发
- [ ] 分库分表
- [ ] 消息队列
- [ ] Java分布式基础
- [ ] Java 分布式基础
- [ ] 负载均衡和调度
- [ ] 分布式缓存
- [ ] 分布式算法
- [ ] kfk消息队列
- [ ] Java数据库
- [ ] kfk 消息队列
- [ ] Java 数据库
- [ ] mysql
- [ ] redis
- [ ] 中间件
- [ ] 微服务架构经典和Mesh
- [ ] 服务侧
- [ ] 注册中心Nacos/ZooKeeper/Etcd
- [ ] 服务治理,实现服务的限流、路由、熔断等功能(Seninel)
- [ ] 配置中心,运行时配置推送下发(Nacos)
- [ ] 网关
- [ ] 客户侧
- [ ] 服务框架,快速引入中间件的客户端(SpringCloud/SpringCloudAlibaba/Dubbo)
- [ ] 通信协议,实现远程调用(Dubbo)
- [ ] 通信中间件
- [ ] 消息(RocketMQ)
- [ ] 调度
- [ ] 事务
- [ ] 数据中间件
- [ ] 数据代理
- [ ] 数据缓存(Redis)
- [ ] 数据同步
- [ ] PaaS 运维发布
- [ ] 运维发布(Docker/K8s/Helm):服务自愈、弹性伸缩
- [ ] 应用管理、服务管理、节点管理、全链路灰度、灰度发布
- [ ] RaaS 监控告警
- [ ] 日志
- [ ] 监控、告警(Prometheus/Grafana)
- [ ] 链路追踪
- [ ] 研发效能 CI/CD
- [ ] 需求管理
- [ ] 迭代管理和流水线(Jekeins)
- [ ] 仓库:代码仓库(Gitee/Git)、镜像仓库(DockerHub)、依赖仓库(Maven)等
> 首先看完这些相关的课程,然后去看书重新学习一遍这些知识
> * 下三个内容jdk/jdbc -> javaweb -> springmvc -> mybatis -> ssm -> sprincloud
> * -> spring(spring->springboot->springmvc->springcloud)
> * -> java(jdk->javaweb->jvm)
> * -> 网站开发(mysql->jdbc->lomback->mybatis)
> * -> 云计算(docker->k8s)
> * -> 微服务(nacos->zookeeper->sentinel->dubbo->rocketeMQ->prometheus->grafana->git->jekens)
### Java 书籍打卡系列开始
### Java书籍打卡系列开始
第一阶段:向下探索五本
- [ ] Java编程思想4版
- [ ] Java核心技术卷112版
- [ ] Java核心技术卷212版
- [ ] Java 编程思想4 版)
- [ ] Java 核心技术卷 112 版)
- [ ] Java 核心技术卷 212 版)
- [ ] Effective Java
- [ ] 深入理解Java虚拟机
- [ ] 深入理解 Java 虚拟机
第二阶段:向上进阶五本
- [ ] ON JAVA基础卷
- [ ] ON JAVA进阶卷
- [ ] Java设计模式及实践这些东西还是得自己手敲一遍
- [ ] Java并发编程的艺术、并发编程实践、高并发编程详解机械工业出版三件套
- [ ] Spring官方文档。全套阅读。
- [ ] ON JAVA 基础卷
- [ ] ON JAVA 进阶卷
- [ ] Java 设计模式及实践(这些东西还是得自己手敲一遍)
- [ ] Java 并发编程的艺术、并发编程实践、高并发编程详解(机械工业出版三件套)
- [ ] Spring 官方文档。全套阅读。
第三阶段:云原生三本
- [ ] 云原生技术
- [ ] 服务网格
> 一份可以参考的文档。其中大数据、网络安全和扩展篇不学。
> ![](image/2022-10-27-20-26-36.png)
> 找到了两个很不得了的东西。如果有时间,可以把这两个文档全部重新看一遍。完全有书籍的质量的基础知识介绍。常看常新。自己接下来的笔记就从这上边找吧。
>
>
> 入门小站。https://rumenz.com/
> BestJavaer。https://github.com/crisxuan/bestJavaer
>

View File

@@ -0,0 +1,112 @@
# Servlet容器、Http服务器Servlet、Tomcat、SpringMVC的关系
> 参考文章
> https://blog.csdn.net/cristianoxm/article/details/121268913
## 1. Servlet
### 概念
![](image/2023-10-22-21-51-33.png)
servlet就是一个接口接口就是规定了一些规范使得一些具有某些共性的类都能实现这个接口从而都遵循某些规范。有的人往往以为就是servlet直接处理客户端的http请求其实并不是这样servlet并不会去监听8080端口直接与客户端打交道是“容器”比如常用的tomcat。客户端的请求直接打到tomcat它监听端口请求过来后根据url等信息确定要将请求交给哪个servlet去处理然后调用那个servlet的service方法service方法返回一个response对象tomcat再把这个response返回给客户端。
简而言之就是说Servlet是一个小型的JAVA程序运行在Web 服务器中来处理用户的请求我们实际上使用的各路Servlet都需要通过各种方式实现这个接口。
### 生命周期
1. 调用 init() 方法初始化
1. 调用 service() 方法来处理客户端的请求
1. 调用 destroy() 方法释放资源,标记自身为可回收
1. 被垃圾回收器回收
### 请求处理
![](image/2023-10-22-22-00-16.png)
servlet的init方法和destroy方法一般容器调用这两个方法之间的过程就叫做servlet的生命周期。调用的整个过程就如上图所示。当请求来容器第一次调用某个servlet时需要先初始化init()但当某个请求再次打到给servlet时容器会起多个线程同时访问一个servlet的service方法。
由此可以看出多个客户访问同一service方法会涉及线程安全的问题。
1. 如果service()方法没有访问Servlet的成员变量也没有访问全局的资源比如静态变量、文件、数据库连接等而是只使用了当前线程自己的资源比如非指向全局资源的临时变量、request和response对象等。该方法本身就是线程安全的不必进行任何的同步控制。
2. 如果service()方法访问了Servlet的成员变量但是对该变量的操作是只读操作该方法本身就是线程安全的不必进行任何的同步控制。
3. 如果service()方法访问了Servlet的成员变量并且对该变量的操作既有读又有写通常需要加上同步控制语句。
4. 如果service()方法访问了全局的静态变量,如果同一时刻系统中也可能有其它线程访问该静态变量,如果既有读也有写的操作,通常需要加上同步控制语句。
5. 如果service()方法访问了全局的资源,比如文件、数据库连接等,通常需要加上同步控制语句。
### Servlet如何同时处理多个请求访问
单实例多线程: 主要是请求来时会由线程调度者从线程池李取出来一个线程来作为响应线程。这个线程可能是已经实例化的也可能是新创建的。Servlet容器默认是采用单实例多线程的方式处理多个请求的
1. 当web服务器启动的时候或客户端发送请求到服务器时Servlet就被加载并实例化(只存在一个Servlet实例)
2. 容器初始化化Servlet主要就是读取配置文件例如tomcat,可以通过servlet.xml的设置线程池中线程数目初始化线程池通过web.xml,初始化每个参数值等等。
3. 当请求到达时Servlet容器通过调度线程(Dispatchaer Thread) 调度它管理下线程池中等待执行的线程Worker Thread给请求者
4. 线程执行Servlet的service方法
5. 请求结束,放回线程池,等待被调用; (注意:避免使用实例变量(成员变量),因为如果存在成员变量,可能发生多线程同时访问该资源时,都来操作它,照成数据的不一致,因此产生线程安全问题)
以上方法主要优点如下
1. 第一Servlet单实例减少了产生servlet的开销
1. 第二:通过线程池来响应多个请求,提高了请求的响应时间;
1. 第三Servlet容器并不关心到达的Servlet请求访问的是否是同一个Servlet还是另一个Servlet直接分配给它一个新的线程如果是同一个Servlet的多个请求那么Servlet的service方法将在多线程中并发的执行
1. 第四每一个请求由ServletRequest对象来接受请求由ServletResponse对象来响应该请求
## 2. Tomcat
1. 尽管tomcat非常灵活而强大可以作为web应用服务器启动端口使用Socket进行网络通信实现Web服务器通过多线程的方式提供请求并发处理能力。
2. 但是tomcat首先是一个Servlet容器,Servlet容器可以对Servlet进行管理控制其生命周期。使其可以专注于自己应该做的事情不需要考虑端口啊多线程啊socket之类的东西也使得Servlet在各种环境下具有适应性。一个基本的流程tomcat接收请求找到合适的Servlet来处理请求如果该Servlet没加载就顺便编译加载到JVM,如果加载了就调init方法初始化调Service方法处理request并返回responser观测Servlet状态变化在结束时调用destory方法。
## 3. SpringMVC
任何Spring Web的entry point都是servlet。
spring的核心就是通过依赖注入、面向切面编程aop、和模版技术解耦业务与系统服务消除重复代码。借助aop可以将遍布应用的关注点如事物和安全从它们的应用对象中解耦出来。
### Bean的声明周期
![](image/2023-10-22-22-32-40.png)
![](image/2023-10-22-22-32-54.png)
![](image/2023-10-22-22-33-02.png)
## 4. 三者关系
1. Tomcat和jettey类似都是HTTP服务器和Servlet容器负责给类似Spring这种servlet提供一个运行的环境其中Http服务器与Servlet容器的功能界限是可以把HTTP服务器想象成前台的接待负责网络通信和解析请求Servlet容器是业务部门负责处理业务请求。
2. Tomcat和Servlet作为Http服务器和Servlet容器的结合可以接受网络http请求解析为Servlet规范的请求对象和响应对象。比如HttpServletRequest对象是Tomcat提供的Servlet是规范Tomcat是实现规范的Servlet容器SpringMVC是处理Servlet请求的应用其中DispatcherServlet实现了Servlet接口Tomcat负责加载和调用DispatcherServlet。同时DispatcherServlet有自己的容器SpringMVC容器这个容器负责管理SpringMVC相关的bean比如Controler和ViewResolver等。同时Spring中还有其他的Bean比如Service和DAO等这些由全局的Spring IOC容器管理因此Spring有两个IOC容器。
3. JavaEE中如果只是使用spring(不包含springmvc)那么是tomcat容器解析xml文件通过反射实例化对应的类根据这些servlet规范实现类触发对应的代码处理逻辑这个时候tomcat负责http报文的解析和servlet调度的工作。
4. 如果使用spring mvc那么tomcat只是解析http报文然后将其转发给dispatchsetvlet然后由springmvc根据其配置实例对应的类执行对应的逻辑然后返回结果给dispatchservlet最后由它转发给tomcat,由tomcat负责构建http报文数据。
### Web服务器的实现方案
实现web服务有很多种方案。为了能够模块化每一个功能将整个web服务器分为一下三个部分。大致分为一下三个步骤
1. Http服务器监听端口、解析Http协议的数据包将处理后的数据包转换成Http格式的数据包返回给操作系统。**提供并发处理请求的能力**,这种并发能力可以基于多种不同的技术实现(多线程、异步非阻塞),但都必须遵循标准。
2. 服务路由映射将解析后的数据包转换为Java类型的请求通过Url Mapping到指定的业务处理器上。
3. 业务逻辑处理:根据请求的具体内容,处理业务需求,返回处理结果。
其中典型的实现方案包括一下几种:
1. Servlet with JavaEE。基于Servlet标准提供Web服务其主要实现包括原生的Servlet服务器。Tomcat、Jetty等Http服务器和Servlet容器实现客户实现的Servlet中实现了业务逻辑处理。
1. Tomcat实现了Http服务器
2. Tomcat实现了Servlet容器在Servlet容器中实现了服务路由映射。
3. 在不同Servlet中实现了业务逻辑处理。
2. Servlet with SpringMVC中。和SpringMVC通过封装Servlet实现的Web服务器+SpringMVC提供的服务路由映射和业务逻辑处理能力
1. Tomcat实现了Http服务器
2. Tomcat实现了Servlet容器将所有请求/**映射到同一个Spring的DispatcherServlet上。
3. SpringMVC实现了服务路由映射通过内置的服务路由映射机制路由到具体的业务代码和Servlet处理业务需求。
3. WebFlux With Netty。底层是Netty通过异步非阻塞的方式实现的web服务器。异步非阻塞不是指在调用链上的某个节点而是所有节点都是异步非阻塞的。
1. Netty实现了Http服务器
2. SpringMVC实现了服务路由映射
3. 用户自己实现了业务逻辑处理
4. Jax-RS with Resteasy/Jessy。底层也是Netty通过多线程阻塞的方式实现web服务器。
1. Netty实现了Http服务器
2. Jax-RS实现了服务路由映射和过滤器的标准、Resteasy提供了具体实现。
3. 用户自己实现了业务逻辑处理。

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

View File

@@ -4,6 +4,11 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<groupId>org.example</groupId>
<artifactId>SpringBootSourceCode</artifactId>
<version>1.0-SNAPSHOT</version>
@@ -19,12 +24,20 @@
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>

View File

@@ -1,13 +1,18 @@
package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Hello world!
*
*/
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class);
System.out.println( "Hello World!" );
}
}

View File

@@ -0,0 +1,115 @@
package org.example.config;
import lombok.Data;
import org.apache.catalina.Group;
import org.apache.catalina.Role;
import org.apache.catalina.User;
import org.apache.catalina.UserDatabase;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Iterator;
@Data
@Configuration
public class MyConfiguration {
@Bean
public User getUser(){
ResourceProperties resourceProperties = new ResourceProperties();
resourceProperties.getChain();
return new User() {
@Override
public String getFullName() {
return null;
}
@Override
public void setFullName(String s) {
}
@Override
public Iterator<Group> getGroups() {
return null;
}
@Override
public String getPassword() {
return null;
}
@Override
public void setPassword(String s) {
}
@Override
public Iterator<Role> getRoles() {
return null;
}
@Override
public UserDatabase getUserDatabase() {
return null;
}
@Override
public String getUsername() {
return null;
}
@Override
public void setUsername(String s) {
}
@Override
public void addGroup(Group group) {
}
@Override
public void addRole(Role role) {
}
@Override
public boolean isInGroup(Group group) {
return false;
}
@Override
public boolean isInRole(Role role) {
return false;
}
@Override
public void removeGroup(Group group) {
}
@Override
public void removeGroups() {
}
@Override
public void removeRole(Role role) {
}
@Override
public void removeRoles() {
}
@Override
public String getName() {
return null;
}
};
}
}

View File

@@ -0,0 +1,17 @@
package org.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
public class HelloController {
@ResponseBody
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String hello(){
return "Hello World!";
}
}

View File

@@ -0,0 +1,43 @@
package org.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Controller
public class TestController {
@GetMapping("/params")
public String helloWorld(Map<String,String> map,
Model model,
HttpServletRequest request,
HttpServletResponse response){
map.put("map","123");
model.addAttribute("model","model123");
request.setAttribute("request","request123");
Cookie cookie = new Cookie("cookie","cookie123");
return "forward:/success";
}
@ResponseBody
@GetMapping("/success")
public Map<String,Object> success(HttpServletRequest request){
Map<String,Object> map= new HashMap<>();
map.put("map",request.getAttribute("map"));
map.put("model",request.getAttribute("model"));
map.put("request",request.getAttribute("request"));
return map;
}
}

View File

@@ -30,7 +30,7 @@ springboot + springcloud
### 云原生
利用云技术构建、部署、运维应用程序的技术。
1. 服务自愈
2. 弹性伸缩
3. 服务隔离

View File

@@ -273,7 +273,3 @@ cmd+U 父项目
它是实现了BeanPostProcessor接口在bean被实例化后会调用后置处理递归的查找属性通过反射注入值对大多数属性而言强制需提供其setter和getter方法。
## 4 启动过程

View File

@@ -2,8 +2,8 @@
> * bean配置文件spring配置bean的文件。
> * java配置文件通过@Configuration加载
> * 原生配置文件xml定义的配置文件
> * java Bean配置文件,通过@Configuration加载
> * XML Bean配置文件xml定义的配置文件
> * 属性配置文件spring配置key-value的文件
@@ -15,12 +15,29 @@
3. Spring标准注解@Bean @Component @Serivce,@Controller,@Configuration,@Import,@Autowire
4. springboot补充注解
### @SpringbootConfiguration开启自动配置
### 导入容器
spring导入容器主要有三种方式
1. 标准组件:@Compoent@Repository@Service@Controller四种类型的组件。**通过@ComponentScan定义的扫描路径**扫描后导入到Spring容器中。
2. 自动配置:通过@Configuration定义的配置类。**通过@EnableAutoConfiguration注解定义扫描** **主类包路径下的所有配置类** 和 SpringFactories加载的**spirng配置文件中声明的全类名**扫描后导入到Spring容器中。
3. 导入配置:通过@Import和@ImportResource注解导入类。通过这两个注解可以**间接导入第三方、XML Bean配置文件中**的组件。
> 这三种导入的组件都有其开启的方式,定义了扫描的范围。如果允许以上注解标识的组件导入到容器中,就必须满足其开启条件。
导入容器还需要注意一下规则
* 扫描到组件并不代表一定加载组件,导入组件都受到@Condition注解的过滤和影响
* 导入组件可以规定顺序,通过@AutoConfigurOrder@AutoConfigureAfter
### @SpringbootConApplication开启自动配置
```
@SpringBootConfiguration springboot启动
@EnableAutoConfiguration 通过properties自动加载
@ComponentScan("com.atguigu.boot")扫描范围
===>
@SpringBootApplication
```
springboot项目中的启动注解。
@@ -131,7 +148,7 @@ public class MyConfig {
* @Component和@Import是不会冲突,如果已经通过@Component导入的bean(优先级更高)不会通过@Import重复导入
### ImportResource
### @ImportResource
导入原生配置文件
```xml

View File

@@ -92,13 +92,14 @@ this.environment.containsProperty("spring.jpa.database-platform")
引入配置提示的依赖。并在maven插件中将该依赖排除。
```xml
//配置提示,@ConfigurationProperties
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
//打包的时候将处理器排除掉
<build>
<plugins>
<plugin>

View File

@@ -154,11 +154,11 @@ SpringApplication.run()到底干了什么
* @SpringBootConfiguration,就是一个@Configuration配置类。定义这是一个配置类。
* @ComponentScan指定包扫描
* @ComponentScan指定包扫描。**扫描@Controller@Component@Repository@Service注解定义的组件,控制翻转放入到容器当中**
* @EnableAutoConfiguration
* @AutoConfigurationPackage自动配置包将该包下的所有配置类导入进去。
* @Import(AutoConfigurationPackages.Register.class)利用register将指定的包下的所有组件注册到容器中。所以默认包路径是Main程序所在的包。
* @Import(AutoConfigurationImportSelect.class)获取所有导入到容器中的配置类。利用Spring工厂加载器从spring-boot-autoconfigure./META-INF/spring-factories中加载文件。Springboot一启动就要加载的所有配置类。
* @AutoConfigurationPackage自动配置包
* @Import(AutoConfigurationPackages.Register.class)利用register**将指定的包下的所有配置类注册到容器中**。所以默认包路径是Main程序所在的包。将该包下的所有配置类放入到容器当中。包括@SpringBootConfiguration注解的启动类这也是系统加载的第一个Configuration组件放入到容器当中。
* @Import(AutoConfigurationImportSelect.class)获取所有导入到容器中的配置类。**利用Spring工厂加载器从spring-boot-autoconfigure./META-INF/spring-factories中加载文件。Springboot一启动就要加载的所有配置类。会根据@Condition系列按需加载。**
![](image/2023-01-09-10-44-48.png)
@@ -170,7 +170,7 @@ SpringApplication.run()到底干了什么
* 只要用户配置了,以用户的优先。
* 定制化用自己的Bean替换底层的组件
* 用户去看这个组件获取的配置文件是什么值就去修改什么值。
* 自动装配规则如果一个SpringBoot配置类只有一个默认的有参构造器则该构造器的所有参数都会从容器中进行自动装配。相当于添加了@Autowire
### 需要怎么使用
@@ -186,4 +186,7 @@ SpringApplication.run()到底干了什么
### debug=true
开启debug=true模式可以快速看到各个条件注解哪一个满足了哪一个没有满足
![](image/2023-10-26-23-50-18.png)

View File

@@ -1,49 +1,795 @@
# web开发
## 概述
将springboot和springmvc结合起来的讲解
## 1 静态资源访问
### 静态资源访问
1. 默认的路径,类路径下/static /public /resources META_INFO/resources。使用当前项目的根路径/
2. 改变默认的静态路远路径spring.resources.static-locations=classpath:/haha
3. 默认是没有访问前缀通过配置添加静态资源访问前缀spring.mvc.static-path-pattern=/static/**
4. webjars支持将前端依赖以jar的形式导入到项目中在jar包的资源路径下有对应的前端资源其路径变为/webjars/** 添加依赖中指定的地址。(前后端分离一般不会使用这个方法)
静态资源访问的原理:
1. 静态资源映射的地址是/**
2. 如果同时存在动态资源和静态资源,则首先访问动态资源
### 欢迎页和图表
1. 欢迎页的默认名称 index.html 放到静态资源路径下,访问根路径会自动跳转
2. 图表的的默认名称 favorite.ico 放到静态资源路径下可以自动加载
### 自动加载原理
> 在使用springboot框架进行开发的时候可以使用这个设计模式实现Bean配置和属性配置。——SpringMvcAutoConfiguration
> 1. 首先定义一个场景 自动配置列,在某些条件下打开、配置自动加载的顺序
> 2. 通过EnableConfiguration指定绑定的 属性配置文件
> 3. 在内部初始化一系列相关的Bean容器用来支持该场景的操作。
1. 与Web相关的自动配置类WebMVCAutoConfiguration。
1. 定义了加载的条件WebServlet场景且没有开启全部接管模式。
2. 定义了配置加载的顺序在某些Configuration之后进行加载。
```java
@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 {
```
2. 静态内部配置类WebMvcAutoConfigurationAdapter给容器中配置了什么静态内部类绑定了属性配置文件。加载了两个properties文件。
```java
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
```
3. 加载了相关的属性文件。通过静态内部类的方式降低了类的嵌套深度,定义了多个属性。
```java
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
```
4. 内部自动注入创建了该Bean。
* resourceProperties spring.resources 相关的配置文件
* mvcProperties spring.mvc 相关的属性配置
* beanFactory IOC工厂
* 其中ObjectProvider用于通过编程的方式实现不唯一的类型注入。也可以直接注入一个List
* messageConvertersProvider 找到所有的MessageConvertor
* resourceHandlerRegistrationCustomizerProvider 用户自定义的资源处理器
* dispatcherServletPath 定义servlet的context的路径
* servletRegistrations 给应用添加java web原生的servlet、listener、filter
> 自动装配规则如果一个SpringBoot配置类只有一个默认的有参构造器则该构造器的所有参数都会从容器中进行自动装配。相当于添加了@Autowire
```java
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
ObjectProvider<DispatcherServletPath> dispatcherServletPath,
ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.dispatcherServletPath = dispatcherServletPath;
this.servletRegistrations = servletRegistrations;
}
```
5. 在该类中创建了Web场景下所需要的各种组件
```java
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
@Override
@SuppressWarnings("deprecation")
public void configurePathMatch(PathMatchConfigurer configurer) {
}
private boolean singleDispatcherServlet() {
}
@Override
@SuppressWarnings("deprecation")
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
}
@Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
}
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
}
@Override
public void addFormatters(FormatterRegistry registry) {
}
//resource 静态资源所有的配置
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
private Integer getSeconds(Duration cachePeriod) {
}
private void customizeResourceHandlerRegistration(ResourceHandlerRegistration{
}
@Bean
@ConditionalOnMissingBean({ RequestContextListener.class, RequestContextFilter.class })
@ConditionalOnMissingFilterBean(RequestContextFilter.class)
public static RequestContextFilter requestContextFilter() {
}
```
6. 静态资源加载的所有规则。**spring所有的加载项都可以通过这种代码调试的方法找到对应的开关或配置**。
1. addmaping 标识是否开启静态资源。
2. 配置/webjars/**添加了所有classpath:/webjars/路径下的静态资源
3. 配置了/**添加了所有classpath:/static/ public/下的资源
```java
//resource 静态资源所有的配置
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
```
## 2 请求映射处理
## Springmvc的研究对象
### 请求映射过程
1. @xxxMapping:@RequestMapptin @GetMapping @PostMapping @DeleteMapping @PutMapping
2. 支持rest请求的配置。只有HTML表单需要进行如下操作如果是xhr则可以直接发送Delete和Pub请求
1. 表单中添加_method隐藏参数指定POST请求真正的请求方式
2. 开启页面表单Rest支持 spring.mvc.hiddenmethod.filter=true
```java
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
```
### 请求映射原理
1. dispatch方法是请求映射的核心类。
```java
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're 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);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
```
2. 请求映射的原理 List<Map<uri,Handler>> HandlerMappingList;多个不同的HandlerMapping按照优先级进行匹配。
* 系统初始化了五个HandlerMapping其中RequestMappingHandlerMapping扫描Requestmapping注解进行请求映射。WelcomePageHandlerMapping 定义了欢迎页的映射。
* 如果需要自定义的映射请求处理可以自定义的HandlerMapping进行扩展。
![](image/2023-10-29-14-19-35.png)
3. RequestMappingHandlerMapping保存了RequestMapping注解保存的uri和方法的映射。mappingRegistry保存了映射信息。
![](image/2023-10-29-17-09-39.png)
### 请求处理的过程(类层侧结构)
```plantuml
@startuml
object HttpServlet{
doGet
doPost
doPut
doDelete
}
note bottom : 这是所有http请求的入口。\
\n 可以通过子类,不断丰富和具体要执行的内容。
package javax/servlet{
interface Servlet{
init()
getServletConfig()
getServletInfo()
destroy()
service()
}
object HttpServletBean{
class GenericServlet implements Servlet{
doGet()
}
note right: 实现了默认的空方法
package http{
class HttpServlet{
doGet
doPost
doPut
doDelete
doHead
doTrace
}
note right : http类型的servlet\n\
将servlet具体位http的post、get等方法
GenericServlet <|-- HttpServlet
}
}
object FrameworkServlet{
doGet--> processRequest
doPost --> processRequest
doPut --> processRequest
doDelete --> processRequest
processRequest -->doService
namespace org/springframwork/web/servlet{
class HttpServletBean{
}
note right: 配置了环境信息
HttpServlet <|-- HttpServletBean
class FrameworkServlet extends HttpServletBean{
doGet--> processRequest
doPost --> processRequest
doPut --> processRequest
doDelete --> processRequest
processRequest -->doService
}
class DispatcherServlet extends FrameworkServlet{
doService -->doDispatcher
doDispatcher
}
}
object DispatcherServlet{
doService -->doDispatcher
doDispatcher
}
FrameworkServlet -|> HttpServlet : 重写do方法全部调用\nprocessRequest->doService
FrameworkServlet -> DispatcherServlet : 重写doService方法\n调用doDispatch进行处理
@enduml
```
## 3 请求参数处理
### 注解请求参数
1. 测试springmvc常用的参数注解 @PathVariable@RequestHeader@ModelAttribute@RequestParam@MatrixVariable@CookieValue@RequestBody
```java
@RestController
public class ParameterTestController {
// car/2/owner/zhangsan
@GetMapping("/car/{id}/owner/{username}")
public Map<String,Object> getCar(@PathVariable("id") Integer id,
@PathVariable("username") String name,
@PathVariable Map<String,String> pv,
@RequestHeader("User-Agent") String userAgent,
@RequestHeader Map<String,String> header,
@RequestParam("age") Integer age,
@RequestParam("inters") List<String> inters,
@RequestParam Map<String,String> params,
@CookieValue("_ga") String _ga,
@CookieValue("_ga") Cookie cookie){
Map<String,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){
Map<String,Object> map = new HashMap<>();
map.put("content",content);
return map;
}
//1、语法 请求路径:/cars/sell;low=34;brand=byd,audi,yd
//2、SpringBoot默认是禁用了矩阵变量的功能
// 手动开启原理。对于路径的处理。UrlPathHelper进行解析。
// removeSemicolonContent移除分号内容支持矩阵变量的
//3、矩阵变量必须有url路径变量才能被解析
@GetMapping("/cars/{path}")
public Map carsSell(@MatrixVariable("low") Integer low,
@MatrixVariable("brand") List<String> brand,
@PathVariable("path") String path){
Map<String,Object> map = new HashMap<>();
map.put("low",low);
map.put("brand",brand);
map.put("path",path);
return map;
}
// /boss/1;age=20/2;age=10
@GetMapping("/boss/{bossId}/{empId}")
public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
@MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
Map<String,Object> map = new HashMap<>();
map.put("bossAge",bossAge);
map.put("empAge",empAge);
return map;
}
}
```
2. HandlerMappingAdapter用来对Handler的处理过程进行封装。其中RequestHandlerMapping就是用来封装@RequestMapping的映射的HandlerFunctionAdapter是用来支持函数映射的。
![](image/2023-10-29-16-59-59.png)
3. 调用adapter的handler方法进行真正的处理逻辑。最终还是调用RequestMappingHandler的handler方法进行处理
![](image/2023-10-29-17-15-54.png)
![](image/2023-10-29-17-17-26.png)
4. 在上述方法中调用参数解析器进行进行参数解析。可以看到springboot支持的所有的参数解析方法。
![](image/2023-10-29-17-19-03.png)
![](image/2023-10-29-17-20-40.png)
5. 同时方法中也可以看到方法支持的返回值处理器。 spring中很多地方都遵循这种设计模式策略模式定义一个共同的接口、给接口定义多种不同的实现、建立条件和实现之间的映射关系从而可以面向不同的场景和进行灵活的扩展。
![](image/2023-10-29-17-22-57.png)
7. 最终通过反射调用具体的方法。在真正的调用之前首先要调用参数解析方法解析参数的值。
```java
--> handler
--> invocableMethod.invokeAndHandle(webRequest, mavContainer);
--> InvocableHandlerMethod.java:Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
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;
}
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) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
```
8. 解析过程如图所示遍历所有的参数解析器找到支持的解析器为了加快运行病针对这个参数建立缓存。为什么不在一开始就建立mapping映射呢而是要在运行中建立映射一方面为了增加启动速度避免在启动的时候加载大量无用的数据进来另一方面有一些映射的建立是动态的过程需要调用目标模块是否支持的方法必须在调用后才才能确定映射到具体哪一个解析器。
```java
@Nullable
private 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);
break;
}
}
}
return result;
}
```
### 传入ServletAPI
1. 支持的request类型。WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
ServletRequestMethodArgumentResolver 以上的部分参数
```java
@Override
public 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) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
}
```
2. 与注解同理,都是通过参数解析器,解析不同的请求。
### 复杂参数
1. 支持的复杂参数如下Map、Modelmap、model里面的数据会被放在request的请求域 request.setAttribute、Errors/BindingResult、RedirectAttributes 重定向携带数据、ServletResponseresponse、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder。
2. 其中Map/Model都是可以给request域中放数据与request.setAttribute()类似。可以通过request.getAttribute()从请求域中获取;
1. map/model 都是调用相同的方法从底层获取到值的mavContainer.getModel。而且两者获取到的是同一个对象。
3. 当方法处理完成后所有的数据都会放单modelAndViewContainer中。当返回值是一个字符串的时候方法不会直接返回给前端而是一个view的地址并且携带了数据model完成后续渲染工作。
### 自定义对象
1. 通过ServletModelAttributeMethodProcessor参数解析器处理自定义了类型的参数。
2. 设计了大量的转换器,将对类型进行转换。主要是为了封装转换器中的 src 和 dsr方法封装成一个重载的方法然后方便获取不同类型的转换器。
1. 当前解析器是否支持解析这种参数
2. 支持就调用 resolveArgument
3. WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
1. WebDataBinder :web数据绑定器将请求参数的值绑定到指定的JavaBean里面
2. WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
GenericConversionService在设置每一个值的时候找它里面的所有converter那个可以将这个数据类型request带来参数的字符串转换到指定的类型JavaBean -- Integer
![](image/2023-10-29-22-16-35.png)
```java
============InvocableHandlerMethod==========================
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;
}
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) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
@Nullable
private 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);
break;
}
}
}
return result;
}
```
* 判断是否符合 Convertor的条件
```java
public static boolean isSimpleValueType(Class<?> type) {
return (Void.class != type && void.class != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||
Enum.class.isAssignableFrom(type) ||
CharSequence.class.isAssignableFrom(type) ||
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
Locale.class == type ||
Class.class == type));
}
```
* 执行自定义类型参数的参数解析
```java
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
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 instance
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
bindingResult = ex.getBindingResult();
}
}
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)) {
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.Optional
if (!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 model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
```
> spring到处都是这种设计模式设置多个不同的处理器然后通过遍历循环找到支持当前类型的处理器如果存在效率问题则直接将当前条件对应的处理器缓存下来。使用到的地方包括
> * 参数解析器*ParamResolver
> * 返回值处理器*ResultProcessor
> * 类型转换器*Convertor
> * 映射处理器*MappingHandler
>
> 特别疑惑这种通过循环遍历的方式查找对应的处理器的方法,是不是存在效率问题呢?
### 自定义数据转换器
* 例如一下是自定义数据转换器的过程。
```java
//1、WebMvcConfigurer定制化SpringMVC的功能
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
// 不移除;后面的内容。矩阵变量功能就可以生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String, Pet>() {
@Override
public Pet convert(String source) {
// 啊猫,3
if(!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;
}
});
}
};
}
```

View File

@@ -0,0 +1,8 @@
#include <stdio.h>
int main(int argc, char **argv){
int i = 10;
char *b = &i;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

17
hello.js Normal file
View File

@@ -0,0 +1,17 @@
function calculateDaysBetweenDates(begin, end) { const oneDay = 24 * 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
const firstDate = new Date(begin);
const secondDate = new Date(end);
const diffDays = Math.round(Math.abs((firstDate - secondDate) / oneDay));
return diffDays;
}
function testThis() {
console.log(this); // logs the value of "this" in the current context
}
// Example usage:
testThis(); // logs the global object (e.g. "window" in a browser)
const obj = { name: "GitHub Copilot" };
obj.testThis = testThis;
obj.testThis(); // logs the "obj" object

View File

@@ -3,4 +3,6 @@
git 版本控制
GitHub 网站托管
markdown 内容编辑器
jekyll 模板样式
jekyll 模板样式
make a index page with good logo and titild