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

news2024/12/23 5:09:23

目录

概述

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就调用

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEO也不行了吗?设计正在变革#实时设计

2023 年 8 月&#xff0c;在与宜家品牌合作近 10 年之后&#xff0c;SPACE10 关门了。 最近&#xff0c;IDEO&#xff0c;设计思维的早期倡导者和践行者&#xff0c;宣布裁员1/3。 介绍下这两家设计公司&#xff1a; SPACE10 由宜家全额资助&#xff0c;于 2015 年落户哥本哈根…

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

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

DevSeo Studio设置中文界面

安装好DevSeo Studio后默认打开是欢迎页。 左下角Configure点击展开&#xff0c;选择plugins 弹出页面选择“installed”,然后输入chinese,默认是关闭的&#xff0c;点击enable将它启用&#xff0c;然后点击OK。 弹出页面点击“restart”重启即可。

Draco Win10编译

1. 工具 CMake 3.2.8&#xff0c;Visual Studio 2019 2. 步骤 2.1 拷贝代码 git clone https://github.com/google/draco.gitgit clone https://github.com/google/draco.git 下载第三方依赖 git submodule sync git submodule update --init --recursive 2.2 CMake编译…

一个UE无法注册的问题

问题场景是环境中只有一个小区&#xff0c;UE在找到这个小区&#xff0c;收到MIB SIB1后一直不发起注册。我想这大概是和S准则不满足有关系了&#xff0c;这个问题基本是又没啥好看的了&#xff0c;太简单了&#xff0c;在SIB1周围找找就解决了&#xff0c;于是我发现了以下log…

Find My充电宝|苹果Find My技术与充电宝结合,智能防丢,全球定位

充电宝是一种个人可随身携带&#xff0c;自身能储备电能&#xff0c;主要为手持式移动设备等消费电子产品&#xff08;例如无线电话、笔记本电脑&#xff09;充电的便携充电器&#xff0c;特别应用在没有外部电源供应的场合。其主要组成部分包括&#xff1a;用作电能存储的电池…

从算法到应用:直播美颜滤镜SDK的全面解读与评测

直播美颜滤镜SDK技术逐渐成为直播平台不可或缺的一环。本文将对直播美颜滤镜SDK进行全面解读&#xff0c;深入探讨其算法原理和应用效果&#xff0c;并通过评测分析展现其在直播领域的实际价值。 一、算法原理解读 直播美颜滤镜的背后是复杂而精密的算法&#xff0c;旨在提升…

【docker】iptables实现NAT

iptables是一个Linux内核中的防火墙工具&#xff0c;可以被用来执行各种网络相关的任务&#xff0c;如过滤、NAT和端口转发等&#xff0c;可以监控、过滤和重定向网络流量。 iptables可以用于以下应用场景&#xff1a; 网络安全&#xff1a;iptables可以过滤网络流量&#xf…

吉林省土木建筑学会建筑电气分会及吉林省建筑电气情报网学术交流年会-安科瑞 蒋静

11月9-10日&#xff0c;吉林省土木建筑学会建筑电气分会及吉林省建筑电气情报网学术交流年会在吉林长春隆重举办。安科瑞电气股份有限公司作为智慧用电产品供应商受邀参会&#xff0c;为参会人士展示了安科瑞能源物联网云平台、电力运维云平台、智慧消防云平台、预付费管理云平…

22款奔驰S450L升级流星雨大灯 感受最高配的数字大灯

“流星雨”数字大灯&#xff0c;极具辨识度&#xff0c;通过260万像素的数字微镜技术&#xff0c;实现“流星雨”仪式感与高度精确的光束分布&#xff1b;在远光灯模式下&#xff0c;光束精准度更达之前84颗LED照明的100倍&#xff0c;更新增坡道照明功能&#xff0c;可根据导航…

[开源]基于 AI 大语言模型 API 实现的 AI 助手全套开源解决方案

原文&#xff1a;[开源]基于 AI 大语言模型 API 实现的 AI 助手全套开源解决方案 一飞开源&#xff0c;介绍创意、新奇、有趣、实用的开源应用、系统、软件、硬件及技术&#xff0c;一个探索、发现、分享、使用与互动交流的开源技术社区平台。致力于打造活力开源社区&#xff0…

java线性并发编程介绍-锁(二)

2.5 重量锁底层ObjectMonitor 需要去找到openjdk&#xff0c;在百度中直接搜索openjdk&#xff0c;第一个链接就是 找到ObjectMonitor的两个文件&#xff0c;hpp&#xff0c;cpp 先查看核心属性&#xff1a;http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473…

吴恩达《机器学习》9-1:代价函数

一、引入新标记方法 首先&#xff0c;引入一些新的标记方法&#xff0c;以便更好地讨论神经网络的代价函数。考虑神经网络的训练样本&#xff0c;其中每个样本包含输入 x 和输出信号 y。我们用 L 表示神经网络的层数&#xff0c;表示每层的神经元个数&#xff08;表示输出层神…

数据结构与算法面试题——C++

自己在秋招过程中遇到的数据结构与算法方面的面试题 数据结构 vector vector是⼀种序列式容器&#xff0c;与array唯⼀差别就是对于空间运⽤的灵活性 array占⽤的是静态空间&#xff0c;⼀旦配置了就不可以改变⼤⼩&#xff0c;如果遇到空间不⾜的情况还要⾃⾏创建更⼤的空间…

处理BOP数据集,将其和COCO数据集结合

处理BOP数据集&#xff0c;将其和COCO数据集结合 BOP 取消映射关系&#xff0c;并自增80 取消文件名的images前缀 import os import json from tqdm import tqdm import argparseparser argparse.ArgumentParser() parser.add_argument(--json_path, defaultH:/Dataset/COCO…

Ghidra逆向工具配置 MacOS 的启动台显示(Python)

写在前面 通过 ghidra 工具, 但是只能用命令行启动, 不太舒服, 写个脚本生成 MacOS 的 app 格式并导入启动台. 不算复杂, 主要是解析包的一些元信息还有裁剪软件图标(通过 MacOS 自带的 API) 脚本 #!/opt/homebrew/bin/python3import os import re import subprocess as sp…

《Linux从练气到飞升》No.31 多线程编程实践与线程安全技术

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

计算机科学速成课

建议看看计算机科学速成课&#xff0c;一门很全面的计算机原理入门课程&#xff0c;短短10分钟可以把大学老师十几节课讲的东西讲清楚&#xff01;整个系列一共41个视频&#xff0c;B站上有中文字幕版。 每个视频都是一个特定的主题&#xff0c;例如软件工程、人工智能、操作系…

6.7二叉树的最小深度(LC111)

审题要清楚&#xff1a; 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点&#xff08;左右孩子都为空的节点才是叶子节点&#xff01;&#xff09;。 算法&#xff1a; 既可以求最小高度&#xff0c;也可以直接求深度。 最小高度&#xff1a; 后序…

四、程序员指南:数据平面开发套件

REORDER LIBRARY 重排序库提供了根据其序列号对mbuf进行重排序的机制。 16.1 操作 重排序库本质上是一个对mbuf进行重新排序的缓冲区。用户将乱序的mbuf插入重排序缓冲区&#xff0c;并从中提取顺序正确的mbuf。 在任何给定时刻&#xff0c;重排序缓冲区包含其序列号位于序列…