1、SpringMVC业务流程
- Dispatcher表示前端控制器,是整个SpringMVC的控制中心,有用户发起请求,DispatcherServlet拦截
- HandlerMapping为处理器映射器,DispatcherServlet调用,HandlerMapping根据请求的url查找Handler
- HandlerExecution有一个执行链(HandlerExecutionChain),返回一个具体的Handler。(起主要作用是根据url查找控制器。)
- HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。DispatcherServlet再将Handler信息发送给HandlerAdapter。
- HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
- Handler让具体的Controller执行。
- Controller将具体的执行消息返回给HandlerAdapter,如ModleAndView(Spring MVC的底层对象,包括 Model 数据模型和 View 视图信息)。
- HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
- DispatcherServlet调用视图解析器来解析HandlerAdapter传递的逻辑视图名。
- 视图解析器将解析的视图逻辑名传递给DispatcherServlet。
- DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图)
- 最终视图将呈现给用户。
2、核心
SpringMVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架
2.1、DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet
是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
2.2、HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler
即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。```
2.3、Handler:处理器
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler
对具体的用户请求进行处理。
2.4、HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
2.5、View Resolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver
首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
2.6、View:视图
SpringMVC 框架提供了很多的 View
视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是
jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
2.7、<mvc:annotation-driven>
标签说明:
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。使用 <mvc:annotation-driven>
自动加载 RequestMappingHandlerMapping
(处理映射器)和RequestMappingHandlerAdapter
( 处 理 适 配 器 ),可 用 在 SpringMVC.xml
配 置 文 件 中 使 用<mvc:annotation-driven>
替代处理映射器和适配器的配置(一般开发中都需要该标签)。注意:我们只需要编写处理具体业务的控制器以及视图。
3、核心配置
3.1 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">
<!-- web工程核心配-->
<!-- 配置前端控制器,中央控制处理器,请求分发器,这是springMVC的核心。-->
<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:beans.xml</param-value>
</init-param>
<!-- 使当前Servlet随着当前项目的启动而启动, 启动级别1(服务器启动,这个就启动)-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--
/ 不包括静态资源,只会拦截请求。匹配所有请求(不包括.jsp)
/* 包括静态资源和请求。匹配所有请求(包括.jsp)。有些只是页面跳转不需要请求到服务器,所以一般不用 /*
-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- <welcome-file-list>-->
<!-- <welcome-file>index.jsp</welcome-file>-->
<!-- </welcome-file-list>-->
</web-app>
3.2 处理器映射器的配置,在beans.xml中
<!-- 处理器映射器: 映射请求路径-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>
3.3 处理器适配器的配置,在beans.xml中
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
</bean>
3.3 视图解析器的配置,在beans.xml中
<!-- 视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp"/>
<property name="suffix" value=".jsp"/>
</bean>
3.4 对于处理器映射器和处理器是适配器,可以一起配置代理
<!--
mvc:annotation-driven 代理处理器和映射器的配置; 自动设置映射器和适配器
标签相当于一行配置处理器映射器/适配器,不能重复配置处理器映射器/适配器
-->
<mvc:annotation-driven/>
3.5 静态资源处理 :配置项目中所需要的前端静态资源路径
<!--
静态资源处理 :配置项目中所需要的静态资源路径
-->
<mvc:resources location="/Css/" mapping="/Css/**"/>
<mvc:resources location="/Js/" mapping="/Js/**"/>
<mvc:resources location="/Images/" mapping="/Images/**"/>
<mvc:resources location="/assets/" mapping="/assets/**"/>
4、请求参数绑定
5、转化器(自定义类型转换器)
SpringMVC不能自动识别参数转换为我们需要的数据类型,浏览器报400错误,类型转换异常;
如果遇到表单提交的参数类型是特殊类型(Date),需要手动处理时,可以定义转换器 在SpringMVC中,转换器是以服务的形式存在的
- 定义转换器类型
/**
* 类型转换器,这是一个服务,需要去开启
* @author dong
* @date 2022-07-26 16:25
*/
public class MyConverter implements Converter<String, Date> {
// 类型转换的方法,这里是将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;
}
}
- 在SpringMVC配置文件中添加转换器服务
<!-- 配置转换器。这个id必须这么写(conversionService),因为springMVC底层是找的这个id-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.dong.converter.MyConverter"/>
</list>
</property>
</bean>
- 开启转换器服务
mvc:annotation-driven标签相当于一行配置处理器映射器/适配器
不能重复配置处理器映射器/适配器
<mvc:annotation-driven conversion-service="conversionService"/>
六、Controller层方法的返回值
6.1 String
表示返回的资源路径
6.2 ModelAndView
设置返回的数据
设置返回资源路径
有几个需要对比一下(数据都是存进request域):
- Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;
- ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特 性;
- ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。
6.3 Void
返回值为空
使用ServletAPI实现转发和重定向时都可以不设置返回值
6.4 对象类型
当前端页面需要展示数据且数据需要为Json格式时,可以返回对象类型
在方法上使用@ResponseBody
注解 ,有这个就不会走视图解析器,会直接返回一个字符串
七、SpringMVC实现转发与重定向
- 请求转发01
@RequestMapping("r1")
public String forward01(@ModelAttribute("msg") String msg,Model model){
// @ModelAttribute 直接将值存入域中,相当于model.addAttribute()
System.out.println(msg);
// 不需要视图解析器
return "forward:main.jsp";
}
- 请求转发02
@RequestMapping("r2")
public String forward02(){
// 这种方法需要视图解析器
return "forward:main";
}
- 重定向01
@RequestMapping("r3")
public String redirect01(){
// 重定向
return "redirect:main.jsp";
}
- 重定向02
@RequestMapping("r4")
public String redirect02(){
// 重定向 定向到r1的请求
return "redirect:r1";
}
- 重定向03
// 重定向传值,SpringMVC独特的方式
@RequestMapping("r5")
public String redirect03(RedirectAttributes attributes){
// attributes.addAttribute("msg","redirect info"); // 这样写,参数会暴露在地址栏中
attributes.addFlashAttribute("msg","info"); // 参数不会暴露在地址栏中
return "redirect:r1";
}
八、SpringMVC的文件上传与下载
需要的依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
配置文件上传处理器
<!-- 文件上传处理器 id就是这个 不能改-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
<property name="maxUploadSize" value="102400000"/>
</bean>
8.1 单个文件上传
public static String FILE_PATH = "D:\\java\\第三阶段\\uploadFile\\";
// 单给文件上传
@RequestMapping("uploadFile")
public void uploadFile(MultipartFile file) throws IOException {
// 接收使用MultipartFile,获取原文件名
String originalFilename = file.getOriginalFilename();
// 根据UUID生成唯一文件名称
String fileName = UUID.randomUUID() + originalFilename;
// 上传位置
File fileDir = new File(FILE_PATH);
// 文件上传地址:目录+文件名
file.transferTo(new File(fileDir, fileName));
System.out.println("上传成功!");
}
8.2 多个文件上传
前端传递多个file类型的input,name属性一样,后端接收就像接收数组一样
// 多文件上传
@RequestMapping("uploadManyFile")
public void uploadManyFile(@RequestParam("file") MultipartFile[] files) throws IOException {
if (files != null && files.length > 0) {
for (MultipartFile file : files) {
// 接收使用MultipartFile,获取原文件名
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID() + originalFilename;
// 上传位置
File fileDir = new File(FILE_PATH);
// 文件上传地址:目录+文件名
file.transferTo(new File(fileDir, fileName));
System.out.println("上传成功!");
}
} else {
System.out.println("上传失败");
}
}
8.3 文件下载
- 对文件名进行硬编码
- 创建
HttpHeaders
对象
表示 HTTP 请求或响应标头的数据结构,将 String 标头名称映射到 String 值列表,还为常见的应用程序级数据类型提供访问器 - 利用
HttpHeaders
对象设置响应的是附件,并设置附件的名称(这个名称需要进行硬编码,否则会乱码) - 利用
HttpHeaders
对象,设置文本内容类型application/octet-stream
- 返回一个响应实体对象
ResponseEntity
,在这个对象中要传入文件的字节数组、响应标头httpHeaders对象、响应状态码
// 文件下载
@RequestMapping("downloadFile")
public ResponseEntity<byte[]> downloadFile() throws IOException {
// 文件
String fileName = "小丑.jpg"; // 文件名也可以从前端传递
File file = new File(FILE_PATH + fileName);
// 对文件名进行硬编码,解决文件下载时,浏览器显示名称的中文乱码问题
String newName = new String(fileName.getBytes("gbk"), StandardCharsets.ISO_8859_1);
// 创建Header对象 请求头 请求体
HttpHeaders httpHeaders = new HttpHeaders();
// 设置成附件,并且下载之后的文件名为newName
httpHeaders.setContentDispositionFormData("attachment", newName);
// 设置文本内容类型。 1 octet = 8 bit
httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<byte[]>(
FileUtils.readFileToByteArray(file),
httpHeaders,
HttpStatus.OK
);
}
九、统一异常处理
9.1 在当前类中
// 表示当前Controller类中的异常处理
@ExceptionHandler
public String error(Exception e, Model model) {
// 处理异常
model.addAttribute("errmsg", e.getMessage());
return "error";
}
9.2 SpringMVC提供的全局异常处理器
在bean配置文件中配置:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">error</prop> <!-- 这个error表示异常处理的页面(视图名字)-->
</props>
</property>
</bean>
9.3 自定义异常处理器
实现HandlerExceptionResolver
接口,重写resolveException
方法
@Override
public ModelAndView resolveException(javax.servlet.http.HttpServletRequest httpServletRequest,
javax.servlet.http.HttpServletResponse httpServletResponse,
Object o, Exception e) {
// 做一些异常处理的逻辑
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("errmsg",e.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
十、拦截器
10.1 拦截器实现
实现HandlerInterceptor
接口,重写三个方法。
拦截器:
/**
* @author dong
* @date 2022-07-28 09:45
*/
public class MyInterceptor implements HandlerInterceptor {
// 预处理方法,请求处理之前执行
// 返回值为boolean类型,表示是否放行,true 放行 false拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandler方法执行");
return true;
}
// 后处理方法,请求处理后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法执行");
}
// 在dispatcherServlet处理后执行,做清理工作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion方法执行");
}
}
请求:
@RequestMapping("hello")
public void hello(){
System.out.println("请求被处理");
}
配置
<!-- 拦截器配置, 拦截器链-->
<mvc:interceptors>
<mvc:interceptor>
<!--
/** 表示任意路径下的任意请求
/* 表示拦截直接子路径下的请求(一级请求),不包括子路径下的路径
比如:/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截
/admin/** 拦截的是/admin/下的所有
-->
<mvc:mapping path="/**"/>
<!--bean配置的就是拦截器-->
<bean id="interceptor" class="com.dong.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
执行结果
10.2 多个拦截器执行问题
类似于栈,先执行的后结束
十一、RestFul风格
-
概念
Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制 -
功能
资源:互联网所有的事物都可以被抽象为资源
资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。
分别对应 添加、 删除、修改、查询。 -
分类
-
幂等:抽象代数中概念,任意执行多次所产生的影响均和一次执行的影响相同
-
get:请求安全和幂等(请求body在地址栏上),向服务器发送数据请求,从而获取数据,只会对数据进行查询,不会进行修改增加,不论进行多少次操作结果都是一样
-
post:请求不安全和不幂等(请求数据在报文body中),向服务器发送数据请求,从而改变数据,每次请求都会创建新的内容,几乎所有提交操作都是Post请求
-
put:请求安全和幂等(请求数据在报文body中),向服务器发送数据请求,从而改变数据,但是不会新增数据,不论进行多少次操作都不有有新数据的产生。
-
delete:请求安全和幂等(请求数据在报文body中),用来删除某一行的
-
patch:请求不安全和幂等(请求数据在报文body中),与put类似,发送一个修改数据的请求,put更新数据时是全部更新,patch只进行部分更新,post的话其它属性会被设置为null
-
options:请求安全和幂等,用于url验证,验证接口是否正常
-
trace:请求安全和幂等,restful框架中使用,回显服务器收到的请求,客户端能看数据在哪些中间服务器
-
head:与get方法类似,但不返回message body内容,仅仅是获取资源部分内容(content-type)
-