Spring-web-Mvc

文章目录

目录

文章目录

前言

[1 . 什么是Spring MVC?](#1 . 什么是Spring MVC?)

[1.1 MVC定义](#1.1 MVC定义)

[1.2 主要作用](#1.2 主要作用)

[2. Spring MVC 接受响应数据](#2. Spring MVC 接受响应数据)

[2.1 @RequestMapping注解配置访问路径](#2.1 @RequestMapping注解配置访问路径)

[2.2 请求](#2.2 请求)

[2.2.1 传递单个参数](#2.2.1 传递单个参数)

[2.2.2 传递多个参数](#2.2.2 传递多个参数)

[2.2.3 传递对象](#2.2.3 传递对象)

[2.2.4 后端参数重命名(后端参数映射)](#2.2.4 后端参数重命名(后端参数映射))

[2.2.5 传递数组](#2.2.5 传递数组)

[2.2.6 传递集合](#2.2.6 传递集合)

[2.2.7 传递JSON数据](#2.2.7 传递JSON数据)

[2.2.8 获取URL中参数@PathVariable](#2.2.8 获取URL中参数@PathVariable)

[2.2.9 上传文件@RequestPart](#2.2.9 上传文件@RequestPart)

[2.2.10 接受cookie](#2.2.10 接受cookie)

[2.2.11 接受请求头](#2.2.11 接受请求头)

[2.3 响应](#2.3 响应)

@ResponseBody注解

@RestController注解

[3.Spring MVC执行流程](#3.Spring MVC执行流程)

4.DispatcherServlet源码解读

总结



前言

大家好,今天给大家介绍一下mvc框架


1 . 什么是Spring MVC?

官方对Spring MVC的描述是: Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从⼀开始就包含在 Spring 框架中。它的 正式名称"Spring Web MVC"来⾃其源模块的名称(Spring-webmvc),但它通常被称为Spring MVC.

在控制层框架历经Strust、WebWork、Strust2等诸多产品的历代更迭之后,目前业界普遍选择了SpringMVC作为Java EE项目表述层开发的首选方案。之所以能做到这一点,是因为SpringMVC具备如下显著优势:

  1. Spring 家族原生产品,与IOC容器等基础设施无缝对接
  2. 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
  3. 代码清新简洁,大幅度提升开发效率
  4. 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
  5. 性能卓著,尤其适合现代大型、超大型互联网项目要求

1.1 MVC定义

MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构设计模式,它把软件系统分 为模型、视图和控制器三个基本部分

•View(视图) 指在应⽤程序中专⻔⽤来与浏览器进⾏交互,展⽰数据的资源.

• Model(模型) 是应⽤程序的主体部分,⽤来处理程序中数据逻辑的部分.

• Controller(控制器)可以理解为⼀个分发器,用来决定对于视图发来的请求,需要⽤哪⼀个模型来处理,以及处理完后需要跳回到哪⼀个视图。即 ⽤来连接视图和模型

1.2 主要作用

SpringMVC的作用主要覆盖的是表述层,例如:

  • 请求映射
  • 数据输入
  • 视图界面
  • 请求分发
  • 表单回显
  • 会话控制
  • 过滤拦截
  • 异步交互
  • 文件上传
  • 文件下载
  • 数据校验
  • 类型转换

简单来说就是 1. 简化前端参数接收( 形参列表 ) 2. 简化后端数据响应(返回值)

2. Spring MVC 接受响应数据

2.1 @RequestMapping注解配置访问路径

@RequestMapping注解的作用就是将请求的 URL 地址和处理请求的方式(handler方法)关联起来,建立映射关系。

SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的方法来处理这个请求。

源码解析

@RequestMapping 注解可以用于类级别和方法级别,它们之间的区别如下:

  1. 设置到类级别:@RequestMapping 注解可以设置在控制器类上,用于映射整个控制器的通用请求路径。这样,如果控制器中的多个方法都需要映射同一请求路径,就不需要在每个方法上都添加映射路径。

  2. 设置到方法级别:@RequestMapping 注解也可以单独设置在控制器方法上,用于更细粒度地映射请求路径和处理方法。当多个方法处理同一个路径的不同操作时,可以使用方法级别的 @RequestMapping 注解进行更精细的映射。

引出进阶注解

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

注意:进阶注解只能添加到handler方法上,无法添加到类上!

2.2 请求

2.2.1 传递单个参数

接收单个参数, 在 Spring MVC 中直接⽤⽅法中的参数就可以,⽐如以下代码:

java 复制代码
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/param")
public class ParamController {
 @RequestMapping("/m1")
 public String method1(String name){
 return "接收到参数name:"+ name;
 }
}

浏览器发送请求 http://127.0.0.1:8080/param/m1?name=spring

保证参数一致,即可自动接收

2.2.2 传递多个参数

如何接收多个参数呢? 和接收单个参数⼀样, 直接使⽤⽅法的参数接收即可. 使⽤多个形参.

java 复制代码
@RequestMapping("/m2")
public Object method2(String name, String password) {
 return "接收到参数name:" + name + ", password:" + password;
}

使⽤浏览器发送请求并传参: http://127.0.0.1:8080/param/m2name=zhangsan&password=123456

2.2.3 传递对象

如果参数⽐较多时, ⽅法声明就需要有很多形参. 并且后续每次新增⼀个参数, 也需要修改⽅法声明. 我们不妨把这些参数封装为⼀个对象. Spring MVC 也可以⾃动实现对象参数的赋值,⽐如 Person 对象:

java 复制代码
public class Person {
    private int id;
    private String name;
    private String password;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

传递对象代码实现

java 复制代码
@RequestMapping("/m3")
public Object method3(Person p){
 return p.toString();
}

http://127.0.0.1:8080/param/m3?id=5&name=zhangsan&password=123456

2.2.4 后端参数重命名(后端参数映射)

某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不⼀致,⽐如前端传递了⼀个 time 给后端,⽽后端是使⽤ createtime 字段来接收的,这样就会出现参数接收不到的情况,如果出现 这种情况,我们就可以使⽤ @RequestParam 来重命名前后端的参数值

java 复制代码
@RequestMapping("/m4")
public Object method_4(@RequestParam("time") String createtime) {
 return "接收到参数createtime:" + createtime;
}

http://127.0.0.1:8080/param/m4?time=2023-09-12

  1. 使⽤ @RequestParam 进⾏参数重命名时, 请求参数只能和 @RequestParam 声明的名称⼀ 致, 才能进⾏参数绑定和赋值.

  2. 使⽤ @RequestParam 进⾏参数重命名时, 参数就变成了必传参数.

⾮必传参数设置

如果我们的实际业务前端的参数是⼀个⾮必传的参数, 针对上述问题, 如何解决呢? 先来了解下参数必传的原因, 我们查看 @RequestParam 注解的实现细节就可以发现端倪,注解 实现如下:

java 复制代码
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
 @AliasFor("name")
 String value() default "";
 @AliasFor("value")
 String name() default "";
 boolean required() default true;
 String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n"
}

可以看到 required 的默认值为true, 表⽰含义就是: 该注解修饰的参数默认为必传 既然如此, 我们可以通过设置 @RequestParam 中的 required=false 来避免不传递时报错

2.2.5 传递数组

Spring MVC 可以⾃动绑定数组参数的赋值

java 复制代码
@RequestMapping("/m5")
public String method5(String[] arrayParam) {
 return Arrays.toString(arrayParam);
}

2.2.6 传递集合

集合参数:和数组类似, 同⼀个请求参数名有为多个, 且需要使⽤ @RequestParam 绑定参数关系 默认情况下,请求中参数名相同的多个值,是封装到数组. 如果要封装到集合,要使⽤ @RequestParam 绑定参数关系

java 复制代码
@RequestMapping("/m6")
public String method6(@RequestParam List<String> listParam){
 return "size:"+listParam.size() + ",listParam:"+listParam;
}

2.2.7 传递JSON数据

接收JSON对象, 需要使⽤ @RequestBody

注解 RequestBody: 请求正⽂,意思是这个注解作⽤在请求正⽂的数据绑定,请求参数必须在写在请求正 ⽂中

java 复制代码
@RequestMapping(value = "/m7")
public Object method7(@RequestBody Person person) {
 return person.toString();
}

2.2.8 获取URL中参数@PathVariable

path variable: 路径变量

默认传递参数写在URL上,SpringMVC就可以获取到

java 复制代码
@RequestMapping("/m8/{id}/{name}")
public String method8(@PathVariable Integer id, @PathVariable("name") String use
 return "解析参数id:"+id+",name:"+userName;
}

2.2.9 上传文件@RequestPart

java 复制代码
@RequestMapping("/m9")
public String getfile(@RequestPart("file") MultipartFile file) throws IOExceptio
 //获取⽂件名称
 String fileName = file.getOriginalFilename();
 //⽂件上传到指定路径
 file.transferTo(new File("D:/temp/" + file.getOriginalFilename()));
 return "接收到⽂件名称为: "+fileName;
}

2.2.10 接受cookie

可以使用 @CookieValue 注释将 HTTP Cookie 的值绑定到控制器中的方法参数

java 复制代码
@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) { 
  //...
}

2.2.11 接受请求头

java 复制代码
@GetMapping("/demo")
public void handle(
    @RequestHeader("Accept-Encoding") String encoding, 
    @RequestHeader("Keep-Alive") long keepAlive) { 
  //...
}

2.3 响应

@ResponseBody注解

1.方法上使用@ResponseBody注解

可以在方法上使用 @ResponseBody注解,用于将方法返回的对象序列化为 JSON 或 XML 格式的数据,并发送给客户端。在前后端分离的项目中使用

2.类上使用@ResponseBody注解

如果类中的每一个方法标记了@ResponseBody注解就可以提取到类上

java 复制代码
@ResponseBody  //responseBody可以添加到类上,代表默认类中的所有方法都生效!
@Controller
@RequestMapping("param")
public class ParamController {
}

@RestController注解

这个注解是一个合并注解 @RestController = @ResponseBody + @Controller

3.Spring MVC执行流程

SpringMVC涉及组件理解:

  1. DispatcherServlet : SpringMVC提供,我们需要使用web.xml配置使其生效,它是整个流程处理的核心,所有请求都经过它的处理和分发!
  2. HandlerMapping : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它内部缓存handler(controller方法)和handler访问路径数据,被DispatcherServlet调用,用于查找路径对应的handler!
  3. HandlerAdapter : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它可以处理请求参数和处理响应数据数据,每次DispatcherServlet都是通过handlerAdapter间接调用handler,他是handler和DispatcherServlet之间的适配器![经理]
  4. Handler : handler又称处理器,他是Controller类内部的方法简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果!
  5. ViewResovler : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效!视图解析器主要作用简化模版视图页面查找的,但是需要注意,前后端分离项目,后端只返回JSON数据,不返回页面,那就不需要视图解析器!所以,视图解析器,相对其他的组件不是必须的!

4.DispatcherServlet源码解读

先来看一下DispatcherServlet的继承体系

关注三个类分别是 DispatcherServlet FrameworkServlet HttpServletBean

首先来看init()方法,在HttpServletBean中进行实现

initServletBean()方法

this.webApplicationContext = initWebApplicationContext(); 初始化web容器

对MVC九大组件进行初始化分别是:

1.初始化文件上传解析器MultipartResolver: 从应⽤上下⽂中获取名称为multipartResolver的 Bean,如果没有名为multipartResolver的Bean,则没有提供上传⽂件的解析器

  1. 初始化区域解析器LocaleResolver:从应⽤上下⽂中获取名称为localeResolver的Bean,如果没 有这个Bean,则默认使⽤AcceptHeaderLocaleResolver作为区域解析器

  2. 初始化主题解析器ThemeResolver:从应⽤上下⽂中获取名称为themeResolver的Bean,如果 没有这个Bean,则默认使⽤FixedThemeResolver作为主题解析器

  3. 初始化处理器映射器HandlerMappings:处理器映射器作⽤,1)通过处理器映射器找到对应的 处理器适配器,将请求交给适配器处理;2)缓存每个请求地址URL对应的位置(Controller.xxx ⽅法);如果在ApplicationContext发现有HandlerMappings,则从ApplicationContext中获取 到所有的HandlerMappings,并进⾏排序;如果在ApplicationContext中没有发现有处理器映射

  4. 初始化处理器适配器HandlerAdapter:作⽤是通过调⽤具体的⽅法来处理具体的请求;如果在 ApplicationContext发现有handlerAdapter,则从ApplicationContext中获取到所有的 HandlerAdapter,并进⾏排序;如果在ApplicationContext中没有发现处理器适配器,则默认 SimpleControllerHandlerAdapter作为处理器适配器

  5. 初始化异常处理器解析器HandlerExceptionResolver:如果在ApplicationContext发现有 handlerExceptionResolver,则从ApplicationContext中获取到所有的 HandlerExceptionResolver,并进⾏排序;如果在ApplicationContext中没有发现异常处理器解 析器,则不设置异常处理器

  6. 初始化RequestToViewNameTranslator:其作⽤是从Request中获取viewName,从 ApplicationContext发现有viewNameTranslator的Bean,如果没有,则默认使⽤ DefaultRequestToViewNameTranslator

  7. 初始化视图解析器ViewResolvers:先从ApplicationContext中获取名为viewResolver的Bean, 如果没有,则默认InternalResourceViewResolver作为视图解析器

  8. 初始化FlashMapManager:其作⽤是⽤于检索和保存FlashMap(保存从⼀个URL重定向到另⼀ 个URL时的参数信息),从ApplicationContext发现有flashMapManager的Bean,如果没有,则 默认使⽤DefaultFlashMapManager

以上就是init()方法的内容,我们来看Service()方法

service() 方法中真正的核心是doDispatch() 方法

java 复制代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // Determine handler for the current request.
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request.
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
            boolean isGet = HttpMethod.GET.matches(method);
            if (isGet || HttpMethod.HEAD.matches(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Actually invoke the handler.
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            applyDefaultViewName(processedRequest, mv);
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new ServletException("Handler dispatch failed: " + err, err);
        }
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new ServletException("Handler processing failed: " + err, err));
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

总结

以上就是这篇博客的主要内容了,大家多多理解,下一篇博客见!

相关推荐
songbaoxian6 分钟前
ElasticSearch
java·linux·elasticsearch
非 白21 分钟前
【Java】代理模式
java·开发语言·代理模式
Good Note31 分钟前
Golang的静态强类型、编译型、并发型
java·数据库·redis·后端·mysql·面试·golang
csdn_aspnet1 小时前
ASP.NET MVC 下载文件
asp.net·mvc
我就是我3521 小时前
记录一次SpringMVC的406错误
java·后端·springmvc
向哆哆1 小时前
Java应用程序的跨平台性能优化研究
java·开发语言·性能优化
ekkcole2 小时前
windows使用命令解压jar包,替换里面的文件。并重新打包成jar包,解决Failed to get nested archive for entry
java·windows·jar
handsomestWei2 小时前
java实现多图合成mp4和视频附件下载
java·开发语言·音视频·wutool·图片合成视频·视频附件下载
全栈若城2 小时前
03 Python字符串与基础操作详解
java·开发语言·python
伯牙碎琴3 小时前
二、Spring Framework基础:IoC(控制反转)和DI(依赖注入)
java·spring·log4j