Skip to content

springMVC

  • 一个实现了 MVC 框架模式的 Web 框架,底层基于 Servlet 实现。

帮助我们做了什么 ?

  1. 入口控制:SpringMVC 框架通过 DispatcherServlet 作为入口控制器,负责接收请求和分发请求。而在 Servlet 开发中,需要自己编写 Servlet 程序,并在 web.xml 中进行配置,才能接受和处理请求。
  2. 在 SpringMVC 中,表单提交时可以自动将表单数据绑定到相应的 JavaBean 对象中,只需要在控制器方法的参数列表中声明该 JavaBean 对象即可,无需手动获取和赋值表单数据。而在纯粹的 Servlet 开发中,这些都是需要自己手动完成的。
  3. IoC 容器:SpringMVC 框架通过 IoC 容器管理对象,只需要在配置文件中进行相应的配置即可获取实例对象,而在 Servlet 开发中需要手动创建对象实例。
  4. 统一处理请求:SpringMVC 框架提供了拦截器、异常处理器等统一处理请求的机制,并且可以灵活地配置这些处理器。而在 Servlet 开发中,需要自行编写过滤器、异常处理器等,增加了代码的复杂度和开发难度。
  5. 视图解析:SpringMVC 框架提供了多种视图模板,如 JSP、Freemarker、Velocity 等,并且支持国际化、主题等特性。而在 Servlet 开发中需要手动处理视图层,增加了代码的复杂度。

第一个 Spring MVC 的开发流程

  1. 创建一个空的工程
  2. 设置 JDK 版本
  3. 设置 maven 版本
  4. 创建 maven 模块
  5. 在 pom 文件设置打包方式 war 方式
  6. 引入依赖 springmvc 依赖 logback 依赖 thymeleaf 和 spring6 整合 servlet 依赖(scope 设置 provided 表示这个依赖由第三方容器来提供)

1.2 给 Maven 模块添加 web 支持

在模块 src/main 目录下新建 webapp 目录 (默认是带有小蓝点 没有需要自己添加 module 设置 ) 另外需要添加 web.xml 文件 注意添加的路径

1.3 在 web.xml 文件中配置前端控制器(springmvc 内置的一个类 DispatchServlet)所有的请求都应该经过 DispatcherServlet 处理

重点: / 表示 除了访问 xxx.jsp 结尾的请求路径外的所有路径 也就是说 只要不是 JSP 访问路径,一定会走 DispatcherServlet

1.4 编写 FirstController,在类上标注 @Controller 注解,纳入 IOC 容器

当然 也可以用 @Component 注解进行标注 @Controller 只是@Component 的别名

1.5 配置编写 Springmvc 框架自己的配置文件

配置文件默认的名字 配置文件默认存放的位置 WEB-INF 两个配置: 1 组件扫描 2 配置视图解析器

1.6 提供视图

在 WEB-INF/templates 目录下新建 first.html 文件 编写符合 html 模版的字符串

1.7 提供请求映射

最终返回逻辑视图名称 逻辑视图名称:first 物理视图名称:前缀 + first + 后缀 最终路径: /WEB-INF/templates/first.html

关于@RequestMapping 注解的 value 属性

value 属性本身是一个 String[] 字符串数组,说明多个请求可以映射同一个处理器方法
如果注解的属性是数组,并且在使用注解的时候,该数组只有一个元素,大括号可以省略
如果使用某个注解的时候,如果只使用一个 value 属性,那么 value 也是可以省略的
value 属性的别名是 path
path 属性的别名是 value

RequestMapping 的 value 属性支持 Ant 风格的 支持模糊匹配的路径

? 表示任意一个字符

  • 表示 0 到 N 个任意字符 (排除'/' '?') ** 表示 0 到 N 个任意字符 ,可以出现路径分隔符 注意 ** 左边只能 '/' spring6 只能是末尾出现

关于@RequestMapping 注解的 value 属性占位符(重点)

现在流行 RESTFUL 风格的 URL:/springmvc/login/admin/123

js
@RequestMapping("/login/{username}/{password}")
public String testRESTFulURL(
       @PathVariable("username")
       String username,
       @PathVariable("password")
       String password) {
   System.out.println("username: " + username + " password: " + password);
   return  "ok";
}

关于@RequestMapping 注解的 method 属性,通过该属性限制前端请求方式,如果请求方式不同,则会报 405 错误

js
@RequestMapping(value = "/user/login", method = { RequestMethod.GET, RequestMethod.POST })
public String userLogin() {
   System.out.println("处理登陆的业务逻辑...");
   return "ok";
}

衍生 Mapping

  • @PostMapping
  • @GetMapping

web 的请求方式

GET POST PUT DELETE HEAD

关于 requestMapping 注解的 params 属性

关于 requestMapping 注解的 headers 属性

获取请求的数据

  1. servlet API

在处理器方法参数上,HttpServletRequest springMvc 框架将自动将 Tomcat 服务器创建的 request 对象传递给处理器方法 我们直接在方法中使用 equest 对象即可。

  1. 注解 @requestParam

属性 value name required:设置该参数是否必传,如果该属性没传则提示 400 这个属性类似于 @RequestMapping 注解中的 params 可以设置为 false,则该参数不是必传 不会报 400,但是前端没有提供这个属性,默认为 null defaultValue:如果前端没有提供参数,可以设置该参数的默认值

  1. 行参名来接收,如果方法行参的名字和提交数据时的 name 相同,则@RequestParam 可省略
js
如果使用的是spring6+版本,则需要在pom文件上添加如下:
<build>
   <plugins>
       <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>3.13.0</version>
           <configuration>
               <source>17</source>
               <target>17</target>
               <compilerArgs>
                   <arg>-parameters</arg>
               </compilerArgs>
           </configuration>
       </plugin>
   </plugins>
</build>

注意:如果 控制器上的行参名 和 请求参数名 不一致,那么控制器上的行参默认值是 null
  1. 使用 POJO 类/JavaBean 接收请求参数(常用)

底层实现:反射机制 不过使用前提是:POJO 类的属性名和请求参数名一致 实现原理是什么? 假设提交一个请求,参数名是 username,那么 POJO 类必须有一个属性名也叫作:username 根据 username 进行 setUsername 注入赋值

重点:底层通过反射机制调用 set 方法给属性赋值,所以 set 方法名非常重要。 如果前端提交参数是 username,那么 POJO 类中必须有 seUsername 方法

获取请求头信息 ?

js
使用@RequestHeader注解获取
@RequestHeader(value = "Referer",required = false, defaultValue = "")

使用@CookieValue 注解 获取控制器方法上的行参

关于 javaweb 项目,如何解决 post 请求乱码问题?

request.setCharacterEncoding("utf-8"); 但是该执行语句必须在 request.getParameter("")之前执行才有效。 第一种:可以自己写 过滤器 Filter 第二种:使用 springmvc 内置的字符编码过滤器 CharacterEncodingFilter

Request 域数据共享

  • 第一种方式:在处理器方法上,添加 HttpServletRequest 参数即可
  • 第二种方式:在 springmvc 的处理器方法上添加一个接口类型 Model (ui.Model)
js
@RequestMapping("/testModel")
public String testModel(Model model) {
    model.addAttribute("testRequest", "testModel");'
    // 转
    return "ok";
}
  • 第三种方式:在 springmvc 的处理器方法上添加一个接口类型 Map
js
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map) {
   map.put("testRequest", "testMap");
   return "ok";
}
  • 第四种方式:在 springmvc 的处理器方法上添加一个类 ModelMap
js
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap) {
   modelMap.addAttribute("testRequest", "testModelMap");
   return "ok";
}

研究下,Model 接口,Map 接口,ModelMap 类 三者之间的关系 ?

WARNING

表面是使用的不同接口和不同的类,实际上使用的是同一个对象 org.springframework.validation.support.BindingAwareModelMap

  • 第五种方式: 使用 ModelAndView 类完成数据共享
js
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
    // 创建模型视图对象
    ModelAndView modelAndView = new ModelAndView();
    // 给模型视图对象 绑定数据
    modelAndView.addObject("testRequest", "testModelAndView");
    // 给模型视图对象 绑定 视图
    modelAndView.setViewName("ok");
    // 返回模型视图对象
    return modelAndView;
}

聊一个真相:

WARNING

对于处理器方法来说,不管使用的是 Model 对象,Map 对象, modelMap 类, ModelAndView 类,最终处理器方法执行 结束后,返回的都是 ModelAndView 对象,这个返回的 ModelAndView 对象给 DispatchServlet 类了

当请求路径不是 JSP 的时候,都会走前端控制器 DispatchServlet. DispatchServlet 中有一个方法 doDispatch,这个方法通过请求路径找到 处理器方法, 然后调用处理器方法返回一个视图名称(也可能是一个 ModelAndView 对象),底层会将逻辑视图名称转换 为 view 对象,然后结合 Model 对象,封装一个 ModelAndView 对象,然后将该对象返回给 DispatchServlet 类.

Session 域数据存储

  • 第一种方式: 使用原生的 servlet API 实现(在处理器方法的参数上添加 httpSession 对象, springMVC 会自动将 session 对象传递给这个参数)
  • 第二种方式: 使用@SessionAttributes 注解实现 session 域数据存储

Application 域数据存储

这个域使用较少,如果使用的话,一般采用 Servlet API 的方式使用.

js
@RequestMapping("/testApplication")
public String testApplicaitonScope(HttpServletRequest request) {
    ServletContext application = request.getServletContext();
    application.setAttribute("testApplication", "testApplication");
    return "ok";
}

SpringMVC 中常用的视图

  • InternalResourceView: 内部资源视图 (是- springMVC 内置的,专门用于解析 JSP 模版语法的, 另外也负责 转发 forward 功能实现)
  • RedirectView: 重定向视图 (是 springMVC 内置的, 专门负责 重定向 redirect 功能实现)
  • ThymeleafView: Thymeleaf 视图 (第三方, 专门负责解析 thymeleaf 模版语法) ...

实现视图的核心类和核心接口

  1. DispatcherServlet: 前端控制器 负责接收前端的请求 根据请求路径找到对应的处理器方法 执行处理器方法 并且最终返回 ModelAndView 对象. 再往下就是视图解析器
  2. ViewSource 接口: 视图解析器接口 (ThymeleafViewResolver 实现了 ViewSource 接口, InternalResourceView 也是实现了 ViewSource 接口) 这个接口做什么 ? 这个接口的作用就是将 逻辑视图名称 转换为 物理视图名称 并且最终返回一个 View 接口对象 核心方法是什么 ? View resolveViewName(String viewName, Locale locale) throws Exception;
  3. View 接口: 视图接口 这个接口做什么 ? 这个接口主要负责将模版语法的字符串转换为 html 代码, 并且将 html 代码响应给浏览器 (即渲染.) 核心方法是什么 ? void render(@Nullable Map<String, ?/> model, HttpServletRequest request, HttpServletResponse response) throws Exception;

在 springMVC 中是怎么实现转发的 ?

js
/* 注意:
  当 return pageA 的时候, 返回一个逻辑视图,这种方式跳转到视图
  默认采用的是forward方式跳转过去的, 只不过这个底层创建的视图对象 是thymeleafView*/
  @RequestMapping("/A")
  public String toA() {
      // 返回逻辑视图
      return "pageA";
  }
  • 如何转发 , 什么格式 ?

    "return forward: /B" 转发到 "/B" ,这是一次请求, 底层创建视图对象是, internalResourceView 对象

  • 怎么重定向 , 什么格式 ?

    "return redirect: /B" 转发到 "/B" ,发起两次请求,底层创建的是 RedirectView

WARNING

总结:
转发: "return forward: /B" ---> internalResourceView
重定向: "return redirect: /B" ---> redirectView

<mvc:view-controller />

INFO

这是个配置信息, 可以在 springmvc.xml 文件中进行配置, 作用是什么 ? 如果一个 Controller 上的方法是为了完成视图跳转,没有任何业务代码,那么这个 controller 可以不写. 直接在 springmvc.xml 中写上 <mvc:view-controller /> 注解即可.

<mvc:view-controller path="/test" view-name="test" />

<mvc:annotation-driven />

开启注解启动,会让整个项目中的注解再次开启.

关于静态资源处理.

  • 假如有静态文件 static,如果想要直接访问有两种解决方式:
    1. 第一种: 开启默认的 Servlet 服务, 需要在 springmvc.xml 配置 开启静态资源访问
      js
      <!--开启默认的defaultServlet 可以使用default访问静态资源-->
      <mvc:default-servlet-handler />
      <mvc:annotation-driven />
      Servlet 服务器默认先走 DispatcherServlet ,如果发生 404, 则会自动走 defaultServlet 服务帮你定位静态资源.
    2. 在 springmvc.xml 文件中 添加如下配置:
    js
    <!--配置处理静态资源-->
    <mvc:annotation-driven />
    <mvc:resources mapping="/static/**" location="/static/" />

RESTful API

什么是 RESTful ?

RESTful 是对 WEB 服务接口的设计风格,提供的一套约束,可以让 WEB 服务接口更加简洁, 易于理解.
RESTful 是表述性状态转移

  • 查询 get
  • 新增 post
  • 删除 delete
  • 修改 put

RESTful 风格中要求,修改的时候,提交必须是一个 PUT 请求, 怎么提交 PUT 请求 ?

  1. 要想发送 put 请求, 前提必须是一个 POST 请求

  2. 在 POST 请求中添加隐藏域:

    js
    <input type="hidden" name="_method" value="put" />
  3. 在 web.xml 中添加一个过滤器

js
<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 中如何使用原生的 ServletAPI 完成 AJAX 请求的响应 ?

js
@RequestMapping("/ajax")
public void ajax(HttpServletResponse response) throws Exception {
    PrintWriter writer = response.getWriter();
    writer.print("hello ajax im spring mvc2w");
}

在 springMVC 中如何使用原生的 ServletAPI 完成 AJAX 请求的响应 ?

js
@RequestMapping("/ajax")
public void ajax(HttpServletResponse response) throws Exception {
    PrintWriter writer = response.getWriter();
    writer.print("hello ajax im spring mvc2w");
}

@ResponseBody 注解 (非常重要)

js
@GetMapping("/ajax")
@ResponseBody
public String ajax() throws IOException {
    return "hello ajax im spring mvc2w";
}

WARNING

注意: 一旦处理器方法上添加了 @ResponseBody 返回将不再是 逻辑视图名称 ,而是将结果返回给浏览器 底册实现原理代替的就是
PrintWriter writer = response.getWriter();
writer.print("hello ajax im spring mvc2w");
以上使用的 http 消息转换器是 StringHttpMessageConverter

js
@GetMapping("/ajax")
@ResponseBody
public User ajax() throws IOException {
   User user = new User(123L,"zhangsan", "1232");
   return user;
}

当一个处理器上面有 @ResponseBody 注解,并且返回一个 java 对象,例如 user 对象,那么 springMVC 框架会自动将 user 对象转换成字符串响应给浏览器.
当然,你要在 pom.xml 文件引入 jackson 依赖

js
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.17.1</version>
</dependency>

以上使用 HTTP 转换器是: MappingJackson2HttpMessageConverter.

非常好用的注解 @RestController

INFO

出现在类上 = @Controller + @ResponseBody
@RestController 是一个复合注解.
表示该类上自动添加了 @Controller 注解, 并在该类中所有的方法上都会自动添加 @ResponseBody 注解.

关于@RequestBody 注解

该注解只能使用在处理器方法的形参上
这个注解的作用是直接将请求体传递给 Java 程序,在 Java 程序中可以直接使用一个 String 类型的变量来接收 这个请求体的内容
底层使用 HTTP 消息转换器是: FormHttpMessageConverter

关于@RequestBody 注解的重要用法: 如果前端请求体当中提交的数据是 JSON 格式,那么 @RequestBody 可以将提交的 JSON 格式的字符串转换成 java 对象.

注意: 同样是说过 jackjson 依赖:

js
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.17.1</version>
</dependency>

以上前端请求体提交的 JSON 格式的字符串, 那么后端直接将 JSON 格式的字符串转换成 java 对象, 这里使用的消息转换器是: MappingJackson2HttpMessageConverter

RequestEntity

这个类的实例封装了整个请求协议
SpringMVC 自动创建好, 传递给处理器方法上的参数
你只需要在处理器的方法的参数上加上: (RequestEntity requestEntity)即可 springmvc 自动创建好该对象
传递到处理器方法的参数上.
通过它可以获取请求协议中个任何信息: 请求行 请求头 请求体

ResponseEntity

这个类的实例封装了整个响应协议 包括 状态行 响应头 响应体;
注意: 如果你要定制响应协议, 那么处理器的返回值类型必须是 ResponseEntity,
泛型为什么是 User, 因为响应体的信息是 user 信息.

文件上传

文件上传必须是 post 请求
文件上传的 form 标签中使用 enctype="multipart/form-data"
enctype 用来设置请求头的内容类型,默认类型 application/x-www-form-urlencoded
如果你是 spring6,需要在 web.xml 中 dispatcherServlet 中配置

js
<multipart-config>
  <!--设置单个支持最大文件的大小-->
  <max-file-size>102400</max-file-size>
  <!--设置整个表单所有文件上传的最大值-->
  <max-request-size>102400</max-request-size>
  <!--设置最小上传文件大小-->
  <file-size-threshold>0</file-size-threshold>
</multipart-config>

文件下载(固定格式)

js
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile(HttpServletResponse response, HttpServletRequest request) throws IOException {
    File file = new File(request.getServletContext().getRealPath("/upload") + "/谢谢参与 2.png");
    // 创建响应头对象
    HttpHeaders headers = new HttpHeaders();
    // 设置响应内容类型
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    // 设置下载文件的名称
    String fileName = URLEncoder.encode(file.getName(), "UTF-8");
    headers.setContentDispositionFormData("attachment", fileName);

    // 下载文件
   return new ResponseEntity<byte[]>(Files.readAllBytes(file.toPath()), headers, HttpStatus.OK);
}