框架-SpringMVC

一、前置知识(Thymeleaf)

什么是Thymeleaf

Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎

  • 模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的html文档。从字面上理解模板引擎,最重要的就是模板二字,这个意思就是做好一个模板后套入对应位置的数据,最终以html的格式展示出来,这就是模板引擎的作用。

以前使用的jsp就是一个模板引擎。

Thymeleaf模板引擎在整个web项目中起到的作用为视图展示(view)。当今web项目较为流行的MVC(Model View Controller)架构:模型(model)-视图(view)-控制器(controller)

  • Model(模型)表示应用程序核心,指工程中的javaBean。

    javaBean分为两类:

    • 一类为实体类:专门存储业务数据,如Student、User
    • 一类是业务处理类:指Service或Dao对象,专门用于处理业务逻辑和数据访问
  • View(视图)显示数据,而本篇使用的就是Thymeleaf作为视图。

  • Controller(控制器)处理输入请求,将模型和视图分离。

参考:https://developer.aliyun.com/article/769977#slide-0

二、SpringMVC简介

1、SpringMVC的特点

  • Spring家族原生产品,与IOC容器等基础设施无缝对接
  • 基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和相应进行统一处理
  • 内部组件化程度高

2、HelloWord项目

创建一个maven项目,导入这些依赖

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
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>ogback-classic</artifactId>
<version>1.2.3</version>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>

</dependencies>

新增一个webapp目录

在webapp目录下加一个web.xml文件,在里面配置servlet的相关信息,Spring MVC 初始化时将在应用程序的 WEB-INF 目录下查找配置文件,该配置文件的命名规则是“< servlet-name >-servlet.xml”,不过可以根据param-name标签将其改为其他路径

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
<?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">

<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>
<!--将前端控制器的初始化事件提前到服务器启动时-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!--/表示可以接受来自浏览器的所有路径的请求,可以是/login,.html,.css等,但不包括.jsp结尾的路径
/*才是所有的请求,因为jsp文件本身就是servlet,所以我们不需要拦截jsp文件-->
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

maven工程中,配置文件应该统一放在resources目录下,所以需要重新配置SpringMVC配置文件的路径。

对于load-on-startup标签的说明:当值为0或大于0时,表示容器在应用启动时就加载并初始化这个DispatcherServlet,正数的值越小,该DispatcherServlet的优先级越高,容器启动时就先加载它。当值小于0或者没有指定时,表示该容器在DispatcherServlet被选择时才会去加载,即第一次访问servlet时加载。

DispatcherServlet的作用是对浏览器发送的请求进行统一的处理,但不同的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器

SpringMVC配置文件中的配置(一般放在recources文件夹下)

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
<?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="springmvc.vickkkyz.controller"></context:component-scan>

<!-- 视图解析器,管理请求跳转到指定页面 配置Thymeleof解析器-->
<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>

创建Controller

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

@RequestMapping(value = "/")
public String index(){
//返回要解析的视图,即想要给用户呈现的页面
return "index";
}
}

创建视图页面

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>

配置Tomcat服务器

一定不要选TomcatEE

然后就可以正确访问到WEB/INF/templates/index.html

3、总结

  • 浏览器发送请求,首先由前端控制器处理,根据web.xml中设置的可以接受的请求的路径,如果请求地址符合前端控制器的url-pattern,该请求就会被前端控制器DispatcherServlet处理。

  • 然后前端控制器会读取SpringMVC的配置文件,通过扫描组件找到真正处理请求的控制器,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,如果匹配成功,该注解所标识的控制器方法就是处理请求的方法。

  • 处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最后转发到视图所对应的页面。

三、@RequestMapping注解

  • @RequestMapping放在类上:设置映射请求的请求路径的初始信息
  • @RequestMapping放在方法上:设置映射请求请求路径的具体信息

value属性

value可以是一个值,也可以是一个list,这样的话就任意一个路径都可以

1
2
3
4
5
6
7
8
<a th:href="@{/testMaping}">testMaping</a>
<a th:href="@{/haha}">haha</a>
// @{/testMaping}就代表http://localhost:8080/SpringMVC/testMaping

@RequestMapping(value = {"/testMaping","/haha"})
public String test02(){
return "sucess";
}

method属性

  • @RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射
1
@RequestMapping(value = {"/testMaping","/haha"},method = RequestMethod.GET)

也可以设置多个请求方法,用大括号。

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

  • get请求:@GetMapping
  • post请求:@PostMapping
  • delete请求:@DeleteMapping

目前浏览器只支持get和post请求,对于在form表单中的delete,put请求,都会按照默认的请求方式get处理。

params属性

params括号内的字符串需要同时满足

1
<a th:href="@{/hehehe(username,password=1234)}">params</a>
1
2
3
4
@RequestMapping(value = {"/testMaping","/haha"},params = {"username","password=1234"})
public String test02(){
return "sucess";
}

!username代表请求参数中不能有username

username!=zhangsan代表要求映射匹配的请求必须携带username并且username的值不能是zhangsan

header属性

和params参数基本一样,请求头中的信息。

ant风格路径

  • ?:表示任意的单个字符

    1
    @RequestMapping("/t?st")
  • *:表示任意的0个或多个字符

    1
    @RequestMapping("/t*st")
  • **:表示任意的一层或多层目录

    1
    @RequestMapping("/**/test")

路径中的占位符

  • 原始方式:/practice?id=1
  • REST方式:/practice/1

占位符{xxx}表示传输的数据

1
<a th:href="@{/practice/1}">占位符</a>
1
2
3
4
5
@RequestMapping(value = "/practice/{id}")
public String test03(@PathVariable("id")String id){
System.out.println(id);
return "success2";
}

四、SpringMVC获取请求参数

通过ServletAPI获取

将HttpServletRequest作为控制器方法的形参,HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象

1
<a th:href="@{/practice(username='admin',password=123456)}">servletAPI方式获取请求参数中的数据</a>
1
2
3
4
5
6
@RequestMapping(value = "/practice")
public String test03(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
return "success2";
}

通过控制器方法的形参获取

在控制器方法Controller的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参

1
<a th:href="@{/testParam(username='admin',password=123456)}">测试获取请求参数</a>
1
2
3
4
5
@RequestMapping("/testParam")
public String testParam(String username, String password){
System.out.println("username:"+username+",password:"+password);
return "success";
}

若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数

1
<a th:href="@{/testParam(hobby='a',hobby='b')}">测试</a>
1
2
3
4
5
6
7
@RequestMapping("/testParam")
public String testParam(String[] hobby){
System.out.println("hobby:"+ Arrays.toString(hobby));
return "success";
}
//输出:hobby:[a, b]
//如果将hobby定义为String类型,则输出:hobby:a,b

@RequestParam

当有请求时,DispatcherServlet中会将请求参数赋值给相应的形参(对应规则就是根据HTML中定义的请求参数的名称来寻找形参中是否有一样名字的参数,将其赋值)。如果请求参数的名称和形参中不一致,就无法映射。因此可以通过@RequestParam来对应

@RequestParam是将请求参数控制器方法的形参创建映射关系

@RequestParam注解一共有三个属性:

  • value:指定为形参赋值的请求参数的参数名
  • required:设置是否必须传输此请求参数,默认值为true
    • 若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数且没有设置defaultValue属性,则页面报错400:Required String parameter ‘xxx’ is not present;
    • 若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null
  • defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为””时,则使用默认值为形参赋值
1
<a th:href="@{/testParam(user_name='admin')}">测试</a>
1
2
3
4
5
@RequestMapping("/testParam")
public String testParam(@RequestParam("user_name") String username){
System.out.println("username:"+username);
return "success";
}

@RequestHeader

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

@CookieValue

  • @CookieValue是将cookie数据控制器方法的形参创建映射关系
  • @CookieValue注解一共有三个属性:value、required、defaultValue
  • 用法同@RequestParam

通过POJO获取请求参数

可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值

1
2
3
4
5
6
7
8
9
<form th:action="@{/testpojo}" method="post"> 
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
性别:<input type="radio" name="sex" value="男">
<input type="radio" name="sex" value="女"><br>
年龄:<input type="text" name="age"><br>
邮箱:<input type="text" name="email"><br>
<input type="submit">
</form>
1
2
3
4
5
6
7
8
public class User { 
private Integer id;
private String username;
private String password;
private String sex;
private Integer age;
private String email;
}
1
2
3
4
5
6
@RequestMapping("/testpojo") 
public String testPOJO(User user){
System.out.println(user);
return "success";
//最终结果-->User{id=null, username='张三', password='123', age=23, sex='男', email='123@qq.com'}
}

请求参数的值乱码处理

如果是get请求乱码,则只需要在tomcat的conf/server.xml处此位置加上URIEncoding=UTF-8

如果是post请求乱码,则需要在springMVC配置文件中设置过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--配置springMVC的编码过滤器--> 
<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>

五、域对象共享数据

使用ServletAPI向request域对象共享数据

1
2
3
4
5
6
7
//先进到index页面上,然后跳转到success页面
<h1>哈哈</h1>
<a th:href="@{/success}">success</a>

//success页面显示success,获取域对象test的值并显示
success<br>
<p th:text="@{test}"></p>
1
2
3
4
5
@RequestMapping("/")
public String test(HttpServletRequest request){
request.setAttribute("test","这是设置的test域对象的值");
return "index";
}

使用ModelAndView向request域对象共享数据

1
2
3
4
5
6
7
8
9
10
11
12
13
@RequestMapping("/testModelAndView") 
public ModelAndView testModelAndView(){
/** * ModelAndView有Model和View的功能
* Model主要用于向请求域共享数据
* View主要用于设置视图,实现页面跳转
*/
ModelAndView mav = new ModelAndView();
//向请求域共享数据
mav.addObject("testScope", "hello,ModelAndView");
//设置视图,实现页面跳转
mav.setViewName("success");
return mav;
}

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

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

使用map向request域对象共享数据

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

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

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

Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的

以上五种方法本质上都是生成一个ModelAndView

向session域共享数据

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

六、SpringMVC中的视图

SpringMVC视图的种类很多,默认有转发视图InternalResourceView和重定向视图RedirectView。若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView

ThymeleafView

当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转。如果SpringMVC配置文件中配置的视图解析器是Thymeleaf,那么就会被Thymeleaf视图解析器解析。

InternalResourceView

SpringMVC中默认的转发视图是InternalResourceView

当控制器方法中所设置的视图名称以”**forward:**“为前缀时,会创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀”forward:”去掉,剩余部分作为最终路径通过转发的方式实现跳转

1
2
3
4
@RequestMapping("/testForward")
public String testForward(){
return "forward:/testHello";
}

RedirectView

SpringMVC中默认的重定向视图是RedirectView

当控制器方法中所设置的视图名称以”**redirect:**“为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀”redirect:”去掉,剩余部分作为最终路径通过重定向的方式实现跳转

1
2
3
4
@RequestMapping("/testRedirect")
public String testRedirect(){
return "redirect:/testHello";
}

视图控制器view-controller

如果发送的请求不想通过controller,只想直接地跳转到目标页面,这时候就可以使用mvc:view-controller标签,在SpringMVC.xml配置文件中配置:

1
2
<!--path:设置处理的请求地址 view-name:设置请求地址所对应的视图名称-->
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>

等于

1
2
3
4
@RequestMapping(value="/testView")
public String hello(){
return "success";
}
  • 使用了这个标签后必须开启mvc注解驱动的标签 <mvc:annotation-driven />,否则会造成所有的@Controller注解无法解析,导致404错误。
  • 如果请求存在处理器,则这个标签对应的请求处理将不起作用。因为请求是先去找处理器处理,如果找不到才会去找这个标签配置

访问jsp页面的视图解析器

即InternalResourceView,需要在SpringMVC配置文件中配置

七、HttpMessageConverter

HttpMessageConverter,报文信息转换器,将获取请求报文,或将Java对象转换为响应报文(json)

HttpMessageConverter 提供了两个注解和两个类型:

  • @RequestBody,@ResponseBody
  • RequestEntity,ResponseEntity

@RequestBody

主要用来接收前端传递给后端json字符串中的数据的(请求体中的数据的);

@RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值


框架-SpringMVC
https://vickkkyz.fun/2022/07/04/Java/framework/SpringMVC/
作者
Vickkkyz
发布于
2022年7月4日
许可协议