您现在的位置是:首页 >其他 >Spring MVC网站首页其他
Spring MVC
Spring MVC
什么是 Spring MVC
Spring的Web模块。方便我们开发JavaWeb程序
M:model 指的是JavaBean,就是我们Java程序中的处理业务的类(实体类、业务类)
V: View 视图 指的是前端渲染的页面,html、jsp等
C: controller 控制器,也就是接受请求的类
第一部分 Holle World
通过spring MVC 实现一个简单的web页面跳转
1. 思路
① 导入相关依赖
② 配置spring.xml文件
③ 编写前端控制器以及前端文件
④ 配置web.xml文件(映射关系)
⑤ 启动服务器测试
2. 实现
① 导入相关依赖
在这一部 导入我么要用到的依赖
<dependencies>
<!-- spring-web依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- logback日志依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- servlet依赖 因为tomcat已经提供所以这里设置为provided-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- spring5的thymeleaf依赖 要是用到前端页面所以使用模板-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
② 配置spring.xml文件
首先要添加 spring 的注解扫描器
其次配置 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="com.xxxx"/>
<!-- 配置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>
</beans>
注:注意自己视图的前缀和后缀
③ 编写前端控制器以及前端文件
1.我们先来实现一个 index 页面的跳转,也就是前端控制器
@Controller
public class UserController {
@RequestMapping("/")
public String index(){
// 因为已配置解析器 所以我们返回index就表示去找 /WEB-INF/templates/index.html 文件
return "index";
}
}
2.实现 index页面注意路径
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
④ 配置web.xml文件(映射关系)
配置 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">
<!-- 配置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>
<!-- 映射请求和我们的前端控制器 “/”代表接受html、css、js等请求 除了.jsp-->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
注:每一步的意思都在上面
⑤ 启动服务器测试
启动自己配置的服务器
我的上下文路径是demo01,所以 我的访问路径是 (http://localhost:8080/demo01/)
正确的结果就是访问道Holle World!!
第二部分 @RequestMapping
无比重要的 @RequestMapping
作用:连接请求和方法体,使相应的请求能够找到相应的处理方法
1. RequestMapping的使用
他可以放在类上,也可以放在方法上
放在类上
表示该类中所有方法的初始请求路径
@Controller
@RequestMapping("test") // 该类中的所有请求映射都要加上 test
public class UserController {
@RequestMapping("/target") // 请求路径为 test/targert
public String totarget(){
return "target";
}
}
放在方法上
表示该方法的具体访问路径
@Controller
@RequestMapping("test")
public class UserController {
@RequestMapping("index") // 每一个方法都有自己专属的路径
public String index(){
return "index";
}
@RequestMapping("/target") //
public String toTarget(){
return "target";
}
}
2. 属性
@RequestMapping有多个属性
① Value
String[] value() default {}
表示一个方法请求映射的路径,他是一个数组,满足任意一个路径即可匹配成功 Value是 RequestMapping属性中的必填项
@RequestMapping(value = {"/","index"}) // 一个方法可以处理多个请求 只要符合其中一个就能匹配上
public String index(){
return "index";
}
@RequestMapping("/target") // 当RequestMapping只有value这一个属性且value只有一个值时 可以省略不写
public String toTarget(){
return "target";
}
② method
RequestMethod[] method() default {};
表示该映射可以处理什么类型的请求,是一个数组,只要满足任意一个类型即可匹配,不写表示所有类型都可以匹配
@RequestMapping(value = {"index"},method = {RequestMethod.DELETE})
public String index(){
return "index";
}
method的值有一个常量类 对应不同的请求方式
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
为了简化写法,RequestMapping派生出,处理不同请求类型的注解
- @GetMapping(“index”) 处理GET请求
- @PostMapping(“index”) 处理POST请求
- @PutMapping(“index”) 处理PUT请求
- @DeleteMapping(“index”) 处理DELETE请求
③ params
String[] params() default {};
表示该映射的 参数条件是一个数组,必须所有的参数都有才可以匹配
@RequestMapping(value = {"param"},params = {"param"})
public String toParam(){
return "param";
}
属性值:
-
params = {“xxxx”} 表示参数列表必须含有 xxxx 才能匹配
-
params = {“!xxxx”} 表示参数列表不能含有 xxxx 才能匹配
-
params = {“xxx=xxx”} 表示参数列表必须有参数 xxx=xxx 才能匹配
-
params = {“xxx!=xxx”} 表示参数列表不能有参数 xxx=xxx 才能匹配
④ headers
String[] headers() default {};
表示该映射的 参数条件是一个数组,必须满足所有条件才可以匹配
@RequestMapping(value = {"header"},headers = {"host"})
public String header(){
return "header";
}
属性值:
-
headers= {“xxxx”} 表示参数列表必须含有 xxxx 才能匹配
-
headers= {“!xxxx”} 表示参数列表不能含有 xxxx 才能匹配
-
headers= {“xxx=xxx”} 表示参数列表必须有参数 xxx=xxx 才能匹配
-
headers= {“xxx!=xxx”} 表示参数列表不能有参数 xxx=xxx 才能匹配
⑤ ant风格和占位符
Spring MVC支持 ant风格和占位符 所以可以对我们的请求路径进行一些扩展
只针对 Value属性
ant风格:
- ? 代表任意一个字符
@RequestMapping("save?") // 他就表示 save1 save2都可以
- *代表任意个字符
@RequestMapping("save*") // 他就表示 save save1 save123 都可以
- ** 有点特殊
@RequestMapping("**/sa**ve") // **/ 代表任意路径
// 与其他字符连接就是普通的** 所以他的访问路径是 任意路径下的 sa**ve
站位符:
站位符就是{}
他要配合注解@PathVariable()
使用
@RequestMapping("delete/{id}")
public String delete(@PathVariable("id") Long id){
return "delete";
}
“delete/{id}” 代表 请求路径delete后的一个值就叫id
@PathVariable(“id”) 代表 获取占位符名为 “id” 的值 并赋值给修饰的属性(属性类型要一致)
所以 delete/23 中的 23就会赋值给属性id
3. 关于一些请求参数
@RequestMapping注解的参数获取有很多方法
① HttpServletRequest
参数 HttpServletRequest代表当前请求,在servlet中能用的方法,他都有
@RequestMapping("/")
public String index(HttpServletRequest request) {
HttpSession session = request.getSession();
return "index";
}
② 同名参数
当形参和前端给定的参数名一致时,会自动赋值
@RequestMapping("/")
public String index(String userName,String password) { // 当前端传递的参数为userName以及password时,Spring MVC会自动赋值,类型不同会报错
System.out.println(userName+"---"+password);
return "index";
}
④ 不同名参数 @RequestParam
注解@RequestParam,建立参数与形参映射关系
有些时候我们传递的参数名和形参名并不一致,就需要借助 Spring MVC提供的注解来解决
@RequestMapping("/")
public String index(@RequestParam(value = "user_name") String userName) { // 他的意思代表会将 名为user_name的参数值赋值给userName
System.out.println(userName);
return "index";
}
@RequestParam有多个属性
- value 将名为value值得参数值赋值给RequestParam注解标注的形参,当属性只有value时,可以省略不写
- name 和value功能一样
- required 标注该参数是否必要(true/false),默认为true
- defaultValue 表示该参数的默认值,当该参数没有传递值,或是为空时,将使用默认值赋值
例:
@RequestMapping("/")
public String index(@RequestParam(value = "user_name",required = false,defaultValue = "默认值") String userName) {
System.out.println(userName);
return "index";
}
⑤ 头信息参数 @RequestHeader
注解@RequestHeader,建立请求头信息和形参的映射关系
当我们想获取请求头中特定消息时,可以使用@RequestHeader注解*
@RequestMapping("/")
public String index(String userName,@RequestHeader("host") String host) {
System.out.println(userName);
System.out.println(host);
return "index";
}
@RequestHeader有多个属性
- value 将名为value值得参数值赋值给RequestParam注解标注的形参,当属性只有value时,可以省略不写
- name 和value功能一样
- required 标注该参数是否必要(true/false),默认为true
- defaultValue 表示该参数的默认值,当该参数没有传递值,或是为空时,将使用默认值赋值
例:
@RequestMapping("/")
public String index(String userName,@RequestHeader(value = "host",required = false,defaultValue = "8080") String host) {
System.out.println(userName);
System.out.println(host);
return "index";
}
⑥ @CookieValue
注解CookieValue,建立cookie和形参的映射
通过该注解,我们可以获取cookie的值,进而获取session的值
@PostMapping("cook")
public String cook(HttpServletRequest request,@CookieValue("JSESSIONID") String JSESSIONID){
System.out.println(JSESSIONID);
return "target";
}
@CookieValue有多个属性
- value 将名为value值得参数值赋值给RequestParam注解标注的形参,当属性只有value时,可以省略不写
- name 和value功能一样
- required 标注该参数是否必要(true/false),默认为true
- defaultValue 表示该参数的默认值,当该参数没有传递值,或是为空时,将使用默认值赋值
4. 乱码问题
① post请求乱码
在web.xml文件中配置过滤器
<!--在CharacterEncodingFilter中哦我们可以配置编码 可以点进去看一下源码,就清楚了-->
<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>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第三部分 常用对象
1. 共享数据
可以在共享数据的对象
① HttpServletRequest
javax.servlet.http.HttpServletRequest
属于是传统方法
作用域:一次请求
@RequestMapping("/request")
public String request(HttpServletRequest request){
request.setAttribute("hell","Hello,world!!");
return "target";
}
② ModelAndView
org.springframework.web.servlet.ModelAndView
Spring MVC提供的方法**(推荐使用)**,直接返回一个ModelAndView对象
作用域:一次请求
public ModelAndView modelAndView(ModelAndView modelAndView) {
modelAndView.addObject("text","Hello,world!!");
modelAndView.setViewName("target");
return modelAndView;
}
③ Model
org.springframework.ui.Model
Spring MVC提供的方法
作用域:一次请求
底层实现是org.springframework.validation.support.BindingAwareModelMap
,与ModelMap、Map<String,Object>一致
该种方式最后依旧是将数据视图封装为一个ModelAndView对象返回
public String index(Model model) {
model.addAttribute("text","Hello,world!!");
return "target";
}
④ Map<String,Object>
java.util.Map
Spring MVC提供的方法
作用域:一次请求
底层实现是org.springframework.validation.support.BindingAwareModelMap
,与ModelMap、Model一致
该种方式最后依旧是将数据视图封装为一个ModelAndView对象返回
public String index(Map<String,Object> map) {
map.put("text","Hello,world!!");
return "target";
}
⑤ ModelMap
org.springframework.ui.ModelMap
Spring MVC提供的方法
作用域:一次请求
底层实现是org.springframework.validation.support.BindingAwareModelMap
,与Map<String,Object>、Model一致
该种方式最后依旧是将数据视图封装为一个ModelAndView对象返回
public String index(ModelMap modelMap) {
modelMap.addAttribute("text","Hello,world!!");
return "target";
}
⑥ session
作用域:一次会话
@RequestMapping("/submit")
public void submit(User user, HttpSession session){
session.setAttribute("name","梁林宗");
session.getAttribute("name");
System.out.println(user);
}
⑦ application
通过application共享数据,application是作用域是整个程序,很大,很少用
@RequestMapping
public void application(HttpSession session){
ServletContext application = session.getServletContext();
application.setAttribute("name","梁林宗");
application.getAttribute("name");
}
第四部分 视图 View
当我们在controller返回一个字符串时,springmvc会对其进行解析
1. forward
当返回值有forward前缀时会被解析为转发视图InternalResourceView
@RequestMapping("/submit")
public String submit(ModelAndView modelAndView, HttpSession session){
session.setAttribute("name","梁林宗");
return "forward:/test/";
}
转发特点:
- 一次浏览器请求,浏览器地址栏不会改变
- 视为一次会话,可以通过session共享数据
2. redirect
当返回值有redirect前缀时会被解析为重定向视图RedirectView
@RequestMapping("/submit")
public String submit(ModelAndView modelAndView, HttpSession session){
session.setAttribute("name","梁林宗");
return "redirect:/test/";
}
重定向特点:
- 视为两次会话,地址栏会发生变化
3. 没有前缀
如果没有前缀,则会被在配置文件中配置的模板解析器解析。
如果没有前缀,也没有配置模板,则会解析为InternalResourceView
第五部分 RestFul风格
1. 什么是RestFul风格
是一种规范
不同的请求,对应不同的目的,url一致
get——获取数据
post——保存数据
put——修改数据
delete——删除数据
例:
@PostMapping("/user/")
public R<Void> save(@RequestBody User user){
....
}
@PutMapping("/user/")
public R<Void> update(@RequestBody User user){
....
}
@DeleteMapping("/user/{id}")
public R<Void> delete(@PathVariable("id")Long id){
....
}
@GetMapping("/user/{id}")
public R<User> getDetail(@PathVariable("id")Long id){
....
}
注意:
- 默认情况下request只能处理post、get请求,若要处理put就需要配置过滤器
2. put、delete请求支持配置
① 配置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>
注意:这个过滤器写在CharacterEncodingFilter下边,不然编码就无效了(HiddenHttpMethodFilter获取了一个叫_method的参数)
② 配置请求方式
通过查看HiddenHttpMethodFilter源码我们发现
过滤器是这样处理的
- 第一步 HttpServletRequest requestToUse = request 替换原来的request,对其中的请求方式进行修改
- 当请求为post时他根据一个叫**”_method“的参数的值**进行进一步判断
- 如果这个值在(PUT、DELETE、PATH)中那就会根据这个值替换requset中的请求方法
所以我们要在请求表单为POST请求的基础上配置一个"_method"的参数
例: 这样就可以处理delete请求了
<h1>DELETE</h1>
<form th:action="@{/user/2}" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="点我">
</form>
第六部分 常用注解
1. 标识注解
表示一个这个类是个什么东西
① Component
类注解表示这个类是一个组件,会被spring容器扫描
@Component
public @interface Controller {
@AliasFor(annotation = Component.class)
String value() default "";
}
② Controller
类注解表示这个类是控制层
@Controller
public class StudentController {
}
属性
- value,指定这个类实例化后的名称,一般使用默认的,就是首字母小写的类名
③ Service
类注解表示这个类是业务层
@Service
public class StudentService {
}
属性
- value,指定这个类实例化后的名称,一般使用默认的,就是首字母小写的类名
④ Repository
类注解表示这个类是持久层
@Repository // 一般都用@Mapper了
public class StudentMapper {
}
属性
- value,指定这个类实例化后的名称,一般使用默认的,就是首字母小写的类名
⑤ Configuration
类注解
表示这个类是持久层
@Configuration
public class MyBatisConfig {
}
属性
- value,指定这个类实例化后的名称,一般使用默认的,就是首字母小写的类名
- proxyBeanMethods,默认为true,规定其中的@Bean标注的返回对象是否是单例模式(就是说,就算你直接调用这个方法,还是返回spring容器中的那个对象)
2. 请求相关注解
就是与一个请求方式或者请求参数有关的注解
① RequestMapping
类注解、方法注解
RequestMapping家族就不赘述了,就是对请求进行映射
② RequestBody
参数注解
接收请求体的参数
请求方式必须是POST
@PostMapping("/post")
public String requestBody(@RequestBody String str){ // 所有的参数都将以一个字符串形式拼接
System.out.println(str);
return "index";
}
@PostMapping("/post")
public String requestBody(@RequestBody User user){ // 参数如果与user的属性名一致则直接赋值
System.out.println(user);
return "index";
}
属性
- boolean required,参数是否必需存在,默认为true
3. 参数注解
① @RequestParam
标注这个形参是用来接受前端参数的
当参数是 List/Map 集合时 必须要使用@RequestParam,不然springMvc不认识
②@PathVariable
从路径中获取参数
如果路径中的参数与参数同名则不用标注value,否则要指定value
@GetMapping("get/{id}")
public R getById(@PathVariable String id){
return R.ok().data("item",service.getById(id));
}
@GetMapping("get/{id}")
public R getById(@PathVariable("id") String uId){ // 路径中的参数与预定的参数名不一致需要指定
return R.ok().data("item",service.getById(id));
}
第七部分 拦截器
拦截器可以对对我们请求进行拦截,根据条件对他们设置放行
拦截器的三个方法
- preHandle 在controller之前运行,方法有返回值,返回为true表示放行
- postHandle 在controller之后运行,方法无返回值,可做一些其他附加操作
- afterCompletion 视图渲染之后运行,方法无返回值,可做一些其他附加操作
1.如何实现一个拦截器
① 创建一个拦截器
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");
}
}
② 注册拦截器
方式一
<mvc:interceptors>
<!-- 以注册bean的方式,配置拦截器,拦截所有请求-->
<bean class="com.llz.mvc.interceptors.MyInterceptor"/>
<!-- 以bean引用的的方式注册拦截器,(给拦截器加注解Component),拦截所有请求-->
<ref bean="myInterceptor"/>
<!-- 以以更清晰的方式注册拦截器 推荐使用-->
<mvc:interceptor>
<!-- 设置要拦截的路径 ”/*“拦截所有一层目录 例如:/a /b /c ”/**“拦截所有-->
<mvc:mapping path="/a"/>
<mvc:mapping path="/c"/>
<!-- 设置不拦截的路径-->
<mvc:exclude-mapping path="/b"/>
<!-- 配置对应的拦截器 两种方式任选其一-->
<ref bean="myInterceptor"/>
<bean class="com.llz.mvc.interceptors.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
方式二
@Configuration
public class MyAppConfig implements WebMvcConfigurer {
//添加拦截器对象,注入到容器中
@Override
public void addInterceptors(InterceptorRegistry registry) {
//创建拦截器对象
HandlerInterceptor interceptor=new LoginInterceptor();
HandlerInterceptor interceptor2=new LoginInterceptor2();
//指定拦截的请求uri地址
String[] path={"/**"};
//指定不拦截的地址,不会经过拦截器
String[] excludePath ={"/user/login"};
// 注册顺序,就代表拦截器拦截顺序
registry.addInterceptor(interceptor)
.addPathPatterns(path)
.excludePathPatterns(excludePath);
registry.addInterceptor(interceptor2)
.addPathPatterns(path)
.excludePathPatterns(excludePath);
}
}
2. 多拦截器执行顺序
以配置两个拦截器为例
<mvc:interceptors>
<bean class="com.llz.mvc.interceptors.FirstInterceptor"/>
<bean class="com.llz.mvc.interceptors.SecondInterceptor"/>
</mvc:interceptors>
① 所有的拦截器都放行
FirstInterceptor+++++preHandle
SecondInterceptor+++preHandle
SecondInterceptor+++postHandle
FirstInterceptor+++++postHandle
SecondInterceptor+++afterCompletion
FirstInterceptor+++++afterCompletion
很明显
- 拦截器的执行和我们在配置文件中配置的顺序一致
- preHandle按正序执行 postHandle、afterCompletion按倒叙执行
- 可以比拟一下拆盒子
② 第二个拦截器不放行
FirstInterceptor+++++preHandle
SecondInterceptor+++preHandle
FirstInterceptor+++++afterCompletion
很明显
- 拦截器的执行和我们在配置文件中配置的顺序一致
- preHandle方法:不放行以及他之前的拦截器能运行
- postHandle方法:所有都不运行
- afterCompletion:不放行之前的拦截器运行
第八部分 springMVC 流程
1. DispatcherServlet初始化流程
dispatherServlet本质还是一个servlet,初始化方法就是init()
从HttpServletBean开始了解init方法即可
途中箭头所标“调用”有无,还请自己前往源码了解
2. DispatcherServlet服务流程
servlet只有一个服务方法就是service
① 服务流程图
绿色代表spring的类
通过一系列的调用和实现,在DispatherServlet的doDistpath方法中完成服务
② doDispatch 方法流程
doDispatch 逻辑流程,其实就是mvc处理一个请求的流程
截取方法重要代码进行说明
// 声明一个处理器执行链对象
HandlerExecutionChain mappedHandler = null;
// 模型视图对象
ModelAndView mv = null;
// 处理过程中的异常
Exception dispatchException = null;
// HandlerMapping一脉的对象通过DispatcherServlet解析出来的url匹配到对应的Handler对象以及与该Handler相关的拦截器对象(就是匹配到url对应的controller以及相关拦截器)
mappedHandler = getHandler(processedRequest);
// DispatcherServlet根据得到的Handler获取该Handler的处理适配器HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理器执行链调用所有拦截器的PreHandle方法,判断该请求是否被拦截
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// HandlerAdapter通过handle方法完成controller(这一步有很多操作,比如spring帮我们获取参数就是在这一步完成)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 处理器执行链调用所有拦截器的PostHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 调用本类的processDispatchResult方法 完成最终步骤
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
//以下是方法processDispatchResult的内容 =======================================
// 判断是否有异常,有的话在这一步进行处理
if (exception != null) {
...
}
// 调用本类的render方法完成视图渲染,判断你需要用什么视图解析等等
render(mv, request, response);
// 最后一步 处理器执行链调用所有拦截器的afterCompletion方法
mappedHandler.triggerAfterCompletion(request, response, null);
3. DispatcherServlet 处理请求流程
-
DispatcherServlet 解析url,将其封装为一个对象,交给HandlerMapping去寻找对应的映射
a.如果不映射存在,就把url交给配置的默认servlet去解析(如果没有配置默认servlet,则抛异常),如果默认的servlet也没有找到对应的映射则抛出异常
b.如果映射存在,则HandlerMapping做下一步处理
-
HandlerMapping根据url找到能处理这个url的Handler(controller)以及与之相关的拦截器,将这些数据封装为一个处理器执行链返回
-
DispatcherServlet 根据Handler获取该Handler的执行器HandlerAdapter
-
处理器执行链调用拦截器的prehandle方法判断该请求是否被拦截
-
HandlerAdapter调用handle方法去调用controller
- 请求参数获取类型转换、格式化等等
- 执行controller最后返回一个ModelAndView
- HttpMessageConverter 提供给我们一些注解 比如:@RequestBody、@ResponseBody等等
-
处理器执行链调用拦截器的posthandle方法
-
对ModelAndView存在的异常进行处理,以及寻找合适的视图渲染
-
处理器执行链调用拦截器的afterCompletion方法
第九部分 部分问题解决
1. 乱码问题
2. 返回json字符串
// 异常
org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.llz.mvc.entity.Student
① 导入json依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
② 检查是否开启mvc注解驱动
如果没有开启。在mvc配置文件中加上
<mvc:annotation-driven/>
③ 检查controller格式
@RequestMapping("/body")
@ResponseBody
public Student body() throws IOException {
return new Student("宗笛",1);
}
④ 检查实体类
检查自己返回的实体类是否有 get/set方法 没有的话依然会报错
3. 文件上传
① 编写接口
@RequestMapping("/fileUpload")
@ResponseBody
public String body(MultipartFile file) throws IOException { // 上传文件的参数名要与 形参名一致,也就是说得是“file”
// 获取上传文件名
String filename = file.getOriginalFilename();
// 断言文件名不能为null
assert filename != null;
// 获取文件后缀
String suffixFileName = filename.substring(filename.lastIndexOf("."));
// 获取uuid 用于拼接文件名 保证上传文件不会因名称一致而被覆盖
String uuid = UUID.randomUUID().toString();
// 创建上传位置
File newFile = new File("C:\Users\97540\Desktop\"+uuid+suffixFileName);
// 完成复制读写
file.transferTo(newFile);
return "success";
}
② 添加上传依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
③ 配置mvc文件解析器
因为我们上传的文件是File,想要让其映射到MultipartFile,得配置解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
第十部分 快速搭建
1. pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>demo03</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<!-- spring-web依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- logback日志依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- servlet依赖 因为tomcat已经提供所以这里设置为provided-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- spring5的thymeleaf依赖-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
</project>
2.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">
<!--在CharacterEncodingFilter中哦我们可以配置编码 可以点进去看一下源码,就清楚了-->
<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>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--请求方式处理器,可以解析put delete path类型请求-->
<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:spring.xml</param-value>
</init-param>
<!--将DispatcherServlet初始化提前到容器执行时-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--映射请求和我们的前端控制器 “/”代表接受html、css、js等请求 除了.jsp-->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3. springmvc-config.xml
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.llz.springmvc"/>
<!-- 配置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>
</beans>
framework.org/schema/context"
xmlns:mvc=“http://www.springframework.org/schema/mvc”
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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd”>
<context:component-scan base-package="com.llz.springmvc"/>
<!-- 配置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>
```