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

springMVC学习笔记-请求映射,参数绑定,响应,restful,响应状态码,springMVC拦截器

目录

概述

springMVC做了什么

springMVC与struts2区别

springMVC整个流程是一个单向闭环

springMVC具体的处理流程

springMVC的组成部分

请求映射

@RequestMapping

用法

属性

1.value

2.method

GET方式和POST方式

概述

HTTP给GET和POST做了哪些规定

GET方式,url参数中有+、空格、=、%、&、#等特殊符号的问题解决

参数绑定

1.默认支持的参数类型

2.绑定简单类型自动绑定

3.@RequestParam手动绑定

属性

4.实体类属性自动绑定

5.Map绑定

6.绑定数组类型

7.绑定List类型

8.绑定日期类型

9.@RequestBody

绑定String,直接接收到请求体JSON串

绑定Map

绑定实体类

绑定数组/集合

@RequestBody与@RequestParam()同时使用

实际上,@RequestBody之外的参数可以是2-8上提到的任意方式,相当于请求体和url上的参数是隔离的

总结

JSON

1.什么是JSON

2.结构

响应(接口返回值)

转发和重定向

void

@ResponseBody

restful风格

特点

URI等价于资源

统一HTTP方法分类

统一返回数据格式

从URL上获取参数

POJO

响应状态码

1xx

2xx

3xx

4xx

5xx

springMVC拦截器

创建和配置

配置多个拦截器时的执行顺序


概述

springMVC是spring框架体系的一部分,功能和struts2类似,可以完美替代struts2,可以认为struts2已经被淘汰

随着前后端分离和springboot的普及,我们不再需要关注xml配置,也不关注Model,View相关的接口返回值,此处只关注springMVC本身

springMVC做了什么

简化了请求的接收和处理,不再需要开发者针对每个请求编写Servlet,重写方法接收请求,也不再需要关注参数转换,大量减少了代码量

springMVC与struts2区别

1.springmvc入口是一个servlet,即前端控制器;struts2入口是filter过滤器(配置很多拦截器)

2.springmvc基于方法开发,请求参数传递到方法,可以为单例或者多例;struts2是基于类开发,方法只能通过类的参数接收,只能为多例

3.springmvc解析request请求内容,把请求参数和映射方法的形参绑定,将数据封装成ModelAndView返回给前端解析;struts2是通过值栈方式存储请求和响应数据,通过OGNL存取数据

springMVC整个流程是一个单向闭环

用户(输入)--->控制器controller,将用户输入分发给业务模型--->模型model,进行业务逻辑判断,数据库存取--->视图view,根据需要渲染不同的视图--->用户(获得反馈)

springMVC具体的处理流程

1.用户请求-被前端控制器拦截DispatcherServlet

2.DispatcherServlet接收到请求以后调用HandlerMapping处理器映射器

3.HandlerMapping根据请求的url找到对应的Handler处理器,生成处理器对象和拦截器返回给dispatcher

4.DispatcherServlet通过HandlerAdapter处理器适配器调用Handler处理器

5.执行处理器(即Controller)

6.执行结果ModelAndView通过HandlerAdapter返回给DispatcherServlet

7.DispatcherServlet将ModelAndView传给ViewReslover视图解析器

8.ViewReslover解析后返回具体的View给DispatcherServlet

9.DispatcherServlet对View进行渲染(填充数据)

10.DispatcherServlet将最终的结果返回给客户

springMVC的组成部分

部分组件会默认加载,程序员需要开发的是Handler和View,也就是接收请求后的业务处理和前端页面

DispatcherServlet 前端控制器:这是mvc流程的核心,通过DispatcherServlet调用其他组件处理请求,降低了组件间的耦合

HandlerMapping 处理器映射器:根据请求的url找到对应的Handler

Handler 处理器:在DispatcherServlet控制下进行请求的业务操作,这是程序员开发的主要内容

HandlAdapter 处理器适配器:DispatcherServlet通过HandlerAdapter处理器适配器调用Handler处理器

ViewResolver 视图解析器:ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,DispatcherServlet对View进行渲染将处理结果通过页面展示给用户

View 视图:springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等

请求映射

@RequestMapping

定义请求映射规则,接收并处理请求

用法

1.注解在类上:访问该类所有方法都要加上该前置路径

2.注解在方法上:访问该方法的映射路径

属性

1.value

映射路径,如果只有value属性,则value="/xxxx"的value=可以省略

@RequestMapping(value="/xx/xx"),@RequestMapping("/xx/xx")

value的值是数组,可以将多个url映射到同一个方法

@RequestMapping(value={"/xx/xx","xx/xxx"})

2.method

限制请求方法类型

RequestMethod.GET,RequestMethod.POST

@RequestMapping(value = "/xxx/person/get/{id}", method = RequestMethod.GET)
public ApiResult getPersonById(@PathVariable Integer id) {...Person person = ...return ApiResult.success(person);
}

GET方式和POST方式

概述

GET方式和POST方式是HTTP协议中两种发送请求的方式

HTTP是基于TCP/IP的,关于数据如何在网络中传递的协议

因此,GET和POST本质上都是基于TCP/IP的发送请求的方式,在底层上说,并没有本质的区别

GET和POST的区别是由HTTP协议规定的,HTTP这么规定是为了给各式各样的请求分类

换句话说,并不是GET请求和POST有什么区别,你如果想要GET请求使用RequestBody传参,并不是不行,所谓的区别只是HTTP协议做出的规定

HTTP给GET和POST做了哪些规定

参数传递渠道:

GET:拼接到url上

POST:放在请求体中(RequestBody)

长度限制:

GET:由浏览器决定,通常为2k,最多64k

POST:理论上无限制

TCP数据包:

GET:发送一次请求,产生一个TCP数据包

POST:发送两次请求,先发送请求头Header,获取到响应后再发送RequestBody,产生两个TCP数据包,但要注意并不是所有浏览器都严格遵循这样的规定,即有的浏览器POST也只发送一次

GET方式,url参数中有+、空格、=、%、&、#等特殊符号的问题解决

问题:Url出现了有+,空格,/,?,%,#,&,=等特殊符号的时候,可能在服务器端无法获得正确的参数值

问题原因:为何Url中有这些字符就会出现问题,这涉及到URL编码与解码

URL编码与解码,网络标准RFC 1738做了硬性规定:

只有字母和数字[0-9a-zA-Z]、一些特殊符号”$-_.+!*’(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL

这意味着,如果URL中有汉字,等特殊字符的时候,就必须编码后使用。而+,空格,/,?,%,#,&,=,这些字符(不安全),当把他们直接放在Url中的时候,可能会引起解析程序的歧义,因此也必须经过编码才能使用

解决办法:将这些字符转化成服务器可以识别的字符,对应关系如下:

+ URL中+号表示空格 %2B

空格 URL中的空格可以用+号或者编码 %20

/ 分隔目录和子目录 %2F

? 分隔实际的URL和参数 %3F

% 指定特殊字符 %25

# 表示书签 %23

& URL 中指定的参数间的分隔符 %26

= URL 中指定参数的值 %3D

参数绑定

1.默认支持的参数类型

处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值

HttpServletRequest 通过域对象传递参数,包含请求的详细信息,不仅仅是参数

HttpServletResponse 处理响应信息

HttpSession 获取session中存放的对象

2.绑定简单类型自动绑定

整形:Integer、int

字符串:String

单精度:Float、float

双精度:Double、double

布尔型:Boolean、boolean

只要Controller形参和请求参数名称能匹配上就可以接收值

通常使用包装类,因为基本数据类型不能为null值

通常有多个参数,会封装成POJO,可以方便数据的进一步使用

@RequestMapping(value = "/xxx/person/getById", method = RequestMethod.GET)
public ApiResult getPersonById(Integer id) {...Person person = ...return ApiResult.success(person);
}

3.@RequestParam手动绑定

将Controller形参和请求参数名称手动绑定,字段名称不一致时可以这样,但通常不这样

@RequestMapping(value = "/xxx/person/getByName", method = RequestMethod.GET)
public ApiResult getPersonById(@RequestParam(value="paramName")String name) {    //这样就将Controller的形参name和请求传递的参数paramName绑定了...Person person = ...return ApiResult.success(person);
}

属性

value 指定绑定的请求参数名称

required 是否必须有值,false/true,默认是true,此时入参为空会报错TTP Status 400 - Required parameter 'XXXX' is not present

defaultValue 默认值,没有值的时候赋默认值

4.实体类属性自动绑定

请求参数名称和实体类属性名一致,会自动将请求参数赋值给实体类的属性

@RequestMapping("/updateCustomer")
public String update(Customer customer) {...
}

5.Map绑定

@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestParam(required = false) Map<Object, Object> customerParams) {List<Customer> customerList = customerService.getCustomerListPage(customerParams);return ApiResult.success();
}

6.绑定数组类型

页面选中多个checkbox向controller方法传递,本身属于一个form表单,此时需要用数组参数接收该请求参数,参数名是checkbox的name

@RequestMapping(value = "/xxx/person/getByIds")
public ApiResult getPersonById(Integer[] ids) {...List<Person> personList = ...return ApiResult.success(personList);
}

7.绑定List类型

List是一个接口,不能直接实例化,因此无法直接绑定,要使用实现类来绑定,比如ArrayList,如果直接绑定ArrayList,还需要加上@RequestParam,这是由参数解析器决定的;

通常我们不这么做,而是通过将List放入实体类中进行绑定

//通过@RequestParam绑定
@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestParam(required = false) ArrayList<Integer> idList) {List<Customer> customerList = customerService.getCustomerListPage(idList);return ApiResult.success();
}//通过实体类绑定
@RequestMapping(value = "/xxx/person/getByIds")
public ApiResult getPersonById(PersonParams personParams) {...List<Person> personList = ...return ApiResult.success(personList);
}pulic class PersonParams {private List<Integer> idList;public List<Integer> getIdList() {...    }...
}

8.绑定日期类型

日期比较特殊,因为前台控件在提交的时候都是String,所以接收的时候不能直接用日期类型

当然可以自定义转换类来实现自定义参数绑定,但通常不这么做,而是使用实体类接收,在对应的成员上通过注解实现自动转换

@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")

9.@RequestBody

上面的2到8种参数绑定,都需要我们把参数放到请求上,如果我们的参数是放在请求体RequestBody中,会发现接收到的都是null,那么怎么获取RequestBody中的参数呢

通过@RequestBody注解

注意:

请求体RequestBody是以JSON字符串的方式传递

一个接口只能定义一个@RequestBody,因为浏览器只会发送一个RequestBody

@RequestBody虽然不可以定义两个,但除了@RequestBody之外,还可以定义别的参数来接收url上的参数

绑定String,直接接收到请求体JSON串

不可以直接绑定单个Integer之类的参数,只能以String传递

@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestBody String i) {//这里的i是请求体的整个JSON串,比如{
"i": "1"
}return ApiResult.success();
}

绑定Map

@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestBody Map map) {return ApiResult.success();
}

绑定实体类

入参key要和实体类中属性名一致

@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestBody CustomerParams customerParams) {return ApiResult.success();
}

绑定数组/集合

元素可以是基本类型包装类,实体类,Map

@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestBody List<Integer> idList) {return ApiResult.success();
}
@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestBody CustomerParams[] customerParamsArr) {return ApiResult.success();
}
@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestBody List<CustomerParams> customerParamsList) {return ApiResult.success();
}

@RequestBody与@RequestParam()同时使用

即请求体和url上都有入参

@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestBody CustomerParams customerParams, @RequestParam String token) {return ApiResult.success();
}//@RequestParam可以省略
@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestBody CustomerParams customerParams, String token) {return ApiResult.success();
}

实际上,@RequestBody之外的参数可以是2-8上提到的任意方式,相当于请求体和url上的参数是隔离的

@RequestMapping(value = "/getCustomerListPage")
@ResponseBody
public ApiResult getCustomerListPage(@RequestBody CustomerParams customerParams, CustomerParams customerParams2) {return ApiResult.success();
}

总结

url传参,使用参数直接绑定,可以使用多种方式绑定

请求体传参,使用@RequestBody方式绑定,@RequestBody可以直接绑定多种形式

换句话说,实际上数据绑定方式与请求方式没有必然关系,直接绑定参数是接收url参数,@RequestBody是接收请求体参数

而因为浏览器对GET,POST的规定,显然,POST请求需要用@RequestBody接收,GET请求需要直接绑定的方式接收

JSON

1.什么是JSON

JSON(JavaScript Object Notation),是一种轻量级的数据交换格式,是一个字符串

简单的说就是javascript中的对象和数组

2.结构

1.对象:{}括起来的内容,结构为键值对,可以嵌套,通过对象名.key取值

2.数组:[]括起来的内容,结构为["xxx","xxxx",...],通过索引取值

{"animals": {"dog": [{"name": "wangcai","age":15},{"name": "Marty","age": null}]}
}
animals.dog[0].name为wangcai

响应(接口返回值)

再次强调,在前后端分离的背景下,此处不过多关注Model,View,static页面

@RequestMapping("/index")
public String toIndex() {return "/index";                //返回static/index页面
}
@RequestMapping("/index")
public String toIndex() {return "redirect:/index";       //重定向到static/index页面
}
@RequestMapping("/index")
public String toIndex(Model model) {model.addAttribute("message", "这是index页面");    //可以通过Model传递数据return "/index";                //返回static/index页面
}
@RequestMapping(value = "/index", method = RequestMethod.GET)
public ModelAndView toIndex() {ModelAndView mv = new ModelAndView();mv.addObject("message", "这是index页面");mv.setViewName("/index");return mv;                      //通过ModelAndView返回
}

转发和重定向

重定向,重定向到别的url,通常是个页面,操作完这一步打开一个新的页面

重定向会使用新的request和response

return "redirect:/newPage?userId=" + vo.getId();

转发,执行后继续执行另一个url,通常为Controller

request和response仍然为最初的,所以数据还在,不需要像重定向那样跟参数

return "forward:/edit";

void

接口可以没有返回值

@RequestMapping("/save")
public void save() {sout("save");
}

@ResponseBody

在前后端分离的背景下,我们通常编写的都是直接返回数据的接口,如果我们要直接返回数据,而不是页面,也不通过Model去渲染,要怎么做

通过@ResponseBody注解

这个注解会通过springmvc提供的HttpMessageConverter接口转换为指定格式的数据,默认是String,然后通过Response响应给客户端

通过这个注解,我们可以实现将返回值转化为JSON字符串响应给浏览器,这个过程可以理解为数据序列化和反序列化的过程,通常我们还会定义一个类作为所有接口的统一返回格式

实际上,使用这个注解需要导入spring-web这个依赖,而如果我们需要返回JSON而不是String,还需要导入JSON相关依赖,但springboot的背景下,我们只需要导入一个spring-boot-starter-web启动器就已经包含了所需要的所有依赖和配置

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>import java.io.Serializable;
public class ApiResult implements Serializable {// 返回编码private String code;// 返回信息private String msg;// 返回数据封装private Object data = null;public static ApiResult failure(String msg) {ApiResult apiResult = new ApiResult();apiResult.setCode(ResultCodeEnum.SYS_ERROR.k());apiResult.setMsg(msg);return apiResult;}...
}@RequestMapping("/save")
@ResponseBody
public ApiResult save() {return ApiResult.failure("保存失败!");
}

restful风格

restful是一种软件开发风格,并不是什么开发规范,或者什么语法规则,只是一种约定,如果你写成这个样子,那么我们就叫你这种代码是restful

特点

URI等价于资源

每个资源对应一个URI,要获取这个资源那么访问它的URI即可,因此URI只能包含名词,URL是最典型的URI

要求资源是无状态的,对某个资源的请求不依赖其他资源/请求

即访问URI就可以获得资源,比如在OA上查看某个员工的信息,需要输入账号密码登陆系统再查看就是有状态的,而输入URI直接就可以获取就是无状态的

统一HTTP方法分类

GET        查询SELECT: 从服务器获取资源

POST        新增CREATE: 在服务器新建资源

PUT        更新UPDATE: 在服务器更新资源(客户端提供改变后的完整资源)

PATCH         更新UPDATE: 在服务器更新资源(客户端提供改变的属性)

DELETE        删除DELETE: 从服务器删除资源

统一返回数据格式

使用JSON串返回

针对不同操作,服务器向用户返回的结果应该符合以下规范

GET        返回单个资源对象/资源对象的列表(数组)

POST        返回新生成的资源对象

PUT        返回完整的资源对象

PATCH        返回完整的资源对象

DELETE        返回空

从URL上获取参数

restful风格要求资源通过URI直接定位,所以参数会拼写在URL中

使用占位符{}进行这样的拼接,使用@PathVariable()获取url路径上的参数

@Pathvariable()常用属性

name/value 绑定占位符名称

required 早期不支持这个属性

        如果配置fasle,意味着url中的路径那个占位符就为空了,将不会拼接到url

        那么@RequestMapping就需要映射多个url,变为@RequestMapping(value = "xxx/people/age/{age}", "xxx/people/age")

@RequestMapping("xxx/people/age/{age}")
public List<people> getPeopleByAge(@PathVariable() Integer age) {
...
}

POJO

POJO(plain ordinary java object) 简单无规则java对象

最基本的对象,没有实现任何接口,继承任何类,只包含属性,getter,setter,可以迁移和复用,比如生成PO,DTO,VO都可以直接继承POJO

PO(persistant object) 持久对象

数据库映射对象,属性与数据库对应表的字段对应

DAO(data access object 数据访问对象)

提供数据库的CRUD操作

DTO (Data Transfer Object)数据传输对象

输入:接口接收传入对象

输出:接口返回数据,此时会被改写为VO

VO( View Object 显示层对象)

页面需要很多DTO之外的信息,比如code,message等等,VO=DTO+其它信息

响应状态码

1xx

信息,1XX类型的状态码是临时响应,代表着请求已经被接受,但需要继续处理

100 Continue 服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求

101 Switching Protocols 服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。

102 Processing 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行

2xx

成功,2XX类型的状态码代表着请求已经被服务器接收、理解、并接受

200 OK 请求成功(其后是对GET和POST请求的响应文档)

201 Created 请求被创建完成,同时新的资源被创建

202 Accepted 请求已被接受,但是处理未完成

203 Non-authoritative Information 文档已经正常地返回,但一些响应头可能不正确,因为使用的是文档的拷贝

204 No Content 没有新文档,浏览器应该继续显示原来的文档,如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的

205 Reset Content 没有新文档,但浏览器应该重置它所显示的内容,用来强制浏览器清除表单输入内容

206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它

207 Multi-Status 由WebDAV(RFC 2518)扩展的状态码,代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码

3xx

重定向,3XX这类状态码代表着客户端需要采取进一步的操作才能完成请求,通常这些状态码是用来重定向的

300 Multiple Choices 多重选择,链接列表,用户可以选择某链接到达目的地,最多允许五个地址

301 Moved Permanently 所请求的页面已经转移至新的url

302 Found 所请求的页面已经临时转移至新的url

303 See Other 所请求的页面可在别的url下被找到

304 Not Modified 未按预期修改文档,客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用

305 Use Proxy 客户请求的文档应该通过Location头所指明的代理服务器提取

306 Unused 此代码被用于前一版本,表示当前功能目前已不再使用,但是代码依然被保留

307 Temporary Redirect 被请求的页面已经临时移至新的url

4xx

客户端错误,4XX类型的状态码代表着客户端可能发生了错误,阻碍了服务器的处理

400 Bad Request 服务器未能理解请求或是请求参数有误

401 Unauthorized 被请求的页面需要用户名和密码

402 Payment Required 此代码尚无法使用(为了将来可能的需求而预留的)

403 Forbidden 对被请求页面的访问被禁止

404 Not Found 服务器无法找到被请求的页面,url有误

405 Method Not Allowed 请求中指定的方法不被允许

406 Not Acceptable 服务器生成的响应无法被客户端所接受

407 Proxy Authentication Required 用户必须首先使用代理服务器进行验证,这样请求才会被处理

408 Request Timeout 请求超出了服务器的等待时间

409 Conflict 由于冲突,请求无法被完成

410 Gone 被请求的页面不可用

411 Length Required "Content-Length" 未被定义,如果无此内容,服务器不会接受请求

412 Precondition Failed 请求中的前提条件被服务器评估为失败

413 Request Entity Too Large 由于所请求的实体的太大,服务器不会接受请求

414 Request-url Too Long 由于url太长,服务器不会接受请求。当post请求被转换为带有很长的查询信息的get请求时,就会发生这种情况

415 Unsupported Media Type — 由于媒介类型不被支持,服务器不会接受请求

416 服务器不能满足客户在请求中指定的Range头

417 Expectation Failed

5xx

服务器错误

500 Internal Server Error 请求未完成。服务器遇到不可预知的情况

501 Not Implemented 请求未完成。服务器不支持所请求的功能

502 Bad Gateway 请求未完成。服务器从上游服务器收到一个无效的响应

503 Service Unavailable 请求未完成。服务器临时过载或当机

504 Gateway Timeout 网关超时

505 HTTP Version Not Supported 服务器不支持请求中指明的HTTP协议版本

springMVC拦截器

springmvc的拦截器可以对处理器进行预处理和后处理

创建和配置

implements HandlerInterceptor,重写方法

public class HandlerInterceptor1 implements HandlerInterceptor {/*** Controller方法执行前调用此方法* 返回true表示继续执行,返回false中止执行*/@Overridepublic boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {System.out.println("HandlerInterceptor1....preHandle");// 设置为true,测试使用,设置为false请求就被拦截了return true;}/*** controller方法执行后但未返回视图前调用此方法* 这里可以对返回数据进行统一的二次处理,比如多个页面都需要某些同样的处理*/@Overridepublic void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {System.out.println("HandlerInterceptor1....postHandle");}/*** controller方法执行后且视图返回后调用此方法(页面渲染后)* 这里可得到执行controller时的异常信息,可以记录请求日志*/@Overridepublic void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {System.out.println("HandlerInterceptor1....afterCompletion");}
}@Configuration
public class mvcConfig implements WebMvcConfigurer {@Autowiredprivate HandlerInterceptor1 handlerInterceptor1;@Overridepublic void add Interceptors(InterceptorRegistry registry) {// 注册拦截器,并指定要拦截的URL模式registry.addInterceptor(handlerInterceptor1).addPathPatterns("/");}
}

配置多个拦截器时的执行顺序

preHandle按拦截器定义顺序调用

postHandler按拦截器定义逆序调用

afterCompletion按拦截器定义逆序调用

postHandler在拦截器链内所有拦截器preHandle返回true才会调用

afterCompletion只要对应的preHandle返回true就调用

相关文章:

springMVC学习笔记-请求映射,参数绑定,响应,restful,响应状态码,springMVC拦截器

目录 概述 springMVC做了什么 springMVC与struts2区别 springMVC整个流程是一个单向闭环 springMVC具体的处理流程 springMVC的组成部分 请求映射 RequestMapping 用法 属性 1.value 2.method GET方式和POST方式 概述 HTTP给GET和POST做了哪些规定 GET方式&…...

Python实现视频字幕时间轴格式转换

自己喜欢收藏电影&#xff0c;有时网上能找到的中文字幕文件都不满足自己电影版本。在自己下载的压制版电影中已内封非中文srt字幕时&#xff0c;可以选择自己将srt的时间轴转为ass并替换ass中的时间轴。自己在频繁 复制粘贴改格式的时候想起可以用Python代码完成转换这一操作&…...

蓝桥杯 枚举

例题讲解 特别数的和 #include<iostream> using namespace std; bool ifspecial(int n){while(n){if(n%100||n%101||n%102||n%109){return true;} n/10;}return false; } int main(){int n;cin>>n;int sum0;for(int i1;i<n;i){if(ifspecial(i)){sumi;}}cout&l…...

C#的MessagePack(unity)--02

高级API (MessagePackSerializer) MessagePackSerializer类是MessagePack for C# 的入口点。静态方法构成了MessagePack for C# 的主要API。 APIDescriptionSerialize<T>将一个对象图序列化为MessagePack二进制块。可以使用异步变体获取Stream。也有非泛型重载可用。De…...

MySQL数据库管理--- mysql数据库迁移-v查看报错sql

默认情况下&#xff1a;每个客户端连接都会在服务器进程中拥有一个线程。 每个线程相当于一个LOCALNO的oracle远程链接。 1 该连接的查询只会在这个单独的线程中执行&#xff0c;该线程驻留在一个内核或者CPU上&#xff0c;服务器维护一个 缓冲区&#xff0c;用于存放已就绪的线…...

基于秃鹰算法优化概率神经网络PNN的分类预测 - 附代码

基于秃鹰算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于秃鹰算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于秃鹰优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…...

向pycdc项目提的一个pr

向pycdc项目提的一个pr 前言 pycdc这个项目&#xff0c;我之前一直有在关注&#xff0c;之前使用他反编译python3.10项目&#xff0c;之前使用的 uncompyle6无法反编译pyhton3.10生成的pyc文件&#xff0c;但是pycdc可以&#xff0c;但是反编译效果感觉不如uncompyle6。但是版…...

Spring学习③__Bean管理

目录 IOC接口ApplicationContext 详解IOC操作Bean管理基于xml方式基于xml方式创建对象基于xml方式注入属性使用set方法进行注入通过有参数的构造进行注入p 名称空间注入&#xff08;了解&#xff09; 基于xml方式注入其他类型属性xml 注入数组类型属性 IOC接口 IOC思想基于IOC…...

《视觉SLAM十四讲》-- 后端 2

文章目录 09 后端 29.1 滑动窗口滤波和优化9.1.1 实际环境下的 BA 结构9.1.2 滑动窗口法 9.2 位姿图9.2.1 位姿图的意义9.2.2 位姿图优化 09 后端 2 9.1 滑动窗口滤波和优化 9.1.1 实际环境下的 BA 结构 由于计算机算力的限制&#xff0c;我们必须控制 BA 的规模&#xff0c…...

安装插件时Vscode XHR Failed 报错ERR_CERT_AUTHORITY_INVALID

安装插件时Vscode XHR Failed 报错ERR_CERT_AUTHORITY_INVALID 今天用vscode 安装python插件时报XHR failed,无法拉取应用商城的数据&#xff0c; 报的错如下&#xff1a; ERR_CERT_AUTHORITY_INVALID 翻译过来就是证书有问题 找错误代码的方法&#xff1a; 打开vscode, 按F1…...

ON_WM_TIMER()

ON_WM_TIMER() static_cast: cannot convert from void (__cdecl CPop::* )(UINT) to void (__cdecl CWnd::* )(UINT_PTR) OnTimer(UINT nIDEvent) ----> OnTimer(UINT_PTR nIDEvent)...

【Unity】单例模式及游戏声音管理类应用

【Unity】单例模式及游戏声音管理类应用 描述 在日常游戏项目开发中&#xff0c;单例模式是一种常用的设计模式&#xff0c;它允许在应用程序的生命周期中只创建一个对象实例&#xff0c;并提供对该实例的全局访问点。通过使用单例模式&#xff0c;可以提高代码的可维护性和可…...

视频剪辑技巧:轻松搞定视频随机合并,一篇文章告知所有秘诀

在视频制作的过程中&#xff0c;视频随机合并是一种创新的剪辑手法&#xff0c;它打破了传统的线性剪辑模式&#xff0c;使得视频剪辑更加灵活和有趣。通过将不同的视频片段随机组合在一起&#xff0c;我们可以创造出独特的视觉效果和情感氛围。这种剪辑方式让观众在观看视频时…...

torch.stack

看网上看多没讲的不是很明白&#xff0c;我来试试空间上的理解 # 假设是时间步T1的输出 T1 torch.tensor([[1, 2, 3],[4, 5, 6],[7, 8, 9]]) # 假设是时间步T2的输出 T2 torch.tensor([[10, 20, 30],[40, 50, 60],[70, 80, 90]])输出&#xff1a; print(torch.stack((T1,T2…...

手搓哈希表、列表、队列,只为了用C语言快速求解华容道游戏,我不是大佬,只是一个游戏算法爱好者

背景 多年前曾经写过C语言求解华容道&#xff0c;当时没有用到哈希表&#xff0c;导致整个查重搜索数组过大&#xff0c;每次求解都得花上数分钟的时间&#xff0c;如今时过境迁&#xff0c;对数据结构和算法有了更深的理解&#xff0c;所以得把这一块补上了。(其实就是最近想…...

MySQL 的执行原理(一)

5.1 单表访问之索引合并 我们前边说过 MySQL 在一般情况下执行一个查询时最多只会用到单个二级 索引&#xff0c;但存在有特殊情况&#xff0c;在这些特殊情况下也可能在一个查询中使用到多个二 级索引&#xff0c;MySQL 中这种使用到多个索引来完成一次查询的执行方法称之为&…...

2023_“数维杯”问题B:棉秸秆热解的催化反应-详细解析含代码

题目翻译&#xff1a; 随着全球对可再生能源需求的不断增加&#xff0c;生物质能作为一种成熟的可再生能源得到了广泛的关注。棉花秸秆作为一种农业废弃物&#xff0c;因其丰富的纤维素、木质素等生物质成分而被视为重要的生物质资源。虽然棉花秸秆的热解可以产生各种形式的可…...

django理解01

接在Vue理解01后 项目创建 pycharm上下载django框架 在需要创建项目的文件夹终端django-admin startproject 项目名终端创建APPpython manage.py startapp app名注册APP&#xff0c;settings.py里INSTALLED_APPS下&#xff0c;增加一项&#xff1a;app名.apps.类名&#xff0…...

限制Domain Admin登录非域控服务器和用户计算机

限制Domain Admin管理员使用敏感管理员帐户(域或林中管理员组、域管理员组和企业管理员组中的成员帐户)登录到信任度较低的服务器和用户端计算机。 此限制可防止管理员通过登录到信任度较低的计算机来无意中增加凭据被盗的风险。 建议采用的策略 建议使用以下策略限制对信任度…...

原来机械硬盘比内存慢10万倍

我们都知道机械硬盘的速度很慢&#xff0c;内存的速度很快&#xff0c;那么不同存储器之间的差距到底有多大呢&#xff1f; 我们先来看一幅图&#xff1a; CPU访问寄存器的时间是0.3纳秒&#xff0c;访问L1高速缓存的时间是1纳秒&#xff0c;访问L2高速缓存的时间是4纳秒… 秒…...

ElementUI的Dialog弹窗实现拖拽移动功能

文章目录 1. ElementUI简介2. 弹窗基本使用3. 实现拖拽移动功能4. 拓展与分析 &#x1f389;欢迎来到Java学习路线专栏~ElementUI的Dialog弹窗实现拖拽移动功能 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&#x1f388;该系列文章专栏&a…...

生成式AI模型量化简明教程

在不断发展的人工智能领域&#xff0c;生成式AI无疑已成为创新的基石。 这些先进的模型&#xff0c;无论是用于创作艺术、生成文本还是增强医学成像&#xff0c;都以产生非常逼真和创造性的输出而闻名。 然而&#xff0c;生成式AI的力量是有代价的—模型大小和计算要求。 随着生…...

机器人制作开源方案 | 智能快递付件机器人

一、作品简介 作者&#xff1a;贺沅、聂开发、王兴文、石宇航、盛余庆 单位&#xff1a;黑龙江科技大学 指导老师&#xff1a;邵文冕、苑鹏涛 1. 项目背景 受新冠疫情的影响&#xff0c;大学校园内都采取封闭式管理来降低传染的风险&#xff0c;导致学生不能外出&#xff0c…...

PostgreSQL技术大讲堂 - 第34讲:调优工具pgBagder部署

PostgreSQL从小白到专家&#xff0c;是从入门逐渐能力提升的一个系列教程&#xff0c;内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容&#xff0c;希望对热爱PG、学习PG的同学们有帮助&#xff0c;欢迎持续关注CUUG PG技术大讲堂。 第34讲&#…...

《Python日志新宠——Loguru,轻松记录,笑对Bug!》

嘿&#xff0c;程序媛和程序猿们&#xff01;&#x1f469;‍&#x1f4bb;&#x1f468;‍&#x1f4bb; 你们是不是也曾为日志处理这个“小事”而头疼&#xff1f;别着急&#xff0c;今天我给你们介绍一个简直比拥抱泰迪熊还要温暖的Python库——Loguru&#xff01;这货不仅强…...

NET8 ORM 使用AOT SqlSugar

.NET AOT8 基本上能够免强使用了, SqlSugar ORM也支持了CRUD 能在AOT下运行了 Nuget安装 SqlSugarCore 具体代码 StaticConfig.EnableAot true;//启用AOT 程序启动执行一次就好了//用SqlSugarClient每次都new,不要用单例模式 var db new SqlSugarClient(new ConnectionC…...

CCRC认证是什么?

什么是CCRC认证&#xff1f; 信息安全服务资质&#xff0c;是信息安全服务机构提供安全服务的一种资格&#xff0c;包括法律地位、资源状况、管理水平、技术能力等方面的要求。 信息安全服务资质&#xff08;CCRC&#xff09;是依据国家法律法规、国家标准、行业标准和技术规范…...

linux内核面试题(2)

整理了一些网上的linux驱动岗位相关面试题&#xff0c;如果错误&#xff0c;欢迎指正。 工作队列是运行在进程上下文&#xff0c;还是中断上下文&#xff1f;它的回调函数是否允许睡眠&#xff1f; 工作队列是运行在进程上下文的。工作队列的回调函数是允许睡眠的&#xff0c;…...

YOLOV5----修改损失函数-ShuffleAttention

主要修改yolo.py、yolov5s.yaml及添加ShuffleAttention.py 一、ShuffleAttention.py import numpy as np import torch from torch import nn from torch.nn import init from torch.nn.parameter import Parameterclass ShuffleAttention(nn.Module):def...

Kafka(四)消费者消费消息

文章目录 如何确保不重复消费消息&#xff1f;消费者业务逻辑重试消费者提交自定义反序列化类消费者参数配置及其说明重要的参数session.time.ms和heartbeat.interval.ms和group.instance.id增加消费者的吞吐量消费者消费的超时时间和poll()方法的关系 消费者消费逻辑启动消费者…...

Python uiautomation获取微信内容!聊天记录、聊天列表、全都可获取

Python uiautomation 是一个用于自动化 GUI 测试和操作的库&#xff0c;它可以模拟用户操作来执行各种任务。 通过这个库&#xff0c;可以使用Python脚本模拟人工点击&#xff0c;人工操作界面。本文使用 Python uiautomation 进行微信电脑版的操作。 以下是本次实验的版本号。…...

Java通过Lettuce访问Redis主从,哨兵,集群

操作 首先需要maven导入依赖 <dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>6.3.0.RELEASE</version> </dependency> 测试连接 public class LettuceDemo {public static voi…...

嵌入式数据库Sqlite

本文主要是介绍如何再Ubuntu下使用sqlite数据库&#xff0c;并且嵌入式QT环境下使用C语言来构建一个sqlite数据库&#xff0c;使用sqlite browser进行数据库的可视化。 1、安装sqlite 在ubuntu系统中的安装需要先下载一个安装包&#xff0c;SQLite Download Page 安装命令&a…...

计算机网络:网络层ARP协议

在实现IP通信时使用了两个地址&#xff1a;IP地址&#xff08;网络层地址&#xff09;和MAC地址&#xff08;数据链路层地址&#xff09; 问题&#xff1a;已知一个机器&#xff08;主机或路由器&#xff09;的IP地址&#xff0c;如何找到相应的MAC地址&#xff1f; 为了解决…...

集成环信IM时常见问题及解决——包括消息、群组、推送

一、消息 环信是不支持空会话的&#xff0c;在插入一个会话&#xff0c;一定要给这个会话再插入一条消息&#xff1b; 发送透传消息也就是cmd消息时&#xff0c;value的em_开头的字段为环信内部消息字段&#xff0c;如果使用会出现收不到消息回调的情况&#xff1b; 如果发送…...

Selenium自动化测试框架

一.Selenium概述 1.1 什么是框架? 框架&#xff08;framework&#xff09;是一个框子——指其约束性&#xff0c;也是一个架子——指其支撑性。是一个基本概念上的 结构用于去解决或者处理复杂的问题。 框架是整个或部分系统的可重用设计&#xff0c;表现为一组抽象构件及…...

C#实现观察者模式

观察者模式是一种软件设计模式&#xff0c;当一个对象的状态发生变化时&#xff0c;其所有依赖者都会自动得到通知。 观察者模式也被称为“发布-订阅”模式&#xff0c;它定义了对象之间的一对多的依赖性&#xff0c;当一个对象状态改变时&#xff0c;所有依赖于它的对象都会得…...

什么是持续部署

管理软件开发和部署有 3 种常见的方法&#xff1a;持续集成、持续交付&#xff0c;然后是持续部署。尽管它们经常被混淆&#xff0c;但它们是明显不同的。 正如您将在本文后面看到的&#xff0c;它们相互融合&#xff0c;并补充彼此的风格。但这篇文章并不是关于他们三个。今天…...

【Python】Loguru模块更简洁的日志记录库

Loguru: 更优雅的日志记录解决方案&#xff01; loguru 是一个Python 简易且强大的第三方日志记录库&#xff0c;该库旨在通过添加一系列有用的功能来解决标准记录器的注意事项&#xff0c;从而减少 Python 日志记录的痛苦。 使用自带自带的 logging 模块的话&#xff0c;则需要…...

智慧环保:科技驱动下的环境保护新篇章

智慧环保&#xff1a;科技驱动下的环境保护新篇章 环境保护已经成为当今社会的重要议题&#xff0c;而科技的飞速发展为我们开启了智慧环保的新篇章。在这篇文章中&#xff0c;我们将介绍智慧环保所带来的机会和创新&#xff0c;以及科技在环境保护中的重要作用。 智慧环保的理…...

CTF-PWN环境搭建手册

工欲善其事必先利其器&#xff0c;作为一名CTF的pwn手&#xff0c;一定要有自己的专用解题环境。本文将详细记录kali下的pwn解题环境的安装过程&#xff0c;B站也会配备配套视频。 目录 安装前的准备工作 虚拟机环境​编辑 VM版本安装教程 1. 下载Kali的VM虚拟机文件 2. 新…...

Nginx安装配置与SSL证书安装部署

一、Nginx Nginx是一款高性能的开源Web服务器和反向代理服务器&#xff0c;被广泛用于构建现代化的Web应用和提供静态内容。 nginx官网 这里下载nginx-1.24.0-zip Nginx是一款高性能的开源Web服务器和反向代理服务器&#xff0c;被广泛用于构建现代化的Web应用和提供静态内…...

高性能面试八股文之编译流程程序调度

1. C的编译流程 C语言程序的编译过程通常包括预处理&#xff08;Preprocessing&#xff09;、编译&#xff08;Compilation&#xff09;、汇编&#xff08;Assembly&#xff09;、链接&#xff08;Linking&#xff09;四个主要阶段。下面是这些阶段的详细说明&#xff1a; 1.…...

opencv的MinGW-W64编译

最近使用Qt&#xff0c;需要用到opencv,安装详情参考下面这个网址&#xff0c;写的挺好&#xff1a; opencv的MinGW-W64编译 - 知乎 我电脑安装Qt中自带了MinGW,所以不需要像上面网址中的下载MinGw&#xff0c;只需要将Qt中自带的MinGW添加到环境变量即可&#xff0c;如&…...

在Go编程中调用外部命令的几种场景

1.摘要 在很多场合, 使用Go语言需要调用外部命令来完成一些特定的任务, 例如: 使用Go语言调用Linux命令来获取执行的结果,又或者调用第三方程序执行来完成额外的任务。在go的标准库中, 专门提供了os/exec包来对调用外部程序提供支持, 本文将对调用外部命令的几种使用方法进行总…...

python学习:break用法详解

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 在执行while循环或者for循环时&#xff0c;只要循环条件满足&#xff0c;程序会一直执行循环体。 但在某些场景&#xff0c;我们希望在循环结束前就强制结束循环。 Python中有两种强制结束循环的方法&#xff1a; continue语…...

【算法萌新闯力扣】:找到所有数组中消失对数字

力扣热题&#xff1a;找到所有数组中消失对数字 开篇 这两天刚交了蓝桥杯的报名费&#xff0c;刷题的积极性高涨。算上打卡题&#xff0c;今天刷了10道算法题了&#xff0c;题目都比较简单&#xff0c;挑选了一道还不错的题目与大家分享。 题目链接:448.找到所有数组中消失对…...

Node.js 安装配置

文章目录 安装检测Node是否可用 安装 首先我们需要从官网下载Node安装包:Node.Js中文网,下载后双击安装没有什么特殊的地方&#xff0c;安装路径默认是C盘&#xff0c;不想安装C盘的话可以选择一下其他的盘符。安装完成以后可以不用配置环境变量&#xff0c;Node安装已经自动给…...

前端JS 使用input完成文件上传操作,并对文件进行类型转换

使用input实现文件上传 // 定义一个用于文件上传的按钮<input type"file" name"upload1" />// accept属性用于定义允许上传的文件类型&#xff0c; onchange用于绑定文件上传之后的相应函数<input type"file" name"upload2"…...

探索AI交互:Python与ChatGPT的完美结合!

大家好&#xff01;我是爱摸鱼的小鸿&#xff0c;人生苦短&#xff0c;我用Python&#xff01;关注我&#xff0c;收看技术干货。 随着人工智能的迅速发展&#xff0c;AI交互正成为技术领域的一大亮点。在这个过程中&#xff0c;Python编程语言和ChatGPT模型的结合展现出强大的…...