spring boot and mvc
111
Java/Java学习路线.md
@@ -1,9 +1,10 @@
|
||||
## Java学习路线
|
||||
> 生命的长度是有限的,但Java的知识是无限的!
|
||||
## Java 学习路线
|
||||
|
||||
> 生命的长度是有限的,但 Java 的知识是无限的!
|
||||
|
||||
总共包括六个主要的部分。学完就能毕业啦。开始吧。
|
||||
### Java的学习路线(视频打卡系列)
|
||||
|
||||
### Java 的学习路线(视频打卡系列)
|
||||
|
||||
- [ ] 基础知识(学习方式——阅读书籍)
|
||||
- [ ] 数据库
|
||||
@@ -11,30 +12,29 @@
|
||||
- [ ] 计算机网络
|
||||
- [ ] 数据结构与算法
|
||||
- [ ] 编译原理
|
||||
- [ ] Java基础教程(Java的基本语法和使用及原理,晚上自学,第一周学完)
|
||||
- [X] Java语言基础。语言语法。(分五个阶段完成把,当前第一阶段已经完成)
|
||||
- [ ] Java高级操作。JDK IO操作/并发编程/网络编程
|
||||
- [ ] Javaweb开发。Servlet和JSP相关的老技术。知道就行
|
||||
- [ ] Java基本原理。JVM底层的原理和技术
|
||||
- [ ] Java架构模式。面向对象和设计模式
|
||||
- [ ] Java网站开发(JavaWeb相关的技术知识。)
|
||||
- [ ] Java 基础教程(Java 的基本语法和使用及原理,分五个阶段完成)
|
||||
- [x] Java 语言基础。语言语法。
|
||||
- [ ] Java 高级操作。JDK 标准库/集合类/IO 操作/并发编程/网络编程
|
||||
- [ ] Javaweb 开发。Servlet 和 JSP 相关的老技术。知道就行
|
||||
- [ ] 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核心技术卷1(12版)
|
||||
- [ ] Java核心技术卷2(12版)
|
||||
- [ ] Java 编程思想(4 版)
|
||||
- [ ] Java 核心技术卷 1(12 版)
|
||||
- [ ] Java 核心技术卷 2(12 版)
|
||||
- [ ] Effective Java
|
||||
- [ ] 深入理解Java虚拟机
|
||||
|
||||
- [ ] 深入理解 Java 虚拟机
|
||||
|
||||
第二阶段:向上进阶五本
|
||||
|
||||
- [ ] ON JAVA基础卷
|
||||
- [ ] ON JAVA进阶卷
|
||||
- [ ] Java设计模式及实践(这些东西还是得自己手敲一遍)
|
||||
- [ ] Java并发编程的艺术、并发编程实践、高并发编程详解(机械工业出版三件套)
|
||||
- [ ] Spring官方文档。全套阅读。
|
||||
|
||||
- [ ] ON JAVA 基础卷
|
||||
- [ ] ON JAVA 进阶卷
|
||||
- [ ] Java 设计模式及实践(这些东西还是得自己手敲一遍)
|
||||
- [ ] Java 并发编程的艺术、并发编程实践、高并发编程详解(机械工业出版三件套)
|
||||
- [ ] Spring 官方文档。全套阅读。
|
||||
|
||||
第三阶段:云原生三本
|
||||
|
||||
- [ ] 云原生技术
|
||||
- [ ] 服务网格
|
||||
|
||||
|
||||
|
||||
|
||||
> 一份可以参考的文档。其中大数据、网络安全和扩展篇不学。
|
||||
> 
|
||||
|
||||
|
||||
> 找到了两个很不得了的东西。如果有时间,可以把这两个文档全部重新看一遍。完全有书籍的质量的基础知识介绍。常看常新。自己接下来的笔记就从这上边找吧。
|
||||
>
|
||||
>
|
||||
> 入门小站。https://rumenz.com/
|
||||
> BestJavaer。https://github.com/crisxuan/bestJavaer
|
||||
>
|
||||
112
Java/Java网站开发/Servlet容器.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Servlet容器、Http服务器:Servlet、Tomcat、SpringMVC的关系
|
||||
|
||||
> 参考文章
|
||||
> https://blog.csdn.net/cristianoxm/article/details/121268913
|
||||
## 1. Servlet
|
||||
|
||||
### 概念
|
||||

|
||||
|
||||
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. 被垃圾回收器回收
|
||||
|
||||
### 请求处理
|
||||
|
||||

|
||||
|
||||
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的声明周期
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## 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. 用户自己实现了业务逻辑处理。
|
||||
BIN
Java/Java网站开发/image/2023-10-22-21-51-33.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
Java/Java网站开发/image/2023-10-22-22-00-16.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
Java/Java网站开发/image/2023-10-22-22-32-40.png
Normal file
|
After Width: | Height: | Size: 235 KiB |
BIN
Java/Java网站开发/image/2023-10-22-22-32-54.png
Normal file
|
After Width: | Height: | Size: 572 KiB |
BIN
Java/Java网站开发/image/2023-10-22-22-33-02.png
Normal file
|
After Width: | Height: | Size: 313 KiB |
@@ -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>
|
||||
|
||||
@@ -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!" );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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!";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ springboot + springcloud
|
||||
|
||||
|
||||
### 云原生
|
||||
|
||||
利用云技术构建、部署、运维应用程序的技术。
|
||||
1. 服务自愈
|
||||
2. 弹性伸缩
|
||||
3. 服务隔离
|
||||
|
||||
@@ -273,7 +273,3 @@ cmd+U 父项目
|
||||
|
||||
它是实现了BeanPostProcessor接口,在bean被实例化后,会调用后置处理,递归的查找属性,通过反射注入值,对大多数属性而言强制需提供其setter和getter方法。
|
||||
|
||||
|
||||
|
||||
## 4 启动过程
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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系列按需加载。**
|
||||
|
||||

|
||||
|
||||
@@ -170,7 +170,7 @@ SpringApplication.run()到底干了什么
|
||||
* 只要用户配置了,以用户的优先。
|
||||
* 定制化,用自己的Bean替换底层的组件
|
||||
* 用户去看这个组件获取的配置文件是什么值就去修改什么值。
|
||||
|
||||
* 自动装配规则:如果一个SpringBoot配置类只有一个默认的有参构造器,则该构造器的所有参数都会从容器中进行自动装配。相当于添加了@Autowire
|
||||
|
||||
### 需要怎么使用
|
||||
|
||||
@@ -186,4 +186,7 @@ SpringApplication.run()到底干了什么
|
||||
|
||||
|
||||
|
||||
### debug=true
|
||||
|
||||
开启debug=true模式可以快速看到各个条件注解,哪一个满足了,哪一个没有满足!
|
||||

|
||||
@@ -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进行扩展。
|
||||

|
||||
|
||||
3. RequestMappingHandlerMapping,保存了RequestMapping注解保存的uri和方法的映射。mappingRegistry保存了映射信息。
|
||||

|
||||
|
||||
|
||||
### 请求处理的过程(类层侧结构)
|
||||
|
||||
```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是用来支持函数映射的。
|
||||
|
||||

|
||||
|
||||
3. 调用adapter的handler方法进行真正的处理逻辑。最终还是调用RequestMappingHandler的handler方法进行处理
|
||||
|
||||

|
||||

|
||||
|
||||
4. 在上述方法中,调用参数解析器进行进行参数解析。可以看到springboot支持的所有的参数解析方法。
|
||||
|
||||

|
||||

|
||||
|
||||
5. 同时方法中也可以看到方法支持的返回值处理器。 spring中很多地方都遵循这种设计模式(策略模式),定义一个共同的接口、给接口定义多种不同的实现、建立条件和实现之间的映射关系,从而可以面向不同的场景和进行灵活的扩展。
|
||||
|
||||

|
||||
|
||||
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、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、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)
|
||||

|
||||
|
||||
```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;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
8
Spring/Springboot/hello.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv){
|
||||
|
||||
int i = 10;
|
||||
char *b = &i;
|
||||
|
||||
}
|
||||
BIN
Spring/Springboot/image/2023-10-26-23-50-18.png
Normal file
|
After Width: | Height: | Size: 328 KiB |
BIN
Spring/Springboot/image/2023-10-29-14-19-35.png
Normal file
|
After Width: | Height: | Size: 497 KiB |
BIN
Spring/Springboot/image/2023-10-29-16-59-59.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
Spring/Springboot/image/2023-10-29-17-09-39.png
Normal file
|
After Width: | Height: | Size: 993 KiB |
BIN
Spring/Springboot/image/2023-10-29-17-15-54.png
Normal file
|
After Width: | Height: | Size: 411 KiB |
BIN
Spring/Springboot/image/2023-10-29-17-17-26.png
Normal file
|
After Width: | Height: | Size: 413 KiB |
BIN
Spring/Springboot/image/2023-10-29-17-19-03.png
Normal file
|
After Width: | Height: | Size: 433 KiB |
BIN
Spring/Springboot/image/2023-10-29-17-20-40.png
Normal file
|
After Width: | Height: | Size: 473 KiB |
BIN
Spring/Springboot/image/2023-10-29-17-22-57.png
Normal file
|
After Width: | Height: | Size: 672 KiB |
BIN
Spring/Springboot/image/2023-10-29-22-16-35.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
17
hello.js
Normal 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
|
||||
@@ -3,4 +3,6 @@
|
||||
git 版本控制
|
||||
GitHub 网站托管
|
||||
markdown 内容编辑器
|
||||
jekyll 模板样式
|
||||
jekyll 模板样式
|
||||
|
||||
make a index page with good logo and titild
|
||||