Spring MVC
一、概述
1.1什么是SpringMVC
复习MVC
MVC提倡:每层编写自己的东西, 不写任何与该层无关的代码
分层的目的:为了解耦,分工明确,方便后期代码维护。
Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架
1.2Spring MVC的特点:
- 轻量级,简单易学
- 高效,基于请求响应的MVC框架
- 与Spring兼容性好, 无缝结合
- 约定优于配置
- 功能强大: RESTful、 数据验证、格式化、本地化、主题等
- 简洁灵活
1.3核心组件:
- DispatcherServlet: 前置控制器,负责调度其他组件的执行,可以降低不同组件之间的耦合性,是整个Sprring MVC 的核心模块。
- Handler: 处理器,完成具体的业务逻辑,相当于Servlet。
- HandlerMapping: DispatcherServlet是通过HandlerMapping将请求映射到不同的Handler。
- HandlerInterceptor: 处理器拦截器,是一个接口,如果我们需要进行一些拦截处理,可以通过实现该接口完成。
- HandlerExecutionChain: 处理器执行链,包括两部分内容: Handler 和HandlerInterceptor (系统会有一个默认的HandlerInterceptor,如果需要额外拦截处理,可以添加拦截器进行设置)。
- HandlerAdapter: 处理器适配器,Handler 执行业务方法之前,需要进行一系列的操作包括表单的数据验证、数据类型的转换、将表单数据封装到POJO等,这一些列操作都是由
HandlerAdapter完成,DispatcherServlet通过HandlerAdapter执行不同的Handler。 - ModelAndView: 封装了模型数据和视图信息,作为Handler的处理结果,返回给DispatcherServlet。
- ViewResolver: 视图解析器,DispatcherServlet 通过它将逻辑视图解析为物理视图,最终将渲染的结果响应给客户端。
1.4工作流程
- 客户端请求被DispatcherServlet接收。
- 根据HandlerMapping映射到Handler.
- 生成Handler和HandlerInterceptor。
- Handler 和HandlerInterceptor以HandlerExecutionChain的形式一并返回给DispatcherServlet。
- DispatcherServlet 通过HandlerAdpater调用Handler的方法完成业务逻辑处理。
- 返回一个ModelAndView对象给DispatcherServlet。
- DispatcherServlet 将获取的ModelAndView对象传给ViewResolver视图解析器,将逻辑视图解析成物理视图。
- ViewResolver返回一个View给DispatcherServlet。
- DispatcherServlet根据View进行视图渲染(将模型数据填充到视图中)
- DispatcherServlet将渲染之后的视图响应给客户端。
二.入门案列
2.1 流程梳理
1、DispatcherServlet 接收到URL请求index,结合@RequestMapping"/index")注解将该请求交给index业务方法进行处理。
2、执行index业务方法,控制台打印日志,并且返回"index"字符串(逻辑视图)。
3、结合springmvc.xml中的视图解析器配置,找到目标资源: /index.jsp, 即根目录下的index.jsp文件,将该JSP资源返回给客户端完成响应。
Spring MVC环境搭建成功。
1.导入相关依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
2.在web.xml中配置前端控制器
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!--DispatcherServlet 对象的创建是在使⽤时创建,如果需要在运⾏时创建,则需要通过load-on-startup 进⾏设置,数值越⼩,加载的优先级越⾼-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!--
/ 拦截 所有请求
/* 拦截所有请求,但是视图层的请求是不可以访问
http://localhost:8080/springmvc/index.jsp
http://localhost:8080/springmvc/hello
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3.在 resources中创建SpringMVC配置文件:spring-mvc.xml
<!--注解扫描-->
<context:component-scan base-package="com.wdzl..controller"></context:component-scan>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
4.编写请求处理器,并表示为控制器,同时为类中的方法提供请求路径
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "/success.jsp";
}
}
2.2 Spring MVC 常用注解
@RequestMapping常用参数
- value :指定URL请求的实际地址,是@RequestMapping的默认值(也可以用path)
@RequestMapping(value = "/d1")
public String index(){
System.out.println("接收到了");
return "index";
}
- method : 指定请求的method 类型,包括GET、POST、PUT、DELETE等。
//只能用post方法请求
@RequestMapping(value = "/d1",method = RequestMethod.POST)
public String index(){
System.out.println("接收到了");
return "index";
}
- params:指定request请求中必须包含的参数值,若不包含,无法调用该方法。
- params={“userName”},表示请求参数必须是有userName
- params={“age!=10”},表示请求参数中age不能为10。
@RequestMapping(value = "/d1",params={"id=1","name=tom"})
public String index(int id,String name){
System.out.println(id+":"+name);
System.out.println("接收到了");
return "index";
}
2.3 Spring MVC组件扫描
SpringMVC注解扫描的两种写法
方式一:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--注解扫描-->
<context:component-scan base-package="com.wdzl..controller"></context:component-scan>
</beans>
方式二:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--注解扫描-->
<context:component-scan base-package="com.wdzl">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
三、 SpringMVC 的数据响应
3.1 页面跳转 - 直接返回字符串
直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀进行拼接,然后跳转。
1. 直接返回字符串
2. 返回带前缀的字符串
Spring MVC默认以转发的形式响应JSP,可以手动进行修改。
转发
@RequestMapping("/d1/t1")
public String test(){
return "forward:/index.jsp";
}
相当于
@RequestMapping("/d1/t2")
public String test2(){
return "index";
}
重定向
@RequestMapping("/d1/t3")
public String test3(){
return "redirect:/index.jsp";
}
设置重定向的时候不能写逻辑视图,必须写明资源的物理路径,如"redirect:/index.jsp"
注意:
重定向的资源一定是具有外部访问权限的资源,比如“WEB-INF”外部是不能访问的
3.2 页面跳转 - 返回ModelAndView
从入门案例演示效果中,我们不难看出,跳转的资源和方式 与返回值有关,所以这次我们将Controller中的方法返回值进行修改,这次我们返回ModelAndView对象
方式一:
/**
* Model-模型:用于分装对象
* View-视图:用于展示数据
* @return
*/
@RequestMapping("/hello/t1")
public ModelAndView Test(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("success");
modelAndView.addObject("msg","你好");
return modelAndView;
}
方式二:
/**
* Model-模型:用于分装对象
* View-视图:用于展示数据
* @return
*/
@RequestMapping("/d1/tt")
public ModelAndView tests(ModelAndView modelAndView){
modelAndView.setViewName("success");
modelAndView.addObject("msg","你好");
return modelAndView;
}
方式三:
@RequestMapping("/hello/t3")
public String test5(Model model){
model.addAttribute("name","这几年");
return "success";
}
注意:
要在jsp页面,要加入一个属性(是否忽略EL表达式)
isELIgnored="false
- Model - 模型:用于封装数据
- View - 视图:用于展示数据,功能类似于Servlet中的转发或重定向的功能
- setViewName():设置跳转的视图名称。会与配置文件中视图解析器的前后缀进行拼接
- addObject():设置模型数据,类似于Servlet中的setAttribute(),使用键值对进行存储。
3.3页面跳转-返回域对象
当我们需要使用传统域对象时,我们也可以将域对象作为参数进行传递。
@RequestMapping("/e/t1")
public String test(HttpServletRequest request){
request.setAttribute("msg","天不生我李淳罡,剑道万古如长夜");
return "success";
}
3.4 回写数据 - 返回字符串
方式1:使用HttpServletResponse对象
@RequestMapping(value = "/e/t6")
public void hello6(HttpServletResponse response) throws IOException {
response.getWriter().println("Hello SpringMVC");
}
方式2:使用注解方式
@RequestMapping(value = "/e1/t7")
@ResponseBody//不进行视图跳转,直接进行数据响应
public String hello7(){
return "Hello SpringMVC";
}
3.5 回写数据 - 返回JSON字符串
方式1:直接返回JSON字符串
@RequestMapping("/e/t8")
@ResponseBody//不进行视图跳转,直接进行数据响应
public String hello8(){
return "{\"username\":\"ZhouJieLun\",\"age\":30}";
}
方式2:使用转换工具将对象转换为JSON字符串并返回
1.在pom.xml中导包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
2.编写方法
方式一
@RequestMapping("/e/t5")
@ResponseBody
public String test3() throws JsonProcessingException {
User user = new User();
user.setId(6);
user.setName("哈哈哈");
//使用JSON转换工具将对象转换成JSON格式的字符串并返回
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);
return json;
}
方式二
@RequestMapping("/e/t6")
@ResponseBody
public User test4(){
User user = new User();
user.setId(6);
user.setName("哈哈哈");
return user;
}
注意:
要在配置文件加入下面的配置
<!-- 配置处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
</list>
</property>
</bean>
方式三:
@RequestMapping("/e/t6")
@ResponseBody
public User test4(){
User user = new User();
user.setId(6);
user.setName("哈哈哈");
return user;
}
<!--MVC注解驱动-->
<mvc:annotation-driven/>
在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为MVC三大组件。使用<mvc:annotation>
可以自动加载 处理器映射器 和 处理器适配器,并且默认底层会集成jackson进行对象或集合的JSON格式字符串的转换。
四、SpringMVC 获取请求参数
服务器端需要获取请求参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数:
- 基本数据类型
- POJO类型
- 数组类型
- 集合类型
接下来我们分别去讲解这几类情况
- @ResponseBody:这个注解配合**@Controller**使用 ,表示他不会走视图解析器,会直接返回一个字符串
- @RestController:表示他不会走视图解析器,会直接返回一个字符串
4.1 获取请求 - 基本数据类型参数
在Controller中,只要业务方法的形参,与请求参数同名,SpringMVC就可以自动将请求参数封装到形参中。
1.编写业务方法
@RequestMapping("/baseType")
@ResponseBody
public String baseType(int id){
return "id:"+id;
}
客户端HTTP请求中必须包含id参数,否则抛出500异常,因为id不能为null。
同时id的值必须为数值且必须为整数,否则抛出400异常。
4.2 获取请求 - POJO类型参数
在Controller中,在业务方法声明中,形参的属性与 请求参数同名,并提供相应 get、set 方法,SpringMVC会自动将参数封装到对象中。
pojo
public class User {
private int id;
private String name;
private int age;
//构造
//get/set
//toString()
}
//前端接收的是一个对象
/**
* 1.按收前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用
*2.假设传递的是一个对 象User.四配User对象中的字段名:如果名字一致则OK, 否则,匹配不到
*/
@RequestMapping("m1/t2")
public String test2(User user){
System.out.println(user);
return "test";
}
注意:如果使用对象的话,,前端传递的参数名和对象名必须一致,否则就是null。
练习
User
public class User {
private int id;
private String name;
//构造
//get/set
//toString()
}
@RequestMapping("/getUser")
public String getUser(User user){
System.out.println(user);
return "index";
}
级联操作
User
public class User {
private int id;
private String name;
private Address address;
//构造
//get/set
//toString()
}
Address
public class Address {
private int code;
private String message;
//构造
//get/set
//toString()
}
add.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/getUser" method="post">
<table>
<tr>
<td>ID</td>
<td>
<input type="text" name="id"/>
</td>
</tr>
<tr>
<td>姓名</td>
<td>
<input type="text" name="name"/>
</td>
</tr>
<tr>
<td>地址ID</td>
<td>
<input type="text" name="address.code"/>
</td>
</tr>
<tr>
<td>地址信息</td>
<td>
<input type="text" name="address.message"/>
</td>
</tr>
<tr>
<td>
<input type="submit" name="提交"/>
</td>
</tr>
</table>
</form>
</body>
</html>
Handler
@RequestMapping("/getUser")
public String test2(User user){
System.out.println(user);
return "i";
}
4.3 获取请求 - 数组类型参数
在Controller中,只要业务方法的形参,与请求参数同名,SpringMVC就可以自动将请求参数封装到形参集合中。
controller:
@RequestMapping(value = "/hello13")
@ResponseBody
public void hello13(String[] name){
System.out.println(Arrays.toString(name));
}
@RequestMapping("/array")
@ResponseBody
public String arrayType(String[] names){
StringBuffer buffer = new StringBuffer();
for (String name : names){
buffer.append(name).append(" ");
}
return "names:"+buffer.toString();
}
4.4 获取请求 - 集合类型参数
集合List
Spring MVC不支持List类型的直接转换,需要包装成Object。
方式一:
List的自定义包装类
public class UserList {
private List<User> userList;
//构造
//get/set
//toString()
}
业务方法
@RequestMapping("/listType")
@ResponseBody
public String listType(UserList userList){
StringBuffer buffer = new StringBuffer();
for (User user : userList.getUserList()){
buffer.append(user).append(" ");
}
return "userList:"+buffer.toString();
}
JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/listType" method="post">
用户1ID<input type="text" name="userList[0].id"><br>
用户1名字<input type="text" name="userList[0].name"><br>
用户2ID<input type="text" name="userList[1].id"><br>
用户2名字<input type="text" name="userList[1].name"><br>
<input type="submit">
</form>
</body>
</html>
需要注意的是User类一定 要有无参构造函数,否则抛出异常。
方式二:通过Ajax提交数据
ajax.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="/js/jquery-3.5.1.min.js"></script>
<script>
var userList = Array();
userList.push({name:"废材",id:"10"});
userList.push({name:"你好",id:"15"});
userList.push({name:"世界",id:"20"});
$.ajax({
type:"POST",
url:"/e/t10",
data:JSON.stringify(userList),
contentType:"application/json;charset=utf-8"
})
</script>
</head>
<body>
</body>
</html>
Controller
@RequestMapping("/e/t10")
@ResponseBody
public void test8(@RequestBody List<User> userList){
for (User user : userList){
System.out.println(user);
}
}
4.5 获取请求 - 获取静态资源
在上一案例中,如果没有配置<mvc:resources>
之前,静态资源是不能被访问到的,为什么呢?
我们先来回顾一下SpringMVC的工作原理。
首先我们需要在web.xml中配置前端控制器,并且我们配置的访问路径是:“ / ”,代表所有的请求都会过前端控制器。
接下来,当我们访问ajax.jsp时,该页面会自动提交json数据,但是在提交之前,需要先访问jquery.js文件,访问该文件的请求也会被前端控制器获取,并且前端控制器会误以为,该请求也是在请求某个处理器,所以就会报错。
<mvc:resources mapping="/js/**" location="/js/"/>
该配置相当于开放资源的访问,配置中的访问不需要通过前端控制器。
比如,后面我们网页会有大量的图片,我们也可以通过此种方式开放对图片的访问
<mvc:resources mapping="/img/**" location="/img/"/>
mapping 是访问资源的URL, location是资源具体存放的目录。
还有另外一种方式,当前端控制器找不到资源时,可以交给原始的控制器,也就是tomcat来寻找。
<!--mvc注解驱动-->
<mvc:annotation-driven/>
<!-- <!–静态资源访问–>
<mvc:resources mapping="/js/**" location="/js/"/>--> <mvc:default-servlet-handler/>
4.6 获取请求 - 处理中文乱码问题
当表单提交时,数据会出现乱码问题,我们可以通过设置一个过滤器来进行编码的过滤。
在web.xml中添加如下配置:注意过滤器要写在serlvet前面。
<!--配置全局过滤器-->
<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>Copy
此时再提交表单就不会有中文乱码问题了。
4.7 参数绑定参数:@RequestParam
1、在业务方法定义时声明参数列表。
2、给参数列表添加@RequestParam注解进行绑定。
- 提交的域名称和处理方法的参数名一致
//http://localhost:8080/m1/t1?name=xxx
@RequestMapping("/m1/t1")
public String test(String name , Model model){
//1.接收前端数据
System.out.println("接收到的数据:"+name);
//2.将返回的结果传递给前端
model.addAttribute("msg",name);
//3.试图跳转
return "test";
}
- 提交的域名称和处理方法的参数名不一致
//http://localhost:8080/m1/t1?username=xxx
//@RequestParam("username") : username提交的域的名称
@RequestMapping("/m1/t1")
public String test(@RequestParam("username") String name , Model model){
//1.接收前端数据
System.out.println("接收到的数据:"+name);
//2.将返回的结果传递给前端
model.addAttribute("msg",name);
//3.试图跳转
return "test";
}
Spring MVC可以自动完成数据类型转换,该工作是由HandlerAdapter来完成的。
@RequestParam 常用参数
- value : 与请求参数同名
- required:指定请求参数是否必须包含,默认为true,提交时如果不包含则报错
- defaultValue:当没有指定请求参数时,则使用指定的默认值进行赋值
@RequestMapping(value = "/hello16")
@ResponseBodypublic void hello16(@RequestParam(value = "username",defaultValue = "哈哈") String name) {
System.out.println(name);
}
4.8Spring MVC也支持RESTful风格的URL参数获取
Restful 是一种软件 架构风格 或 设计风格,而不是标准,它只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这种风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。
Restful风格的请求是使用 url + 请求方式
来表示一次请求目的的,HTTP协议里面四个表示操作方式
- GET:用于获取资源
- POST:用于新建资源
- PUT:用于更新资源
- DELETE:用于删除资源
//原来的 : http://localhost:8080/SpringMVC/add?a=1&b=3
@RequestMapping("/add")
public String test(int a, int b, Model model){
int res = a + b;
model.addAttribute("msg","结果1为:"+res);
return "hello";
}
// RestFul : http://localhost:8080/SpringMVC/add/2/5
//@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)
//@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.POST)
//@PostMapping
//@DeleteMapping
//@PutMapping
@GetMapping("add/{a}/{b}")
public String test2(@PathVariable int a, @PathVariable int b, Model model){
int res = a + b;
model.addAttribute("msg","结果2为:"+res);
return "hello";
}
@PostMapping("add/{a}/{b}")
public String test3(@PathVariable int a, @PathVariable int b, Model model){
int res = a + b;
model.addAttribute("msg","结果3为:"+res);
return "hello";
}
映射Cookie
//映射Cookie
@RequestMapping("/cookie")
public String getCookie(@CookieValue("JSESSIONID") String sessionId){
System.out.println(sessionId);
return "index";
}
4.9 自定义类型转化器
在前面hello11() 方法演示中,虽然我们输入的age是数字,但是传输到服务器端都会变成字符串。在二阶段时,Servlet获取请求参数使用了一个方法叫 getParameter()
该方法返回的数据类型都是String就足以证明无论前端传输的数据类型是什么,最终到达服务器端都是字符串格式。
SpringMVC默认提供了一些常用的类型转换器,例如:客户端提交的字符串转换成相应的int类型。但是并不是所有数据类型都提供了转换器,如果没有提供就需要我们手动的定义转换器。比如日期类型的数据就需要我们自定义转换器。
实现步骤:
1. 定义转换器类实现Converter接口
2. 在配置文件中声明转换器
3. 在`<annotation-driven>`中引用转换器
Converter
/**
* 自定义转换器:String -> Date
*/
public class DateConverter implements Converter<String ,Date> {
@Override
public Date convert(String s) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = simpleDateFormat.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
Controller
@RequestMapping("/e/t11")
@ResponseBody
public void test9(Date date){
System.out.println(date);
}
配置文件
<!--MVC注解驱动-->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
<!--声明转换器-->
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.wdzl.converter.DateConverter"></bean>
</list>
</property>
</bean>
4.10 Servlet相关API
@RequestMapping(value = "/e/t13")
@ResponseBody
public void hello19(HttpServletRequest request , HttpServletResponse response, HttpSession session) {
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
4.11 获取请求头
通过使用@RequestHeader可以获取请求头信息,相当于web阶段学习的request.getHeader(name)
@RequestMapping(value = "/e/t14")
@ResponseBodypublic void hello20(@RequestHeader(value="Host") String host, @RequestHeader(value ="User-Agent") String user_agent) {
System.out.println(host);
System.out.println(user_agent);
}
这里需要指出的是头信息信息中有一个比较特殊的值:cookie,它需要另一个注解来取@CookieValue
@RequestMapping(value = "/e/t15")
@ResponseBodypublic void hello20(@RequestHeader(value="Host") String host,@RequestHeader(value ="User-Agent") String user_agent,@RequestHeader(value = "Cookie") String cookie, @CookieValue(value = "JSESSIONID") String jsessionid) {
System.out.println(host);
System.out.println(user_agent);
System.out.println(cookie);//取出Cookie对应的所有键值对
System.out.println(jsessionid);
}
通过@CookieValue可以将 cookie中存储的键值对的值取出,相当于嵌套Map集合取内层Map的value
五、文件上传
三要素
- 表单项:input type=“file”
- 表单提交方式:POST
- 表单enctype属性为"multipart/form-data"
导入依赖
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
单个文件上传
JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/file/upload" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="name">
文件:<input type="file" name="upload">
<input type="submit" value="提交">
</form>
</body>
</html>
配置文件需要文件解析器
<!--文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置上传文件的总大小-->
<property name="maxUploadSize" value="10240000"></property>
<!--设置单个上传文件的大小-->
<property name="maxUploadSizePerFile" value="102400"></property>
<!--设置编码格式-->
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
Controller
@RequestMapping("/upload")
@ResponseBody
public void upload(String name , MultipartFile upload) throws IOException {
String filename = upload.getOriginalFilename();
//上传
upload.transferTo(new File("C:\\Users\\Public\\Pictures\\NVIDIA Corporation\\"+filename));
}
多个文件上传
JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/file/uploads" method="post" enctype="multipart/form-data">
文件1:<input type="file" name="uploads"><br>
文件2:<input type="file" name="uploads"><br>
文件3:<input type="file" name="uploads"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
Controller
@RequestMapping("/uploads")
@ResponseBody
public void uploads(@RequestParam("uploads") MultipartFile[] uploads){
for (MultipartFile upload : uploads){
if (upload.getSize()>0){
String filename = upload.getOriginalFilename();//获取文件名
//上传
try {
upload.transferTo(new File("D:\\C盘的应用\\"+filename));
System.out.println("OK");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
六、拦截器 - Interceptor
概述:
SpringMVC的拦截器类似于Servlet开发中的过滤器Fileter , 用于处理器进行预处理 和 后处理。
将拦截器按照一定顺序 连接成一条链,这条链称为 拦截器链(Interceptor Chain)。在访问被拦截的方法和字段时,拦截器链中的拦截器就会按照定义的顺序被调用。拦截器也是AOP思想的实现。
拦截器和过滤器的区别:
区别 | 过滤器 | 拦截器 |
---|---|---|
使用范围 | 是Servlet规范中的一部分,任何Web工程都可以使用。 | 属于SpringMVC,只有使用该框架才能够使用拦截器。 |
拦截范围 | 在中配置了/*之后,可以对所有要访问的资源拦截。 | 只会拦截访问控制器的方法,如果访问的是JSP,html,css,image,js是不会进行拦截的。 |
实现步骤:
- 创建拦截器类,实现HandlerInterceptor接口
- 配置拦截器
- 测试拦截器的拦截对象
1.创建拦截器
package com.wdzl.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 拦截器类
* @author lp
* @version 1.0
*/
public class MyInterceptor implements HandlerInterceptor {
//在目标方法执行前,被执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
return true;
}
//在目标方法还行之后,视图返回之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
//在视图返回后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}
2.spring-mvc.xml中配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.wdzl.interceptor.MyInterceptor"/> </mvc:interceptor>
</mvc:interceptors>
6.2 方法详解
preHandle
概述:
preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处
理之前进行调用,SpringMVC中的Interceptor拦截器是链式的,可以同时存在多个
Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有
的Interceptor中的preHandle方法都会在Controller方法调用之前调用。
SpringMVC的这种Interceptor链式结构也是可以进行中断的, 这种中断方式
是令preHandle的返回值为false,当preHandle的返回值为false的时候整个请求就
结束了。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
String flag = request.getParameter("flag");
if (flag.equals("ok")) {
return true;
} else {
request.getRequestDispatcher("../error.jsp").forward(request,response);
return false;
}
}
测试:
新建一个error.jsp
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>系统正在维护。。。</h1>
</body>
</html>
地址栏输入访问地址:
http://localhost:8080/user/show?flag=no ----> 显示 系统正在维护。。。
http://localhost:8080/user/show?flag=yes ----> 显示 index的内容
2.postHandle
概述:
postHandle是进行处理器拦截用的,它的执行在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用。
演示:
//在目标方法还行之后,视图返回之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
modelAndView.addObject("userName", "林俊杰");
System.out.println("postHandle...");
}
测试:
跳转成功后,页面本身的userName属性值会被该方法中的新属性值替代。
3.afterCompletion
该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行, 这个方法的主要作用是用于清理资源的。
4.多个拦截器执行顺序
多个拦截器的执行顺序和 他们在 spring-mvc.xml 配置顺序有关。
情景1:
情景2:
情景3:
从上述图示,我们可以得出一个结论:
如果拦截器的preHandle返回true,则该拦截器的afterCompletion方法一定会被调用。
七、 异常处理
7.1 简单异常处理器SimpleMappingExceptionResolver
SpringMVC定义好了该处理器,在使用时可以根据项目情况进行相应异常与视图的映射配置。
1.新建一个module,导入相应依赖
2.创建spring-mvc.xml配置文件
3.在web.xml中配置前端控制器和全局过滤器
4.创建Controller
/**
* 用户控制层
* @author lp
* @version 1.0
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/ex")
public String exceptionTest(){
System.out.println(3/0);//运行时异常
return "index";
}
}
5.测试
访问Controller,因为方法中有运行时异常,所以页面会直接报500错误,这样对于用户体验是不好的。所以我们需要将异常进行一个处理,给用户一些更友好的提示。
6.配置简单异常处理器
<!--简单异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error"/>
</bean>
其中 value
属性值就是捕获异常后,将要跳转的视图名称。此时error.jsp还不存在,所以我们去创建一个相应的视图
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>系统正在维护中。。。。</h1>
</body>
</html>
7.再次测试,此时页面就不会显示500的错误,而是显示我们配置好的异常页面,这样对于用户体验度来说会好很多。
当然对于异常我们也可以采取差异化处理。
8.在spring-mvc.xml中配置
<!--简单异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<map>
<entry key="java.lang.ClassCastException" value="error2"></entry>
<entry key="java.lang.Exception" value="error"></entry>
</map>
</property>
</bean>
9.复制一个error2.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>类型转换异常。。。。</h1>
</body>
</html>
10.在控制层添加各种异常
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/ex")
public String exceptionTest(){
//System.out.println(3/0);
Object name = "周杰伦";
System.out.println((int)name);
return "index";
}
}
11.测试
在地址栏输入访问地址,可以通过每次页面显示的内容来确定异常是否被差异化处理。
7.2 自定义异常处理
实现步骤:
- 创建异常处理类实现HandlerExceptionResolver
- 创建异常处理器
- 编写异常页面
- 测试
演示:
1.创建自定义异常处理类
/**
* 自定义异常处理类
* @author lp
* @version 1.0
* 实现步骤:
* 1. 创建异常处理类实现HandlerExceptionResolver
* 2. 创建异常处理器
* 3. 编写异常页面
* 4. 测试
*/
public class MyExceptionResolver implements HandlerExceptionResolver {
/**
*
* @param request
* @param response
* @param handler 执行的处理程序,如果在发生异常时未选择任何处理程序,则为null
* @param ex 异常对象
* @return ModelAndView 跳转到错误视图的信息
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView modelAndView = new ModelAndView();
if(ex instanceof ClassCastException){
modelAndView.addObject("msg", "异常转换异常");
} else if (ex instanceof Exception) {
modelAndView.addObject("msg", "其他异常");
}
modelAndView.setViewName("error");
return modelAndView;
}
}
2.在主配置文件中配置
<!--自定义异常处理器-->
<bean class="com.wdzl.resolver.MyExceptionResolver"></bean>