您现在的位置是:首页 >其他 >Spring MVC网站首页其他

Spring MVC

谜语人负宗笛 2024-06-17 10:48:36
简介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/";
}

转发特点:

  1. 一次浏览器请求,浏览器地址栏不会改变
  2. 视为一次会话,可以通过session共享数据

2. redirect

当返回值有redirect前缀时会被解析为重定向视图RedirectView

@RequestMapping("/submit")
public String submit(ModelAndView modelAndView, HttpSession session){
    session.setAttribute("name","梁林宗");
    return "redirect:/test/";
}

重定向特点:

  1. 视为两次会话,地址栏会发生变化

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){
    ....
}

注意:

  1. 默认情况下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源码我们发现

过滤器是这样处理的

  1. 第一步 HttpServletRequest requestToUse = request 替换原来的request,对其中的请求方式进行修改
  2. 当请求为post时他根据一个叫**”_method“的参数的值**进行进一步判断
  3. 如果这个值在(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 {
    
}

属性

  1. value,指定这个类实例化后的名称,一般使用默认的,就是首字母小写的类名
③ Service

类注解表示这个类是业务层

@Service
public class StudentService {
    
}

属性

  1. value,指定这个类实例化后的名称,一般使用默认的,就是首字母小写的类名
④ Repository

类注解表示这个类是持久层

@Repository   // 一般都用@Mapper了
public class StudentMapper {
    
}

属性

  1. value,指定这个类实例化后的名称,一般使用默认的,就是首字母小写的类名
⑤ Configuration

类注解

表示这个类是持久层

@Configuration   
public class MyBatisConfig {
    
}

属性

  1. value,指定这个类实例化后的名称,一般使用默认的,就是首字母小写的类名
  2. 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";
}

属性

  1. 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));
}

第七部分 拦截器

拦截器可以对对我们请求进行拦截,根据条件对他们设置放行

拦截器的三个方法

  1. preHandle 在controller之前运行,方法有返回值,返回为true表示放行
  2. postHandle 在controller之后运行,方法无返回值,可做一些其他附加操作
  3. 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

很明显

  1. 拦截器的执行和我们在配置文件中配置的顺序一致
  2. preHandle按正序执行 postHandle、afterCompletion按倒叙执行
  3. 可以比拟一下拆盒子
② 第二个拦截器不放行
FirstInterceptor+++++preHandle
SecondInterceptor+++preHandle
FirstInterceptor+++++afterCompletion

很明显

  1. 拦截器的执行和我们在配置文件中配置的顺序一致
  2. preHandle方法:不放行以及他之前的拦截器能运行
  3. postHandle方法:所有都不运行
  4. 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 处理请求流程

  1. DispatcherServlet 解析url,将其封装为一个对象,交给HandlerMapping去寻找对应的映射

    a.如果不映射存在,就把url交给配置的默认servlet去解析(如果没有配置默认servlet,则抛异常),如果默认的servlet也没有找到对应的映射则抛出异常

    b.如果映射存在,则HandlerMapping做下一步处理

  2. HandlerMapping根据url找到能处理这个url的Handler(controller)以及与之相关的拦截器,将这些数据封装为一个处理器执行链返回

  3. DispatcherServlet 根据Handler获取该Handler的执行器HandlerAdapter

  4. 处理器执行链调用拦截器的prehandle方法判断该请求是否被拦截

  5. HandlerAdapter调用handle方法去调用controller

    1. 请求参数获取类型转换、格式化等等
    2. 执行controller最后返回一个ModelAndView
    3. HttpMessageConverter 提供给我们一些注解 比如:@RequestBody、@ResponseBody等等
  6. 处理器执行链调用拦截器的posthandle方法

  7. 对ModelAndView存在的异常进行处理,以及寻找合适的视图渲染

  8. 处理器执行链调用拦截器的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>
```
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。