2024-11-21-Thu-T-SpringMVC

SpringMVC

Demo代码

1 SpringMVC 简介

MVC是一种软件架构思想, 将软件按照模型、视图、控制器来划分
M: Model : 模型层, 指工程中的Java Bean, 作用是处理数据
Java Bean分为两类:

  • 实体类Bean: 专门存储业务数据的, 比如User, Student
  • 业务处理Bean: 指Service或者Dao, 专门用于处理业务逻辑和数据访问

V: View :视图层, 指工程中html或sp等页面, 作用是与用户进行交互, 展示数据
C: Controller : 控制层, 指工程中的servlet, 作用是接受请求和响应浏览器

SpringMVC是一个Spring的子项目, SpringMVC有如下特点:

  • Spring系列原生产品, 与IOC容器等基础设施无缝对接
  • 基于原生的Servlet, 通过功能强大的前端控制器DispatcherServlet, 对请求和响应进行统一处理
  • 表述层各细分领域需要解决的问题全方面覆盖, 提供全面解决方案
  • 代码清晰简洁、组件化程度高、可插拔式组件即插即用、性能卓越..

2. HelloWorld

2.1准备工作

  1. 添加模块

  2. 打包方式war

  3. 引入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    <!--SpringMVC-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>6.1.13</version>
    </dependency>


    <!--日志-->
    <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.12</version>
    </dependency>

    <!--ServletAPI Tomcat中会自带此包-->
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
    <!--依赖由运行环境(例如容器或框架)提供的库,比如 servlet-api(在 Servlet 容器中运行时由容器提供)。-->
    </dependency>


    <!--Spring5和Thymeleaf整合包-->
    <dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring6</artifactId>
    <version>3.1.2.RELEASE</version>
    </dependency>
  4. 在main目录下创建webapp目录

  5. 载project structure中的module中添加web.xml(注意路径应该在webapp目录下)

2.2 配置web.xml

注册SpringMVC的前端控制器DispatcherServlet,

  • 默认配置方式
    此配置作用下, SpringMVC的配置文件默认位于WEB-INF下, 默认名为<servlet-name>-servlet.xml, 例如, 以下配置对应的SpringMVC的配置文件位于WEB-INF下, 文件名为springMVC-servlet.xml
1
2
3
4
5
6
7
8
9
10
11
12
<!--配置SpringMVC的前端控制器, 对浏览器发出的请求进行统一的处理-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
<!--"/"包括除了.jsp之外的所有请求(.jsp本质是一个servlet, 需要由特定的servlet处理器处理-->
</servlet-mapping>

  • 扩展配置方式(常用)
    可通过init-param标签设置SpringMVC配置文件的位置和名称, 通过load-on-startup标签设置SpringMVC前端控制器DispatcherServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 前端控制器 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 选择spring容器管理文件 管理mvc-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!-- 加载方式 程序运行时-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!-- 访问请求 / 接受所有请求 不包括.jps请求拒接 /* 接受所有请求 包括.jsp-->
<url-pattern>/</url-pattern>
</servlet-mapping>

2.3 创建请求控制器

由于前端控制器对浏览器发出的请求进行了统一的处理, 但是具体的请求有不同的处理过程, 因此需要创建处理具体请求的类, 即请求控制器
请求控制器中每一个处理请求的方法都成为控制器方法.
因为SpringMVC的控制器由一个POJO(普通的java类plain old java object)担任, 因此需要通过@Controller注解将其标识为一个控制层组件, 交给Spring ioc容器进行管理. 此时SpringMVC才能识别出控制器的存在.

1
2
3
4
5
6
7
8
9
@Controller
public void HelloWorld(){
@RequestMapping(value = "/")
public String index(){
//返回视图名称
return "index";
}

}

2.4 创建springMVC.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?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
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 上下文包 组件 扫描-->
<context:component-scan base-package="com.learning.mvc.controller"/>

<!-- 视图解析器-->
<bean id="springResourceTemplateResolver" class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>

<bean id="templateEngine" class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver" ref="springResourceTemplateResolver"/>
</bean>
<bean id="viewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine" ref="templateEngine"/>
</bean>
</beans>

2.5 配置tomcat

tomcat10

alt text

3. 核心内容

3.1 @RequestMapping注解

@RequestMapping的作用就是将请求和处理请求的控制器关联起来, 建立映射关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.web.bind.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.aot.hint.annotation.Reflective;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
@Reflective({ControllerMappingReflectiveProcessor.class})
public @interface RequestMapping {
String name() default "";

@AliasFor("path")
String[] value() default {};

@AliasFor("value")
String[] path() default {};

RequestMethod[] method() default {};

String[] params() default {};

String[] headers() default {};

String[] consumes() default {};

String[] produces() default {};
}

3.1.1 @RequestMapping注解的位置

  • 标识一个类: 设置请求路径的主路径
  • 标识一个方法: 设置请求路径的子路径

3.1.2 @RequestMapping注解的value属性

  • value属性通过请求地址匹配请求映射

  • value属性是一个字符串数组, 标识其可以进行多个路径匹配

  • value属性必须设置, 不能缺少

3.1.3 @RequestMapping注解的method属性

@RequestMapping注解的method属性通过请求的请求方式(get、post等)匹配请求路径
@RequestMapping的method属性是一个RequestMethod类型的数组

对于处理指定请求方式的控制器方法, SpringMVC提供了@Request Mapping的派生注解:

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

3.1.4 @RequestMapping的params属性

@RequestMapping的params参数是一个字符串数组, 可以通过四种表达式设置请求和请求映射的匹配关系

  • param: 必须携带参数param
  • !param: 只要不携带参数param即可
  • param=value: 必须要参数param等于value
  • param!=value: 只要参数param不等于value即可
1
2
3
4
5
6
# 例子

params = {username} # 必须携带username
params = {!username} # 只要不携带username即可
params = {username=zhangsan} # 必须username = zhangsan
params = {username!=zhangsan} # 只要username不等于zhangsan即可

3.1.5 SpringMVC支持ant风格的路径

  • ?: 表示任意的单个字符
  • *: 标识任意的0个或多个字符
  • **: 表示任意的一层或多层目录
  • 注意: 使用**时, 只能使用/**/xxx的方式

3.1.6 SpringMVC支持路径中的占位符(重点)

  • 原始方式: /deleteUser?id=1 (面向操作)
  • Restful方式: /deleteUser/1 (面向资源, 更易理解)
1
2
3
4
5

@RequestMapping("/test/{id}/{username}")
public User getUser(@PathVariable("id") String id, @PathVariable String username){
return new User(id, username);
}

3.2 SpringMVC获取请求参数

3.2.1 @RequestParam

1
2
3
4
5
6
7
//一般不使用此方式
@RequestMapping("/test")
public User getUser(HttpServletRequest request){
String name = request.getParameter("username");
String password = request.getParameter("id");
return new User(name,password);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//SpringMVC方式

// 1. 默认情况
//保证形参名与请求参数中的名字一样, 比如此案例中应该为 /testParam?username=zs&password=123
//对于多个相同的参数名, 可以使用数组接收
@RequestMapping(value = "/testParam")
public String testParam(String username, String password){
return "success";
}

//2. 使用@RequestParam注解
@RequestMapping(value = "/testParam")
public String testParam(@RequestParam("username") String username, @RequestParam("password") String password){
return "success";
}

3.2.2 @RequestHeader

@RequestHeader是将请求头信息和控制器方法的形参创建映射关系
@RequestHeader注解一共有三个属性: value, required, defaultValue, 用法与@RequestParam一样

3.2.3 @CookieValue

@CookieValue是将cookie数据和控制器方法的形参创建映射关系
三个属性与@RequestParam一样

3.2.4 通过POJO获取请求参数

在控制器方法的形参位置设置一个实体的类型的形参, 浏览器请求的参数与该实体内属性一致, 则参数会自动赋值

3.3 域对象共享数据

域对象 作用范围 生命周期 应用场景

  • ServletContext: 全局范围 Web 应用启动到销毁 应用配置、全局统计信息
  • HttpSession: 会话范围 用户访问到会话超时或销毁 登录信息、购物车
  • ServletRequest: 请求范围 单个 HTTP 请求处理期间 请求数据、转发共享数据
  • PageContext: 单个 JSP 页面 从 JSP 页面开始执行到处理完成 当前 JSP 页面内部 存储 JSP 页面的临时变量

3.3.1 使用servletAPI向Request域对象共享数据

1
2
3
4
5
6
@RequestMapping("/test")
public String test(HttpServletRequest request){
request.setAttribute("testScope", "Hello");
return "success";
}

3.3.2 使用ModelAndView向request域对象添加数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RequestMapping("/testModleAndView")
public ModelAndView testModelAndView(){

/**
* ModelAndView包含model和view功能
* model主要用于向请求域中共享数据
* view主要用于设置视图, 实现页面跳转
*/
ModelAndView modelAndView = new ModelAndView();
//向请求域中共享数据
modelAndView.addObject("msg111", "testModelAndView + " + modelAndView);
//设置视图, 实现页面跳转
modelAndView.setViewName("success");
return modelAndView;
}

3.3.3 使用Model向request域对象共享数据

1
2
3
4
5
@RequestMapping("/testModle")
public String testModel(Model model){
model.setAttribute("testScope", "hello");
return "success"
}

3.3.4 使用Map向request域对象共享数据

1
2
3
4
5
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
map.put("testScope", "hello");
return "success"
}

3.3.5 使用ModelMap向request域对象共享数据

1
2
3
4
5
@RequestMapping("/testModleMap")
public String testModelMap(ModelMap model){
model.setAttribute("testScope", "hello");
return "success"
}

3.3.6 向Session域共享数据

1
2
3
4
5
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("testSession", "hello, session");
return "success";
}

3.3.6 向application域中共享数据

1
2
3
4
5
6
7
@RequestMapping("/testApp")
public String testApp(HttpSession session){
ServletContext application = session.getServletContext();
application.setAttribute("testApp", "hellp application domain");
return "success";
}

3.4 SpringMVC的视图

SpringMVC的视图是View接口, 视图的作用是渲染数据, 将模型Model中的数据展示给用户
SpringMVC视图的种类有很多, 默认有转发视图InternalResourceView和重定向视图RedirectView
当工程引入jstl的依赖, 转发视图会自动转化为JstlView

3.4.1 ThymeleafView

若使用的视图为Thymeleaf, 在SpringMVC的配置文件中配置了Thymeleaf的视图解析器, 由此视图解析器解析之后所得到的是ThymeleafView

3.4.2 InternalResourceView

SpringMVC中默认的转发视图是InternalResourceView
当控制器方法中所设置的视图名称以“forward:/“为前缀时, 会创建InternalResourceView视图, 此时视图名称不会被SpringMVC配置文件中的所配置的视图解析器解析. 而是通过转发的方式跳转

3.4.3 RedirectView

alt text

3.4.4 视图控制器view-controller

当控制器中的方法, 仅仅用来实现页面跳转, 即只需要设置视图名称时, 可以将处理器方法使用view-controller标签进行表示

1
2
3
4
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>

<!--配置了view- controller后注解驱动会失效, 需要重新开启-->
<mvc:annotation-driven/>

4. RESTful

REST: Representational State Transfer 表现层资源状态转移

  • 资源
    处于服务器上一切可以访问的数据都可称为资源

  • 资源的表述
    资源的表述是一段对于资源在某个特定时刻的状态的描述. 可以理解为数据的格式

  • 状态转移
    状态转移是系统从一个状态变化到另一个状态的过程。状态(State)是系统在某一时刻的特定条件或配置,而转移(Transition)是由某种事件或条件触发的状态变化。例如: 用户已登陆, 用户未登录

3.1 RESTful的实现

HTTP协议中, 有四个表示对资源的操作动词:

  • GET:获取资源
  • POST: 新建资源
  • PUT: 更新资源
  • DELETE: 删除资源

RESTful风格提倡URL地址使用统一的风格设计, 从前到后各个单词使用斜杠分开, 不使用问号键值对的方式携带请求参数, 而是将要发送给服务器的数据作为url的一部分, 保证风格一致性

alt text

3.2 通过过滤器处理delete和put请求

1
2
3
4
5
6
7
8
9
10
<!--配置过滤器HiddenHttpMethodFilter-->
<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>

5. SpringMVC执行流程

5.1 SpringMVC常用组件

  • DispatcherServlet: 前端控制器, 框架提供,不需要工程师开发. 整个流程控制的中心, 统一处理请求和响应. 由它调用其他组件处理用户的请求
  • HandlerMapping: 处理器映射器, 框架提供, 不需要工程师开发. 根据请求的url,method等信息查找Handler, 即控制器方法
  • Handler: 处理器,即控制器Controller, 需要工程师开发. 在DispatcherServlet的控制下, Handler对具体的用户请求进行处理
  • HandlerAdapter: 处理器适配器, 由框架提供, 不需要工程师开发. 通过HandlerAdapter对控制器方法(处理器)进行执行
  • ViewResolver: 视图解析器, 由框架提供, 不需要工程师开发. 进行视图解析, 得到对应的视图
  • View: 视图, 不需要工程师开发, 由框架或视图技术提供. 将模型数据通过页面展示给用户

5.2 DispatcherServlet初始化过程

DispatcherServlet本质上是一个Servlet, 所以遵循Servlet的生命周期,

Servlet生命周期

  1. Servlet 初始化后调用 init () 方法。
  2. Servlet 调用 service() 方法来处理客户端的请求。
  3. Servlet 销毁前调用 destroy() 方法。
  4. 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
1
2
3
4
5
6
7
8
9
10
11
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}

5.3 SpringMVC执行流程

  1. 用户向服务器发起请求, 请求被SpringMVC前端控制器DispatcherServlet捕获
  2. DispatcherServlet对URL进行解析, 得到请求资源定位符URI, 判断请求URI对应的映射:
    1. 不存在URI对应映射
      1. 再判断是否配置了mvc:default-servlet-handler
        1. 如果没配置, 控制台报映射查找不到, 客户端显示404错误
        2. 有配置, 则访问目标资源(一般是静态资源, html, js, css). 找不到式客户端会显示404
    2. 存在URI对应映射
      1. 根据URI, 调用HandlerMapping得到该Handler配置的所有有关对象, 包括控制器和拦截器, 最后以HandlerExecutionChain执行链对象的形式返回
      2. DispatcherServlet根据的到的Handler, 选择一个合适的HandlerAdapter
      3. 如果成功或得HandlerAdapter, 开始执行拦截器的preHandler方法
      4. 提取Request中的模型数据, 填充Handler入参, 开始执行Handler(Controller)方法, 处理请求. 在填充Handler的入参过程中, 根据个人配置, Spring将完成一些额外的工作:
        1. HttpMessageConvert: 将请求信息(json, xml等)转化为一个对象, 或者将对象转化为对应的响应信息
        2. 数据转换: 比如String转化为Integer
        3. 数据格式化: 比如String转化为数字或者日期
        4. 数据验证: 验证数据有效性(长度, 格式等)
      5. Handler执行完成后, 向DispatcherServlet返回一个ModleAndView对象
      6. 此时将开始执行拦截器postHandler()方法
      7. 根据ModelAndView(如果有异常, 执行Handler ExceptionResolver进行异常处理), 选择一个合适的ViewResovler(普通、forward、redirect)进行视图解析, 根据model和view来渲染视图
      8. 将渲染结果返回给客户端

6. 注解配置SpringMVC

使用配置类和注解代替web.xml和SpringMVC配置文件功能

6.1 创建初始化类, 代替web.xml

在Servlet3.0中, 容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类, 如果找到的话就用它来配置Servlet容器
Spring提供了这个接口的实现, 名为SpringServletContainerInitializer. 这个类还会查找实现WebApplicationInitializer的类, 并将配置的任务交给他们完成, Spring3.2引入了一个WebApplicationInitializer的实现类, AbstractAnnotationConfigDispatcherServletInitializer.
当我们继承了AbstractAnnotationConfigDispatcherServletInitializer并且将其部署到了Servlet3的容器中时, 容器会自动发现它, 并用它来配置Servlet上下文.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.learning.mvc.anno.config;

import jakarta.servlet.Filter;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
* Author fei
* Version 1.0
* Description Web工程的初始化类
* 用来代替web.xml
* DATA 2024/11/22 19:04
*/
public class WebXML extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定Spring配置类
*
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}

/**
* 指定SpringMVC配置类
*
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}

/**
* 指定DispatcherServlet的url- patter
* 指定SpringMVC的映射规则,即url-pattern
*
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}

/**
* 注册过滤器
* @return
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
}


}

6.2 创建SpringMVC配置, 代替SpringMVC.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package com.learning.mvc.anno.config;

import com.learning.mvc.anno.controller.TestController;
import com.learning.mvc.anno.interceptor.TestInterceptor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.thymeleaf.spring6.SpringTemplateEngine;
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring6.view.ThymeleafViewResolver;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
* Author fei
* Version 1.0
* Description 代替SpringMVC配置文件
* 1. 扫描组件
* 2. 视图解析器、3. view-controller, 4. default- servlet-handler: WebMvcConfigurer
* 5. mvc注解驱动 6. 文件上传解析器。7. 异常处理。8. 拦截器
* DATA 2024/11/22 18:52
*/
@Configuration
@ComponentScan("com.learning.mvc.anno")
@EnableWebMvc //5. 开启MVC注解驱动
public class WebConfig implements WebMvcConfigurer {

/**
* 配置default- servlet-handler:
* @param configurer
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}

/**
* 8 添加拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**"); //拦截所有


WebMvcConfigurer.super.addInterceptors(registry);
}

/**
* 3. view-controller,
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:/index.html");
}

/**
* 6. 文件上传解析器
* @return
*/
@Bean
public MultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();

}

/**
* 。7. 异常处理。
* @param resolvers
*/
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("java.lang.ArithmeticException", "error");
exceptionResolver.setExceptionMappings(properties);
exceptionResolver.setExceptionAttribute("exception"); // 方便从请求域中获取异常信息
resolvers.add(exceptionResolver);

}

/**
* 配置 thymeleaf视图解析器
* @param templateEngine
* @return
*/
@Bean
public ThymeleafViewResolver thymeleafViewResolver(@Qualifier("springTemplateEngine") SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setOrder(1);
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}

@Bean("springTemplateEngine")
public SpringTemplateEngine templateEngine(@Qualifier("templateResolver") SpringResourceTemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(templateResolver);
return templateEngine;
}

@Bean("templateResolver")
public SpringResourceTemplateResolver springResourceTemplateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();

//视图前缀
templateResolver.setPrefix("classpath:/WEB-INF/templates/");

//视图后缀
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}

7. 扩展功能

7.1 HttpMessageConverter

HttpMessageConverter, 报文信息转换器, 将请求报文转化为Java对象, 或将java对象转化为响应报文
HttpMessageConverter提供了两个注解和两个类型:
注解:

  • @RequestBody: 在控制器中设置一个形参, 并使用此注解标识控制器
  • @ResponseBody: 标识控制器方法, 可以将该控制器的返回值作为响应体返回给浏览器
    类型:
  • RequestEntity: 放入控制器方法的形参中, 当有请求匹配到此控制器中, 则请求体信息会自动注入该形参中
  • ResponseEntity: 用于控制器返回值类型, 就是响应到浏览器的响应报文

@ResponseBody处理json对象需要引入注解才可以让java对象自动转化为json对象

1
2
3
4
5
6
<!--引入json依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.0</version>
</dependency>

@RestController = @Controller + @ResponseBody
@RestController加载控制器的类上, 相当于为类添加了@Controller, 同时为类中每个控制器添加了@ResponseBody

7.2 文件上传和下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Controller
public class FileController {

@RequestMapping("/testDown")
public ResponseEntity<byte[]> testDown(HttpSession session) throws IOException {
ServletContext servletContext = session.getServletContext();
String realPath = servletContext.getRealPath("/static/img/a.png");
FileInputStream fileInputStream = new FileInputStream(realPath);
byte[] bytes = new byte[fileInputStream.available()];
fileInputStream.read(bytes);
MultiValueMap<String, String> httpHeaders = new HttpHeaders();
httpHeaders.add("Content-Disposition", "attachment; filename=a.png");
HttpStatus httpStatus = HttpStatus.OK;
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, httpHeaders, httpStatus);
fileInputStream.close();
return responseEntity;


}
}

文件上传需要先引入相关依赖:

1
2
3
4
5
6
<!--文件上传需要的依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>

配置bean

1
2
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"></bean>

配置解析器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 前端控制器 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 选择spring容器管理文件 管理mvc-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!-- 加载方式 程序运行时-->
<load-on-startup>1</load-on-startup>


<multipart-config>
<!-- 上传文件最大为多少 -->
<max-file-size>10485760</max-file-size>
<!-- 最大的请求大小 -->
<max-request-size>10485760</max-request-size>
<!-- 多大以上的文件可以上传 -->
<file-size-threshold>0</file-size-threshold>
</multipart-config>
</servlet>

表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<form th:action="@{/testUp}" method="post" enctype="multipart/form-data">
图片上传: <input type="file" name="photo">
<input type="submit" value="上传">
</form>

</body>
</html>

控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RequestMapping(value = "/testUp", method = RequestMethod.POST)
public String testUp(@RequestParam("photo") MultipartFile photo, HttpSession session) throws IOException {
System.out.println(photo.getOriginalFilename());
System.out.println(photo.getName());
ServletContext servletContext = session.getServletContext();
String realPath = servletContext.getRealPath("photo");
File file = new File(realPath);
if (!file.exists()) {
file.mkdirs();
}
String finalPath = realPath + File.separator + photo.getOriginalFilename();
photo.transferTo(new File(finalPath));
return "testUP 成功";
}

7.3 拦截器

alt text

编写拦截器方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class FirstInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("FirstInterceptor preHandle");
return false ;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("FirstInterceptor postHandle");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("FirstInterceptor afterCompletion");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}

编写控制器

1
2
3
4
5
6
7
8
9
10
@Controller
public class HandlerController {

@RequestMapping("/testInterceptor")
public String testInterceptor() {

return "interceptor";
}

}

配置SpringMVC

1
2
3
4
5
6
7
8
9

<!--配置拦截器-->
<mvc:interceptors>
<!--<bean class="com.learning.mvc.rest.interceptor.FirstInterceptor"></bean>-->
<mvc:interceptor>
<mvc:mapping path="/testInterceptor"/>
<ref bean="firstInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

有多个拦截器时
alt text

7.4 异常处理器

SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口: HandlerExceptionResolver
HandlerExceptionResolver的实现类有:

  • DefaultHandlerExceptionResolver SpringMVC默认使用的异常处理器
  • SimpleHandlerExceptionResolver: 留给开发人员自定义处理异常

基于配置的异常处理

1
2
3
4
5
6
7
8
9
<!--配置异常处理-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props >
<prop key="java.lang.ArithmeticException">error</prop>
<!-- 将会自动跳转到error的页面 -->
</props>
</property>
</bean>

基于注解的异常处理

1
2
3
4
5
6
7
8
9
10
@ControllerAdvice
public class ExceptionCatchController2 {

//如果出现相应的异常, 那么就会调用这个controller
@ExceptionHandler(value = {ArithmeticException.class, NullPointerException.class})
public String testExceptionHandler(Exception e) {
return "error";
}

}

2024-11-21-Thu-T-SpringMVC
http://example.com/2024/11/21/2024-11-21-Thu-T-SpringMVC/
Author
Fei
Posted on
November 21, 2024
Licensed under