01springmvc初探

#springmvc

springmvc简介

​ 大部分java应用都是web应用,表现层是web应用最为重要的部分。Spring为表现层提供了一个优秀的web框架——SpringMVC。和众多其他web框架一样,它是基于mvc的设计理念,此外,它采用了松散耦合可插拔组件结构,比其他mvc框架更具有扩展性和灵活性。

SpringMVC通过一套MVC注解,让POJO(Plain Ordinary Java Object,简单的Java对象)成为处理请求的处理器,无需实现任何接口,同时,SpringMVC还支持REST风格的URL请求。此外,SpringMVC在数据绑定、视图解析、本地化处理以及静态资源处理上都有许多不俗的表现。

它在框架设计、扩展性、灵活性等方法全面超越的Struts、WebWork等MVC框架,从原来的追赶者一跃成为了MVC的领跑者。

SpringMVC框架围绕DispatcherServlet这个核心展开,DispatcherServlet是SpringMVC框架的总导演、总策划,它负责拦截请求并将其分派给相应的处理器。

springmvc入门

1.创建maven工程

记得添加webapp目录

2.添加打包方式和引入依赖

3.添加日志和springmvc配置文件

4.添加视图

在WEB-INF下创建一个views目录,在views目录下添加一个hello.jsp。

5.配置前端控制器

在web.xml中配置DispatcherServlet控制器:

6.创建自定义处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Controller
public class HelloController {

/**
* @RequestMapping("/show1.do"): 使得请求的url可以映射到指定的目标方法上
* @return
*/
@RequestMapping("/show1.do")
public ModelAndView test1(){
ModelAndView mv = new ModelAndView();
//设置要显示的页面:视图
mv.setViewName("/WEB-INF/views/hello.jsp");
//添加要显示的数据:数据是存在request域中
mv.addObject("msg","Hello Word!");
return mv;
}
}

7.开启注解扫描

8.访问localhost:8080/show1.do

springmvc架构

springmvc架构

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet手动请求调用HandlerMapping处理器映射器
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
  4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
  5. 执行处理器(Controller,也叫后端控制器)
  6. Controller执行完成返回ModelAndView
  7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器
  9. ViewReslover解析后返回具体View
  10. DispatcherServlet对View进行渲染视图(将模型数据填充到视图中)
  11. DisparcherServlet响应给用户

DispatcherServlet:前端控制器

用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcher是整个流程控制中心,由它调用其他组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性

HandlerMapping:处理器映射器

HandlerMapping 负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现了不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等

HandlerAdapter:处理器适配器

通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

Handler:处理器

它就是我们开发中要编写的具体业务处理器,由DispatcherServlet把用户请求转发到Handler。由Handler对具体的用户请求进行处理

View Resolver:视图解析器

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

View:视图

Springmvc框架提供了很多的View视图类型的支持,包括:jstlView,freemarkerView,pdfView等,我们最常用的视图就是html。一般情况下需要通过页面标签将模型数据通过页面展示给用户,需要根据业务需求开发具体页面

默认加载的组件:

我们在配置文件中只配置DispatcherServlet,缺能正常运行,说名springmvc的执行流程中需要配置映射器和适配器,而两个springmvc框架帮我们默认配置完成了,因此无需用户手动配置

在控制器DispatcherServlet中:有一行默认配置,控制器会读取该默认的资源文件DispatcherServlet.properties

1
2
3
4
5
6
7

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

为了方便起见我们在配置文件中做出如下修改

视图解析器框架默认的InternalResourceViewResolver,该视图解析器支持jsp视图的解析

1
2
3
4
5
6
7
 <!--配置视图解析器解析规则 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置视图解析器的前缀:去哪个位置找视图 -->
<property name="prefix" value="/WEB-INF/views/"></property>
<!--配置视图解析器的后缀:找以.jsp为后缀的视图 -->
<property name="suffix" value=".jsp"></property>
</bean>

视图名称需要在处理器的方法中使用ModelAndView进行封装后返回,如果视图名称名为hello,那么最终返回的jsp视图地址为:

“/WEB-INF/views/hello.jsp”

最终jsp的物理地址为:前缀+视图名称+后缀

小结

springmvc的使用步骤:

​ 1.配置web.xml

​ 配置前端控制器

​ tomcat启动时初始化

​ 指定springmvc的配置文件的位置

​ 2.编写springmvc的配置文件

​ 开启包扫描

​ 配置mvc注解驱动:注册各种功能,使用spring推荐的映射器和适配器和消息转化器

​ 配置视图解析器:指定如果去查找视图

​ 3.编写自定义处理器

​ 将处理器装配到容器中

​ 编写请求的url映射关系:@ResquestMapping(“xxx”):来映射要执行的方法

​ 在@RequestMapping(“xxx”)下编写方法处理具体的业务逻辑,返回一个ModelAndView,其中可以封装视图名称和数据

#RequestMapping映射请求

1.简介

  • 标准url映射
  • Ant风格的映射:(0个或多个字符)、?(单个字符)、**(0个或多个路径)
  • Rest风格的映射:占位符
  • 限定请求方法的映射:get post put delete
  • 限定参数的映射:限定那些请求参数可以访问

2.标准URL映射

@RequestMapping(value = “xxx”)

在springmvc众多Controller以及每个Controller的众多方法中,请求是如何映射到具体的处理方法上的呢?

它可以定义在方法上,也可以定义在类上.

  • 定义在方法上

    直接定义在方法上,这时请求路径就是方法上的@RequestMapping的value

  • 定义在类上

    相当于给url多加了一个一级路径

3.Ant风格的映射(通配符)

?:通配一个字符

*:通配任意多个字符,可以是0个,可以是1个,可以是多个.但是最好和其他字符一起使用

**:通配任意多个路径

4.Rest风格(占位符)的映射

@RequestMapping(value = “hello/{name}/{id}”)

请求url:http://localhost:8080/hello/zhangsan/1001.do

注意:占位符的主要应用是用来接受url中的参数

比如:@PathVariable(“userid”) Long id , @PathVariable(“name”) String name 获取对应的参数

注意:@PathVariable(“key”) 中的key必须和对应的占位符的参数名一致,而方法形参的参数可任意取。

如果传递的参数类型和接受参数的形参类型不一致,则会自动转换,如果转换出错,(例如:id传了abc字符串,方法形参使用Long来接受参数)则会报400错误(参数列表错误 )

5.限定请求方法的映射

限定单个请求方法

@RequestMapping(value = “x”, method = RequestMethod.POST)

限定多个请求方法

@RequestMapping(value = “x”, method = {RequestMethod.POST, RequestMethod.GET})

6.限定请求参数的映射

@RequestMapping(value= “”, params= “”)

params = “userId” : 请求参数中必须带有userId

params = “!userId” : 请求参数中不能包含userId

params = “userId = 1” :请求参数中必须包含userId并且值必须为1

params = “userId ! = 1” : 请求的所有参数中如果有userId必须不为1 ,参数中可以不包含userId

params = {“userId”, “name”} :请求参数中必须有userId,name 参数

7.组合注解

@GetMapping:相当于@RequestMapping (method =RequestMethod.GET)

@PostMapping:相当于@RequestMapping(method= RequestMethod.POST)

@PutMapping:相当于@RequestMapping(method= RequestMethod.PUT)

@DeleteMapping:相当于@RequestMapping(method= RequestMethod.DELETE)

也可以在注解中限定请求参数,用法和之前的一样。

#返回值类型

1.返回值是String类型

1
2
3
4
5
6
7
@RequestMapping(value="show")
public String test(Model model){

model.addAttribute("msg", "优化后的代码");//添加数据

return "hello";//springmvc默认将返回值 视为视图名称
}

2.返回值是void类型

1
2
3
4
5
6

@RequestMapping("show")
@ResponseStatus(value=HttpStatus.OK)
public void test(Model model) {
System.out.println("返回值是void");
}

#接受数据及数据绑定

  • 接收servlet的内置对象

  • 接收占位符请求路径中的参数

  • 接收普通的请求参数

  • 获取cookie参数

  • 基本数据类型的绑定

  • pojo对象绑定

  • 集合绑定

1.接收Servlet的内置对象

1
2
3
4
5
6
//这些对象的接收非常简单,只需要在方法形参中有该对象就能接收,不需要任何配置
@RequestMapping(value="show19")
public String test19(Model model, HttpServletRequest request, HttpServletResponse response, HttpSession session){
model.addAttribute("msg", request+"<br/>"+response+"<br/>"+session);
return "hello";
}

2.接收普通的请求参数

@RequestParam(value = “”, required = true/false, defaultValue = “”)

1.value: 参数名

2.required:是否必须, 默认为true,标识请求参数中必须包含该参数,如果不包含则抛出异常

3.defaultValue:默认参数值,如果设置了该值,required = true 将失效(即使手动设置了也会失效),自动为false,如果请求中不包含该参数则使用默认值

@RequestParam和@PathVariable的区别

​ @RequestParam比@PathVariable的区别是不需要在注解中使用占位符{xxx}

不设置required属性,那么默认提交参数name,因为required默认为true

1
2
3
4
5
6
@RequestMapping(value="show20")
public String test20(Model model,@RequestParam(value="name")String name){

model.addAttribute("msg", "使用@RequestParam接收到的参数为:"+name);
return "hello";
}

只有当手动设置了required为false时,才可以不用传递参数

1
2
3
4
5
6
@RequestMapping(value="show20")
public String test20(Model model,@RequestParam(value="name",required=false)String name){

model.addAttribute("msg", "使用@RequestParam接收到的参数为:"+name);
return "hello";
}

3.defaultValue属性

如果设置了defaultValue属性,那么表示可以不用传递参数,一旦不传递参数,就使用默认值

1
2
3
4
5
@RequestMapping(value="show20")
public String test20(Model model,@RequestParam(value="name",defaultValue="lisi")String name){
model.addAttribute("msg", "使用@RequestParam接收到的参数为:"+name);
return "hello";
}

一旦设置了默认值,那么required = true 就失效了,那怕手动设置required为true页没用,照样可以不传递参数

小结:只有配置了defaultValue属性,required属性就失效了

4.传统方式获取cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RequestMapping(value = "show21")
public String test21(Model model, HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies!=null) {//由于第一次访问,cookie是null,因此需要手动判断一下

for (Cookie cookie : cookies) {
if (cookie.getName().equalsIgnoreCase("jsessionid")) {
model.addAttribute("msg", "jsessionid:" + cookie.getValue());
}
}
}

return "hello";
}

5.注解方式获取cookie

注意:@CookieValue(“JSESSION”)必须大写

1
2
3
4
5
6
@RequestMapping(value = "show22")
public String test22(Model model, @CookieValue("JSESSIONID")String jsessionid) {
model.addAttribute("msg", "jsessionid:" + jsessionid);

return "hello";
}

notes:

​ 如果是清除Chrome缓存后发送请求,此时cookie中没用数据,因此页无法在参数中使用注解获取jsession,那就会报400

此时当cookie中没有该属性时,可以设置参数非必须或者设置一个默认即可

1
2
3
4
5
6
@RequestMapping(value = "show22")
public String test22(Model model, @CookieValue(value="JSESSIONID",required = false)String jsessionid) {
model.addAttribute("msg", "jsessionid:" + jsessionid);

return "hello";
}

6.基本数据类型的绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RequestMapping("show23")
public String test23(Model model, @RequestParam("name") String name,
@RequestParam("age") Integer age,
@RequestParam("isMarry") Boolean isMarry,
@RequestParam("income") Float income,
@RequestParam("interests") String[] interests) {

System.out.println(name);
System.out.println(age);
System.out.println(isMarry);
System.out.println(income);
System.out.println(Arrays.toString(interests));

return "hello";
}

7.pojo对象的绑定

SpringMVC会将请求参数名和pojo实体类中属性名(set 方法) 进行自动匹配如果名称一致,将把值填充到对象的属性中,并且支持级联(user.dept.id)

例如:自己编写User属性,idea在生成isMarry的setter和getter方法的时候会变成setMarry而非setIsMarry,需要手动更改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class User {
private String name;
private Integer age;
private Boolean isMarry;
private Float income;
private String [] interests;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getIsMarry() {
return isMarry;
}
public void setIsMarry(Boolean isMarry) {
this.isMarry = isMarry;
}
public Float getIncome() {
return income;
}
public void setIncome(Float income) {
this.income = income;
}
public String[] getInterests() {
return interests;
}
public void setInterests(String[] interests) {
this.interests = interests;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", isMarry=" + isMarry + ", income=" + income + ", interests="
+ Arrays.toString(interests) + "]";
}

}
1
2
3
4
5
@RequestMapping(value="show24")
public String test24(Model model,User user) {
model.addAttribute("msg",user);
return "hello";
}

当自动讲数据封装到User对象中之后,如果还想单独获取某个或几个参数,可以单独设置形参获取。

集合的绑定

1
2
3
4
5
@RequestMapping(value="show25")
public String test25(Model model,@RequestParam("ids")List<Long> ids) {
model.addAttribute("msg","打印参数:"+ids.toString());
return "hello";
}

集合元素中为pojo,这个时候用一个UserVO来包装List集合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

public class UserVO {
private List<User> users;

public List<User> getUsers() {
return users;
}

public void setUsers(List<User> users) {
this.users = users;
}

@Override
public String toString() {
return "UserVO [users=" + users + "]";
}

}

Controller方法

1
2
3
4
5
@RequestMapping(value="show26")
public String test26(Model model,UserVO userVO) {
model.addAttribute("msg","打印参数:"+userVO);
return "hello";
}

#jstl标签的使用

springmvc虽然有自己的标签库,但是其使用并不方便,因此几乎没有人使用,甚至没有人知道其存在。因而一般还是使用的jstl标签。

JSTL:标准标签库

JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。

JSTL支持通用的、结构化的任务,比如迭代,条件判断,XML文档操作,SQL标签。

1
2
3
4
5
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

找一个jsp页面,将其头信息复制该userList.jsp中:

1
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

引入jstl核心标签库:

1
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

Json

springmvc提供了一种更为简便的方式传递数据。

@ResponseBody

是把Controller方法返回值转化为JSON,称为序列化

当一个处理请求的方法标记为@ResponseBody时,表示该方法需要输出其他视图(json、xml),springmvc会通过默认的json转化器转化输出。

@RequestBody

是把接收到的JSON数据转化为Pojo对象,称为反序列化

转换json的常用方式:

1、 Gson

Google的工具,功能强大,但是效率稍逊。

2、 Fastjson

阿里的工具,效率高,但是功能稍逊。

3、 jackson

springmvc内置的转换工具,功能和效率都有不俗的表现,在世界范围内使用较广。

引入依赖

1
2
3
4
5
6
<!-- Jackson Json处理工具包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

/**
* 将list集合响应成json数据
* @return
*/
@RequestMapping(value="show28")
@ResponseBody//将数据响应成json格式的数据
public List<User> test28() {

List<User> list = new ArrayList<User>();
for(int i = 0;i< 20;i++) {
User user = new User();
user.setId(i+1L);
user.setUsername("zhangsan"+i);
user.setName("张三"+i);
user.setAge(18);
list.add(user);
}

return list;
}

报错:在使用jackson转换json数据时需要消息转换器HttpMessageConverter的支持,该消息转换器默认并没有开启。

1
2
<!--配置注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>

使用pojo接收接送数据

@RequestBody:接收一个json并且转换成一个对象。
接收一个json数据并反序列化为一个user对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 将提交的json格式的数据封装到user对象中
*
* @RequestBody():自动将json数据序列化成一个user对象
* @param model
* @param user
* @return
*/
@RequestMapping(value="show29")
public String test29(Model model,@RequestBody User user) {
model.addAttribute("msg", user);
return "hello";
}

使用String接收json数据

1
2
3
4
5
@RequestMapping(value="show30")
public String test30(Model model,@RequestBody String user) {
model.addAttribute("msg", user);
return "hello";
}

解决中文乱码问题

原因:使用的消息转换器换成了StringHttpMessageConverter,该转换器中使用的默认的编码为ISO_8859_1:

解决方案:

修改消息转换器中的编码集

将注解驱动修改如下:
1、 设置String的消息转换器
2、 该消息转换器中有一个构造函数可以设置编码集,因此只要直接赋值即可。

1
2
3
4
5
6
7
8
<!--配置注解驱动-->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg index="0" value="utf-8"></constructor-arg>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

@RestController

有时如果在一个Contoller中所有的方法都是用来响应json格式数据的,那么如果有多个方法,就需要在多个方法上使用@ResponseBody,这样太麻烦,springmvc提供了一个@RestController,将该注解使用在Controller类上,那么该controller中的所有方法都默认是响应json格式的数据了。

#文件上传

添加依赖

1
2
3
4
5
6
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>

配置文件上传解析器

需要在springmvc的配置文件中配置一个文件上传解析器(在spring-web包下),并且设置其解析器的某些参数,如上传文件大小和编码集等。

在springmvc-servlet.xml中配置:
注意:必须配置id属性,并且名称固定为multipartResolver,否则无法使用.

1
2
3
4
5
6
7
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置上传文件大小-->
<property name="maxUploadSize" value="5242880"></property>
<!--设置上传文件编码集-->
<property name="defaultEncoding" value="utf-8"></property>
</bean>

defaultEncoding:用来设置上传文件名称的参数,如果不设置的话,问题如下:

保存的图片会出现乱码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 文件上传
* 需要通过MultipartFile类型来接收上传数据
*
* @throws Exception
* @throws IllegalStateException
*/
@RequestMapping("show31")
public String test31(Model model,@RequestParam("file")MultipartFile file) throws Exception{

if (file!=null) {
//将文件存储到指定路径
file.transferTo(new File("d://upload//"+file.getOriginalFilename()));
}
model.addAttribute("msg", "上传成功!");
return "hello";
}

但是如果上传图片过大就会报500的错误

如此的话,用户体验不好,我们应该自定义一个错误页面来告诉用户,哪里出问题了。
这是有一个处理器异常解析器抛出的错误,在请求到达我们自定义的Controller之前就抛出了异常。

自定义一个处理器异常解析器:

​ 1、自定义处理器异常解析器实现HandlerExceptionResolver

​ 2、 在springmvc-servlet.xml中注册自定义的处理器异常解析器

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mv = new ModelAndView();
//判断是什么异常
if (ex instanceof MaxUploadSizeExceededException) {
//添加异常信息
mv.addObject("msg","上传文件大小不得超过5个字节");
mv.setViewName("hello");
}
return mv;
}
}
1
2
3
<!--装配自定义处理器错误解析器-->

<bean class="cn.itcast.exception.MyHandlerExceptionResolver"></bean>

当上传多个的时候后台使用数据接收遍历循环

1
2
3
4
5
6
7
8
9
10
@RequestMapping("show32")
public String test32(Model model,@RequestParam("files")MultipartFile[] files) throws Exception{
for (MultipartFile file : files) {
if (file!=null) {
file.transferTo(new File("d://upload//"+file.getOriginalFilename()));
}
}
model.addAttribute("msg", "上传成功");
return "hello";
}

#转发和重定向

返回值为字符串时,默认为视图名称。当返回值字符串是以”forward:”或者”redirect:”开头,则会被认为是转发或者重定向。

使用方式如下:

转发:forward:/hello/show.do(绝对路径)或者forward:show.do(相对路径)

重定向:redirect:/hello/show.do(绝对路径)或者redirect:show.do(相对路径)

/:表示绝对路径,指的是localhost:8080/springmvc(项目名称可以省略)

不带/:表示相对路径,相对于当前请求的路径

如果当前请求是:localhost:8080/springmvc(项目名称可以省略)/hello/show32

那么不带/:表示localhost:8080/springmvc(项目名称可以省略)/hello/

forward

redirect

拦截器

HandlerExecutionChain是一个执行链,当请求到达DispatchServlet时,DispatchServlet根据请求路径到HandlerMapping查询具体的Handler,从HandlerMapping返回执行链给DispatcherServlet,其中包含了一个具体的Handler对象和Interceptors(拦截器集合)。

拦截器一般用于对处理器进行预处理和后处理。

应用场景:

1、权限检查:如登录检测,进入处理器前检测用户是否登录,如果没有登陆直接返回到登录页面。

2、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,统计处理器执行使用了多少时间。

springmvc的拦截器接口(HandlerInterceptor)定义了三个方法:

n preHandle调用Handler之前执行,称为预处理回调方法

返回值:true表示放行,后续业务逻辑继续执行

false表示被拦截,后续业务逻辑不再执行,但之前返回true的拦截器的请求完成回调方法会倒叙执行

n postHandle调用Handler之后执行,称为后处理回调方法

n afterCompletion视图渲染完成之后执行,可以称为请求完成回调方法

拦截器过程

拦截器处理错误过程

拦截器的配置过程:

1、 编写自定义拦截器实现HandlerInterceptor

2、 在springmvc-servlet.xml中注册自定义拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MyInterceptor1 implements HandlerInterceptor {

/**
* 在Handler方法执行之前执行,顺序执行
* 返回值,返回true拦截器放行 false拦截器不通过,后续业务逻辑不再执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("MyInterceptor1,预处理回调方法正在执行");
return true;
}

/**
*在执行完Handler方法之后执行,倒序执行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1,后处理回调方法正在执行");
}

/**
* 在视图渲染完成之后执行,倒序执行
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("MyInterceptor1,请求完成回调方法正在执行");
}

}

配置自定义拦截器

1
2
3
4
5
6
7
8
9
/: 表示绝对路径:
http://localhost:8080/springmvc
/*:表示绝对路径下的任意一级路径:
http://localhost:8080/springmvc/xxx
/**:表示绝对路径下的任意多级目录:
http://localhost:8080/springmvc/xxx
http://localhost:8080/springmvc/xxx/xxx/xxx
注意:之所以请求url中看不到springmvc,是因为在pom.xml中配置了tomcat插件,配置了path为/而省略了项目名称springmvc
在springmvc-servlet.xml中配置自定义的拦截器,/**:拦截所有请求
1
2
3
4
5
6
7
8
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--拦截所有请求-->
<mvc:mapping path="/**"/>
<bean class="com.qiezicy.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
</mvc:interceptors>

当有多个拦截器时

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--拦截所有请求-->
<mvc:mapping path="/**"/>
<bean class="com.qiezicy.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<!--拦截所有请求-->
<mvc:mapping path="/**"/>
<bean class="com.qiezicy.interceptor.MyInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>

小结:

拦截器的预处理回调方法依次执行,
后处理回调方法和请求完成回调方法倒序执行
当预处理回调方法返回false时,后续的拦截器以及Handler方法不再执行,但它前面的预处理回调方法返回值为true的拦截器的请求完成回调方法会倒序执行。
请求完成回调方法会在视图渲染完成之后才去执行。

中断拦截器的预处理回调方法

当有多个拦截器的时候,

中断第一个拦截器的预处理回调方法,将第一个拦截器的预处理回调方法返回值设置为false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MyInterceptor1 implements HandlerInterceptor {

/**
* 在Handler方法执行之前执行,顺序执行
* 返回值,返回true拦截器放行 false拦截器不通过,后续业务逻辑不再执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("MyInterceptor1,预处理回调方法正在执行");
return false;
}

/**
*在执行完Handler方法之后执行,倒序执行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1,后处理回调方法正在执行");
}

/**
* 在视图渲染完成之后执行,倒序执行
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("MyInterceptor1,请求完成回调方法正在执行");
}

}

结果:后续方法不再执行

中断第二个拦截器的预处理回调方法(先将第一个恢复)

将第二个拦截器的预处理回调方法返回值设置为false:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Public class MyInterceptor2 implements HandlerInterceptor {

/**
* 在Handler方法执行之前执行,顺序执行
* 返回值,返回true拦截器放行 false拦截器不通过,后续业务逻辑不再执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("MyInterceptor2,预处理回调方法正在执行");
return false;
}

/**
*在执行完Handler方法之后执行,倒序执行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor2,后处理回调方法正在执行");
}

/**
* 在视图渲染完成之后执行,倒序执行
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("MyInterceptor2,请求完成回调方法正在执行");
}

}

结果:

​ 执行测试:执行完连个预处理回调方法后,倒叙执行请求完成回调方法,由于第二个连接器的预处理回调方法返回false,因此自定义处理器不再执行,然后开始倒叙执行请求完成回调方法,并且没有机会执行第二个拦截器的请求完成回调方法,而是从第一个拦截器的请求完成回调方法开始执行。

#POST和GET乱码解决(tomcat7)

在使用Post提交请求参数的时候,一旦参数是中文的,那么controller中获取到的数据数据就会乱

编写自定义处理器

1
2
3
4
5
@RequestMapping("show36")
public String test36(@RequestParam("name") String name) {
System.out.println(name);
return "hello";
}

当传来参数是中文时,会产生乱码

解决方案:
手动配置post请求编码过滤器

Spring-web给我们提供了解决post请求编码的过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--post请求乱码过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

解决get请求乱码文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8080</port>
<path>/</path><!-- 相当于工程名称,一旦配置了/,那么在访问时可以省略工程名称 -->
<!--设置get请求编码集-->
<uriEncoding>utf-8</uriEncoding>
</configuration>
</plugin>
</plugins>

Powered by Hexo and Hexo-theme-hiker

Copyright © 2016 - 2018 Francis的个人博客 All Rights Reserved.

UV : | PV :