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中止执行
     */
    @Override
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        System.out.println("HandlerInterceptor1....preHandle");
        // 设置为true,测试使用,设置为false请求就被拦截了
        return true;
    }
    
    /**
     * controller方法执行后但未返回视图前调用此方法
     * 这里可以对返回数据进行统一的二次处理,比如多个页面都需要某些同样的处理
     */
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
        System.out.println("HandlerInterceptor1....postHandle");
    }
    
    /**
     * controller方法执行后且视图返回后调用此方法(页面渲染后)
     * 这里可得到执行controller时的异常信息,可以记录请求日志
     */
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
        System.out.println("HandlerInterceptor1....afterCompletion");
    }
}

@Configuration
public class mvcConfig implements WebMvcConfigurer {
 
    @Autowired
    private HandlerInterceptor1 handlerInterceptor1;
    
    @Override
    public void add Interceptors(InterceptorRegistry registry) {
        // 注册拦截器,并指定要拦截的URL模式
        registry.addInterceptor(handlerInterceptor1).addPathPatterns("/");
    }
}

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

preHandle按拦截器定义顺序调用

postHandler按拦截器定义逆序调用

afterCompletion按拦截器定义逆序调用

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/159677.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

隐私合规检测工具_camille使用

前期准备 电脑端安装python3、frida&#xff0c;准备一台root过的安卓机 电脑安装frida和camille pip install frida pip install frida-tools #安装frida git clone https://github.com/zhengjim/camille.git #下载camille cd camille pip install -r requirements.txt手机…

(论文阅读)TiDB:一款基于Raft的HTAP数据库

引言 混合事务分析处理&#xff08;HTAP&#xff09;数据库要求隔离处理事务查询和分析查询&#xff0c;以消除它们之间的干扰。要实现这一点&#xff0c;有必要维护为这两种查询类型指定的数据的不同副本。然而&#xff0c;为存储系统中的分布式副本提供一致的视图是一项挑战…

初识Java

一、Java语言概述 1.1 Java是什么 Java是一种优秀的程序设计语言&#xff0c;它具有令人赏心悦目的语法和易于理解的语义 不仅如此&#xff0c;Java还是一个有一系列计算机软件和规范形成的技术体系&#xff0c;这个技术体系提供了完整的用于软件开发和跨平台部署的支持环境&a…

C++ 基础一

准备工具Vscode或者Clion或者Dev C或者Vs studio 和 MSYS2 是C跨平台的重要工具链. 文章目录 准备工作安装MSYS2软件 创建文件或者导入vs2019的环境 一、基本介绍1.1C源文件1.2 代码注释1.3变量与常量1.3.1变量1.3.2 常量1.3.3 二者的区别&#xff1a;1.3.4初始化&#xff08;C…

C语言变量与常量

跟着肯哥&#xff08;不是我&#xff09;学C语言的变量和常量、跨文件访问、栈空间 栈空间还不清楚&#xff0c;期待明天的课程内容 C变量 变量&#xff08;Variable&#xff09;是用于存储和表示数据值的名称。 主要包括四个环节&#xff1a;定义、初始化、声明、使用 在我刚…

springcloud新闻发布系统源码

开发技术&#xff1a; jdk1.8&#xff0c;mysql5.7&#xff0c;nodejs&#xff0c;idea&#xff0c;vscode springcloud springboot mybatis vue elementui 功能介绍&#xff1a; 用户端&#xff1a; 登录注册 首页显示搜索新闻&#xff0c;新闻分类&#xff0c;新闻列表…

IDEA 集成 Docker 插件一键部署 SpringBoot 应用

目录 前言IDEA 安装 Docker 插件配置 Docker 远程服务器编写 DockerFileSpringBoot 项目部署配置SpringBoot 项目部署结语 前言 随着容器化技术的崛起&#xff0c;Docker成为了现代软件开发的关键工具。在Java开发中&#xff0c;Spring Boot是一款备受青睐的框架&#xff0c;然…

二分查找和二分答案

【深基13.例1】查找 题目描述 输入 n n n 个不超过 1 0 9 10^9 109 的单调不减的&#xff08;就是后面的数字不小于前面的数字&#xff09;非负整数 a 1 , a 2 , … , a n a_1,a_2,\dots,a_{n} a1​,a2​,…,an​&#xff0c;然后进行 m m m 次询问。对于每次询问&#x…

高济健康:数字化科技创新与新零售碰撞 助推医疗产业优化升级

近日&#xff0c;第六届中国国际进口博览会在上海圆满落幕&#xff0c;首次亮相的高济健康作为一家专注大健康领域的疾病和健康管理公司&#xff0c;在本届进博会上向业内外展示了围绕“15分钟步行健康生活圈”构建进行的全域数字化升级成果。高济健康通过数字化科技创新与新零…

Allure集成Testng

目录 前言 介绍 安装 集成Testng 查看allure报告 前言 本节我们会介绍如何安装allure&#xff0c;allure集成testng生成测试报告。 介绍 Allure是一个用于测试报告生成的开源框架&#xff0c;它支持多种测试框架&#xff0c;包括JUnit、TestNG、Cucumber等。Allure的目…

excel 自动向下填充数据

问题 excel里的数据是合并的 拆分之后 想自动填充下边的数据 看了好几种方式都不行 用代码实现 package com.alibaba.cainiao.controller;import org.apache.poi.ss.usermodel.*;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOExceptio…

使用FP8加速PyTorch训练

现代的人工智能硬件架构(例如&#xff0c;Nvidia Hopper, Nvidia Ada Lovelace和Habana Gaudi2)中&#xff0c;FP8张量内核能够显著提高每秒浮点运算(FLOPS)&#xff0c;以及为人工智能训练和推理工作负载提供内存优化和节能的机会。 在这篇文章中&#xff0c;我们将介绍如何修…

【Linux】线程控制

文章目录 线程的概念Linux下的进程Linux下的线程进程再理解Linux线程和接口的认识代码验证二级页表 页表线程的优点线程的缺点线程异常 线程的用途进程和线程的关系线程控制线程线程ID和LWP线程等待线程终止线程分离 线程ID及进程地址空间布局 线程的概念 我们知道&#xff0c…

全新小权云黑系统

小权云黑管理系统 V1.0 功能如下&#xff1a; 1.添加骗子&#xff0c;查询骗子 2.可添加团队后台方便审核用 3.在线反馈留言系统 4.前台提交骗子&#xff0c;后台需要审核才能过 5.后台使用光年UI界面 6.新增导航列表&#xff0c;可给网站添加导航友链 7.可添加云黑类型收录 8.…

基于STC12C5A60S2系列1T 8051单片的IIC总线器件模数芯片PCF8591实现模数转换应用

基于STC12C5A60S2系列1T 8051单片的IIC总线器件模数芯片PCF8591实现模数转换应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍IIC总线器件模数芯片PCF8591介绍通过I…

注解方式优雅的实现 Redisson 分布式锁

1前言 日常开发中&#xff0c;难免遇到一些并发的场景&#xff0c;为了保证接口执行的一致性&#xff0c;通常采用加锁的方式&#xff0c;因为服务是分布式部署模式&#xff0c;本地锁Reentrantlock和Synchnorized这些就先放到一边了&#xff0c;Redis的setnx锁存在无法抱保证…

asp.net健身会所管理系统sqlserver

asp.net健身会所管理系统sqlserver说明文档 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 主要技术&#xff1a; 基于asp.net架构和sql server数据库 功能模块&#xff1a; 首页 会员注册 教练预约 系统公告 健身课程 在线办卡 用户中心[修改个人信息 修…

Modbus RTU 使用教程(modbus教程、modbus协议)

参考文章&#xff1a;这节课带你吃透Modbus通信协议 文章目录 Modbus RTU 使用教程第1部分&#xff1a;Modbus RTU 协议概述什么是Modbus RTU协议Modbus RTU的特点工作原理 第2部分&#xff1a;硬件和软件要求硬件要求软件要求 第3部分&#xff1a;Modbus RTU数据结构数据帧格式…

android适配鸿蒙系统开发

将一个Android应用迁移到鸿蒙系统需要进行细致的工作&#xff0c;因为两者之间存在一些根本性的差异&#xff0c;涉及到代码、架构、界面等多个方面的修改和适配。以下是迁移工作可能涉及的一些主要方面&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专…

各品牌PLC元件在modbus内区域

1台达&#xff1a; 输出在0区&#xff0c; 040961是在 0区 0xA000~0xA0FF 【Y0~Y377】 输入在1区&#xff0c;124577是在 1区 0x6000~0x60FF 【X0~X377】 M寄存器0区&#xff0c;0000001是 0区&#xff0c;0x000~0x1FFF 【M0~M8191】 D寄存器4区&#xff0c;400000…