SpringMVC
SpringMVC
Spring Web MVC 是一种基于Java 的实现了Web MVC 设计模式的请求驱动类型的轻量级Web 框架,即使用了MVC 架 构模式的思想,将 web 层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开 发,Spring Web MVC 也是要简化我们日常Web 开发的。
Spring MVC的请求流程
Spring Web MVC 框架也是一个基于请求驱动的Web 框架,并且也使用了前端控制器模式来进行设计,再根据请求映射 规则分发给相应的页面控制器(动作/处理器)进行处理。
核心架构的具体流程步骤
核心架构的具体流程步骤如下:
- 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行 处理,作为统一访问点,进行全局的流程控制;
- DispatcherServlet——>HandlerMapping, HandlerMapping 将会把请求映射为 HandlerExecutionChain 对象(包含一 个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新 的映射策略;
- DispatcherServlet——>HandlerAdapter,HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器, 即适配器设计模式的应用,从而很容易支持很多类型的处理器;
- HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处 理方法,完成功能处理;并返回一个ModelAndView 对象(包含模型数据、逻辑视图名);
- ModelAndView 的逻辑视图名——> ViewResolver,ViewResolver 将把逻辑视图名解析为具体的View,通过这种策 略模式,很容易更换其他视图技术;
- View——>渲染,View 会根据传进来的Model 模型数据进行渲染,此处的Model 实际是一个Map 数据结构,因此 很容易支持其他视图技术;
- 返回控制权给DispatcherServlet,由DispatcherServlet 返回响应给用户,到此一个流程结束。
入门案例
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置spring的编码过滤器-->
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 处理请求方式的过滤器-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--
作为框架的核心组件,在启动过程中有大量的初始化操作要做
而这些操作放在第一次请求时才执行会严重影响访问速度
因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
springmvc.xml配置
thymeleaf配置
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.noybzy.controller"></context:component-scan>
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
<mvc:default-servlet-handler/>
<!-- 开启mvc注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- 处理响应中文内容乱码 -->
<bean
class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="defaultCharset" value="UTF-8" />
<property name="supportedMediaTypes">
<list>
<value>text/html</value>
<value>application/json</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
</beans>
Controller
package cn.noybzy.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/")
public String protal(){
return "index";
}
}
ant风格路径写法
SpringMVC支持ant风格的路径
?:表示任意的单个字符
*:表示任意的0个或多个字符
**:表示任意层数的任意目录 **
**注意:在使用**时,只能使用/**/xxx的方式
配置请求映射
@RequestMapping
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
- method属性
@RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射
method属性是一个RequestMethod类型的**数组,**表示该请求映射能够匹配 多种请求方式的请求
@RequestMapping的派生注解
处理get请求的映射–>@GetMapping
处理post请求的映射–>@PostMapping
处理put请求的映射–>@PutMapping
处理delete请求的映射–>@DeleteMapping
- params属性
@RequestMapping注解的params属性通过请求的请求参数匹配请求映射
params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数 和请求映射的匹配关系
“param”:要求请求映射所匹配的请求必须携带param请求参数
“!param”:要求请求映射所匹配的请求必须不能携带param请求参数
“param=value”:要求请求映射所匹配的请求必须携带param请求参数且param**=**value
“param!=value”:要求请求映射所匹配的请求必须携带param请求参数但是param != value
- headers属性
@RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射
@RequestMapping注解的headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信 息和请求映射的匹配关系 “header”:要求请求映射所匹配的请求必须携带header请求头信息
“!header”:要求请求映射所匹配的请求必须不能携带header请求头信息
“header=value”:要求请求映射所匹配的请求必须携带header请求头信息且header = value
“header!=value”:要求请求映射所匹配的请求必须携带header请求头信息且header != value
@RequestMapping(
value = {"/testRequestMapping", "/test"}
,method = {RequestMethod.GET, RequestMethod.POST}
,params = {"username","password!=123456"}
,headers = {"..."}
)
获取请求参数
通过原生ServletAPI获取参数
将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请 求的请求报文的对象
@RequestMapping("/testParam")
public String testParam(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:"+username+",password:"+password);
return "success";
}
通过Controller方法形参获取
在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在 DispatcherServlet中就会将请求参数赋值给相应的形参
@RequestMapping("/testParam")
public String testParam(String username, String password){
System.out.println("username:"+username+",password:"+password);
return "success";
}
@RequestParam
请求参数和控制方法的形参绑定
3个参数
- value:参数名
- required:是否必须传入此参数(默认true)
- defaultValue:默认值
java
@RequestMapping("/testParam")
public String testParam(@RequestParam(value="name",required=true,defaultValue="null")String username, String password){
System.out.println("username:"+username+",password:"+password);
return "success";
}
@RequestHeader
获取请求头
将请求头信息和控制器方法的形参创建映射关系
- value:参数名
- required:是否必须传入此参数(默认true)
- defaultValue:默认值
@CookieValue
获取Cookie
将cookie数据和控制器方法的形参创建映射关系
- value:参数名
- required:是否必须传入此参数(默认true)
- defaultValue:默认值
通过pojo获取参数
可以在控制器方法的形参位置设置一个实体类类型的形参,
此时若浏览器传输的请求参数的参数名和实体类中的**属性名 ** 一致,那么请求参数就会为此属性赋值
@RequestMapping("/testpojo")
public String testPOJO(User user){
System.out.println(user);
return "success";
}
解决获取请求参数的乱码问题
解决获取请求参数的乱码问题,
使用SpringMVC提供的编码过滤器CharacterEncodingFilter,在web.xml中进行注册
SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效
<!--配置springMVC的编码过滤器-->
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
域对象共享数据
使用ServletAPI-Request域
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request){
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
使用ModelAndView-Request域
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
/**
* ModelAndView有Model和View的功能
* Model主要用于向请求域共享数据
* View主要用于设置视图,实现页面跳转
*/
ModelAndView mav = new ModelAndView();
//向请求域共享数据
mav.addObject("testScope", "hello,ModelAndView");
//设置视图,实现页面跳转
mav.setViewName("success");
return mav;
}
使用Model-request域
Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的
@RequestMapping("/testModel")
public String testModel(Model model){
model.addAttribute("testScope", "hello,Model");
return "success";
}
### 使用ModelMap-request域
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
modelMap.addAttribute("testScope", "hello,ModelMap");
return "success";
}
使用Map-request域
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
map.put("testScope", "hello,Map");
return "success";
}
向session域共享数据
ServletAPI获取
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("testSessionScope", "hello,session");
return "success";
}
向application域共享数据
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
ServletContext application = session.getServletContext();
application.setAttribute("testApplicationScope", "hello,application");
return "success";
}
SpringMVC视图
SpringMVC中的视图是View接口,视图的作用渲染数据
Thymeleaf视图解析器
若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视 图解析器解析之后所得到的是ThymeleafView
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
转发视图
forward
@RequestMapping("/testForward")
public String testForward(){
return "forward:/testHello";
}
重定向视图
redirect
@RequestMapping("/testRedirect")
public String testRedirect(){
return "redirect:/testHello";
}
视图控制器
springmvc.xml
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>
注: 当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需 要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:
<mvc:annotation-driven />
RESTful
REST:Representational State Transfer,表现层资源状态转移。
HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
请求方式过滤器
SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求
a>当前请求的请求方式必须为post
b>当前请求必须传输请求参数**_method**
请求参数**_method**的值才是最终的请求方式
在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter
web.xml
<!-- 处理请求方式的过滤器-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
获取RESTful的请求参数
@RequestMapping(value = "/employee/{id}", method = RequestMethod.GET)
public String getEmployeeById(@PathVariable("id") Integer id, Model model){
sout(id);
return "";
}
SpringMVC处理ajax请求
@RequestBody
@RequestBody可以获取请求体信息,
使用@RequestBody注解标识控制器方法的形参,当前请求的请 求体就会为当前注解所标识的形参赋值
@RequestMapping("/test/RequestBody")
public String testRequestBody(@RequestBody String requestBody){
System.out.println("requestBody:"+requestBody);
return "success";
}
@RequestBody获取json格式的请求参数
使用jackson的依赖,
将json数据直接转换成map集合或者对象…
直接在控制器方法的形参接受
//将json格式的数据转换为map集合
@RequestMapping("/test/RequestBody/json")
public void testRequestBody(@RequestBody Map<String, Object> map,
HttpServletResponse response) throws IOException {
System.out.println(map);
//{username=admin, password=123456}
response.getWriter().print("hello,axios");
}
//将json格式的数据转换为实体类对象
@RequestMapping("/test/RequestBody/json")
public void testRequestBody(@RequestBody User user, HttpServletResponse
response) throws IOException {
System.out.println(user);
//User{id=null, username='admin', password='123456', age=null,gender='null'}
response.getWriter().print("hello,axios");
}
@ResponseBody
@ResponseBody用于标识一个控制器方法,
可以将该方法的返回值直接作为响应报文的响应体响应到 浏览器
不会被视图解析器解析
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
//此时响应浏览器数据success
return "success";
}
@RestController注解
@RestController注解是springMVC提供的一个复合注解,
标识在控制器的类上,就相当于为类添加了 @Controller注解,并且为其中的每个方法添加了@ResponseBody注解
返回json数据
加上**@ResponseBody注解后返回( return )对象或者集合直接响应到浏览器的是json格式**的数据
spring上传和下载
文件下载
使用ResponseEntity实现下载文件的功能
@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/static/img/1.jpg");
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers,
statusCode);
//关闭输入流
is.close();
return responseEntity;
}
文件上传
文件上传要求form表单的请求方式必须为post,并且添加属性enctype=“multipart/form-data” SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息
-
添加依赖
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>
-
springmvc配置文件添加配置
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> </bean>
-
控制器
@RequestMapping("/testUp") public String testUp(MultipartFile photo, HttpSession session) throws IOException { //获取上传的文件的文件名 String fileName = photo.getOriginalFilename(); //处理文件重名问题 String hzName = fileName.substring(fileName.lastIndexOf(".")); fileName = UUID.randomUUID().toString() + hzName; //获取服务器中photo目录的路径 ServletContext servletContext = session.getServletContext(); String photoPath = servletContext.getRealPath("photo"); File file = new File(photoPath); if(!file.exists()){ file.mkdir(); } String finalPath = photoPath + File.separator + fileName; //实现上传功能 photo.transferTo(new File(finalPath)); return "success"; }
拦截器
SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现HandlerInterceptor
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
<bean class="com.atguigu.interceptor.FirstInterceptor"> </bean>
<ref bean="firstInterceptor"></ref>
<!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/testRequestEntity"/>
<ref bean="firstInterceptor"></ref>
</mvc:interceptor>
<!--
以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过
mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
-->
拦截器的三个抽象方法
preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返 回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
postHandle:控制器方法执行之后执行postHandle()
afterCompletion:处理完视图和模型数据,渲染视图完毕之后执行afterCompletion()
异常处理
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:
HandlerExceptionResolver
接口的实现类有:DefaultHandlerExceptionResolver和 SimpleMappingExceptionResolver
SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:
- 使用xml配置异常解析器
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--
properties的键表示处理器方法执行过程中出现的异常
properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--
exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
-->
<property name="exceptionAttribute" value="ex"></property>
</bean>
- 使用注解的异常处理
//@ControllerAdvice将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
//@ExceptionHandler用于设置所标识方法处理的异常
@ExceptionHandler(ArithmeticException.class)
//ex表示当前请求处理中出现的异常对象
public String handleArithmeticException(Exception ex, Model model){
model.addAttribute("ex", ex);
return "error";
}
}