言成言成啊 | Kit Chen's Blog

SpringMVC

发布于2021-08-10 21:39:14,更新于2021-12-23 21:58:43,标签:java spring  文章会持续修订,转载请注明来源地址:https://meethigher.top/blog

例子中很多都是接口形式的,虽然案例中使用的是html发送请求的方式,但是还是更推荐使用postman来调试,会方便许多。

一、基本概念

1.1 三层架构

我们的开发模式,一般都是基于两种形式,一种是C/S架构,也就是客户端/服务器;另一种是B/S架构,也就是浏览器/服务器。

在JavaEE开发中,几乎全都是基于B/S架构的开发。在B/S架构中,系统标准的三层架构包括:表现层、业务层、持久层。

  1. 表现层:如SpringMVC。负责接收客户端的请求、向客户端响应结果。表现层包括展示层和控制层。
  2. 业务层:如Spring。也就是service层,负责业务逻辑处理,表现层依赖业务层,业务层并不依赖表现层。
  3. 持久层:如Mybatis、Hibernate。

1.2 MVC模型

MVC全名是Model View Controller,是模型-视图-控制器的缩写,是一种用于设计创建web应用程序表现层的模式。MVC中每个部分各司其职

  • Model模型:通常指的是数据模型。一般情况下用于封装数据。
  • View视图:如html。展示数据,视图通常是依据数据模型来创建的。
  • Controller控制器:应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。

1.3 SpringMVC

SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架,是目前最主流的MVC框架之一。同样的MVC框架还有structs2。支持RestFul编程风格。

优势

  1. 清晰的角色划分
    • 前端控制器DispatcherServlet
    • 请求到处理器映射HandlerMapping
    • 处理器适配器HandlerAdapter
    • 视图解析器ViewResolver
    • 处理器或页面控制器Controller
    • 验证器Validator
    • 命令对象Command(请求参数绑定到的对象就叫命令对象)
    • 表单对象FormObject(提供给表单展示和提交到的对象叫表单对象)
  2. 命令对象就是一个pojo,直接就能作为业务对象
  3. SpringMVC基于组件的方式执行流程

1.4 restful编程

对比这张图里,就能大概了解restful是个啥意思了。

传统的编程中,控制器里面,save、findAll、update会支持各种请求方法,创建三个接口。

restful编程中,控制器里面只需要提供一个接口,接口按照不同的请求方式执行不同的操作。

restful编程参考RESTful API 设计指南 - 阮一峰的网络日志,如果你用了屏蔽广告插件导致内容无法显示,直接将其域名的js权限禁用掉就ok。

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面五个(括号里是对应的SQL命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
  • DELETE(DELETE):从服务器删除资源。

还有两个不常用的HTTP动词。

  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

下面是一些例子。

  • GET /zoos:列出所有动物园
  • POST /zoos:新建一个动物园
  • GET /zoos/ID:获取某个指定动物园的信息
  • PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
  • PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
  • DELETE /zoos/ID:删除某个动物园
  • GET /zoos/ID/animals:列出某个指定动物园的所有动物
  • DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

二、入门

2.1 实现

需求

  1. index.jsp发送请求
  2. 转发到success.jsp成功页面

步骤

  1. 搭建开发环境:使用maven提供的骨架搭建webapp
  2. 编写程序

pom.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
<?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>top.meethigher</groupId>
<artifactId>SpringMvc</artifactId>
<version>1.0</version>
<packaging>war</packaging>

<name>SpringMvc Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>


<!-- 版本锁定 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>


<build>
<finalName>SpringMvc</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--前端控制器-->
<servlet>
<servlet-name>dispacherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--不配置dispatcherServlet只会在第一次收到请求启动,配置为项目启动时启动。当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;当是一个负数时或者没有指定时,则指示容器在该servlet被请求时才加载。-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispacherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包 -->
<context:component-scan base-package="top.meethigher"/>
<!--配置spring开启注解mvc的支持-->
<mvc:annotation-driven/>

<!-- 配置视图解析器 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>

index.jsp

1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>入门程序</h3>
<a href="/hello">入门程序</a>
</body>
</html>

success.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>入门成功</h3>
</body>
</html>

HelloController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Controller
public class HelloController {
/**
* 修改默认构造方法,验证加载顺序
*/
public HelloController() {
System.out.println("启动");
}

@RequestMapping("/hello")
public String sayHello(){
System.out.println("Hello MVC");
return "success";
}
}

访问的hello,实际是转发请求到了success.jsp

2.2 原理

由于配置了dispatcherServlet为项目启动时就进行加载,所以像springmvc.xml,以及创建扫描的类和视图解析器的对象。

可以在添加了注解并且要进行扫描的类中,在默认构造方法中打印日志来验证,不同的配置的执行顺序。

dispatcherServlet主要体现在他的控制作用,客户端的请求过来,首先被dispatcherServlet捕获,然后将请求转发到对应的控制器中获取到返回值,根据这个返回值,通过视图解析器,解析到对应的结果视图。将返回内容返给客户端。

2.3 组件详解

DispatcherServlet:前端控制器

DispatcherServlet是整个流程控制的中心,由他来调用其他组件处理用户的请求,同时,也降低了组件之间的耦合性。

HandlerMapping:处理器映射器

HandlerMapping负责根据用户请求找到请求适配器HandlerAdapter。

HandlerAdapter:处理器适配器

通过HandlerAdapter对处理器进行执行,这是适配器模式的应用。通过扩展适配器可以对更多类型的处理器进行执行。

Handler:处理器

开发中要编写的具体业务控制器。由DispatcherServlet把用户请求转发到Handler,由Handler对具体的用户进行请求处理。

ViewResolver:视图解析器

View Resolver负责将处理结果生成view视图。View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

View:视图

SpringMVC提供了很多view视图的支持,像jstlView、freemarkerview、pdfView。

2.4 RequestMapping

通过阅读源码可知,@RequestMapping既可以放在类上也可以放在方法上。并且其中的valuepath作用是相同的。

如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略

如果在类上加了@RequestMapping注解,并且配置了value,那么,方法中所有的通过@RequestMapping配置的路径,都在类的value的路径之下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Controller
@RequestMapping("/hh")
public class HelloController {
/**
* 修改默认构造方法,验证加载顺序
*/
public HelloController() {
System.out.println("启动");
}

@RequestMapping(value = "/hello",method = {RequestMethod.DELETE,RequestMethod.GET},params = {"name","age"},headers = "aaa")
public String sayHello(){
System.out.println("Hello MVC");
return "success";
}
}

上面代码,访问sayHello方法时,就需要通过/hh/hello来访问。

method属性表示访问的请求方式。上述代码只能通过Delete、Get请求访问。

params属性用于指定限制请求参数的条件,必传的名称可以放入到params里面。上述代码必须携带name、age参数。

headers属性用于限制请求头的条件,必传的名称放入headers里面。

三、参数绑定

参数绑定通俗点说,就是获取参数。类似于Servlet通过getParameter获取参数。

基本参数

如果接收的参数是基本类型,可以直接通过在controller中写方法,就能自动接收了。

发送请求

1
<a href="hh/param?name=hh&age=20">入门程序</a>

获取参数

1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/hh")
public class HelloController {
@RequestMapping(value="/param")
public String getParam(String name,String age){
System.out.println("name="+name+",age="+age);
return "success";
}
}

实体参数

属性为基本或实体类型

如果是实体参数内部都是基本属性,可以直接在控制器方法中写对应实体,直接接收。

如果是实体参数内部嵌套了实体属性,就需要在发送时用对象属性.属性来发送请求,控制器那边直接写实体接收即可。

发送请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>入门程序</h3>
<a href="hh/param?name=hh&age=20">入门程序</a>
<h3>造钱</h3>
<form action="/hh/money" method="post">
<input name="money" placeholder="钱">
<button type="submit">submit</button>
</form>
<h3>造人</h3>
<form action="/hh/person" method="post">
<input name="name" placeholder="名">
<input name="age" placeholder="年龄">
<input name="money.money" placeholder="钱">
<button type="submit">submit</button>
</form>
</body>
</html>

控制器

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
@Controller
@RequestMapping("/hh")
public class HelloController {

/**
* 实体类
* @param money
* @return
*/
@RequestMapping(value="/money")
public String getMoney(Money money){
System.out.println(money);
return "success";
}

/**
* 实体类嵌套实体类
* @param person
* @return
*/
@RequestMapping(value="/person")
public String getPerson(Person person){
System.out.println(person);
return "success";
}
}

在post请求时,会出现后端获取中文出现乱码的情况。

如果是在Servlet中时,我们需要通过request.setCharacterEncoding(“utf-8”)来解决。

在Spring中,提供了类似的方法,需要在配置文件中配置过滤器

web.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
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--前端控制器-->
<servlet>
<servlet-name>dispacherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<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>
<servlet-mapping>
<servlet-name>dispacherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<!--配置解决中文乱码的过滤器-->
<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

通过阅读filter-class源码,我们可以知道其实本质上,它也是走的request.setCharacterEncoding这个方式。

属性为集合类型

直接看例子吧。

请求

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
<%--
Created by IntelliJ IDEA.
User: meethigher
Date: 2021/8/22
Time: 21:29
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>List与Map</h3>
<form action="/hh/people" method="post">
<input name="personList[0].name" placeholder="名"/>
<input name="personList[0].age" placeholder="年龄"/>
<input name="personList[0].money.money" placeholder="钱"/>
<input name="personList[1].name" placeholder="名"/>
<input name="personList[1].age" placeholder="年龄">
<input name="personList[1].money.money" placeholder="钱">

<input name="personMap['first'].name" placeholder="名"/>
<input name="personMap['first'].age" placeholder="年龄"/>
<input name="personMap['first'].money.money" placeholder="钱"/>
<input name="personMap['second'].name" placeholder="名"/>
<input name="personMap['second'].age" placeholder="年龄">
<input name="personMap['second'].money.money" placeholder="钱">
<button type="submit">submit</button>
</form>
</body>
</html>

控制器

1
2
3
4
5
6
7
8
9
10
@Controller
@RequestMapping("/hh")
public class HelloController {

@RequestMapping(value="/people")
public String getList(People people){
System.out.println(people);
return "success";
}
}

自定义类型转换器

Spring提供的转换器都实现了Convertor这个接口,所以如果我们要实现自定义类型转换器的话,也需要实现该接口。

Convertor的两个泛型,前面指的是源类型、后面指的是目标类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (ObjectUtils.isEmpty(source)) {
throw new RuntimeException("必须传入数据!");
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
return sdf.parse(source);
} catch (ParseException e) {
throw new RuntimeException("必须传入数据!格式yyyy-MM-dd");
}
}
}

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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包 -->
<context:component-scan base-package="top.meethigher"/>


<!-- 配置视图解析器 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>

<!--配置自定义类型转换器-->
<bean id="customConversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="top.meethigher.controller.StringToDateConverter"/>
</set>
</property>
</bean>

<!--配置spring开启注解mvc的支持 并支持自定义类型转换器-->
<mvc:annotation-driven conversion-service="customConversionService"/>
</beans>

发送请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>创建一只狗</h3>
<form action="/hh/dog" method="post">
<input name="name" placeholder="名">
<input name="birth" placeholder="生日">
<button type="submit">submit</button>
</form>
</body>
</html>

控制器

1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/hh")
public class HelloController {
@RequestMapping(value = "/dog")
public String getDog(Dog dog) {
System.out.println(dog);
return "success";
}
}

获取Servlet原生API

如果想要获取servlet的request,那就直接在控制器方法中加HttpServletRequest。

如果想要获取servlet的response,那就直接在控制器方法中加HttpServletResponse。

发送请求

1
<a href="/hh/api">servletAPI</a>

控制器

1
2
3
4
5
6
7
8
9
10
11
12
@Controller
@RequestMapping("/hh")
public class HelloController {
@RequestMapping(value = "/api")
public String getApi(HttpServletRequest request, HttpServletResponse response) {
System.out.println(request);
System.out.println(response);
System.out.println(request.getSession());
System.out.println(request.getSession().getServletContext());
return "success";
}
}

四、常用注解

4.1 RequestParam

作用

把请求中的指定参数,给控制器方法中参数赋值

属性

  • value:请求的参数名称
  • required:请求参数中此参数是否必传。默认值true

请求

1
<a href="/testRequestParam?username=ccc">requestParam</a>

控制器

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

@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam(value="username") String username1) {
System.out.println(username1);
return "success";
}
}

如果不加@RequestParam,那么调用该接口时,没有对应的username属性来接收内容,输出为空。

如果加了@RequestParam,里面默认的required=true,那么调用该接口时,如果传值不对,会报错。

4.2 RequestBody

作用

get请求方式不适用。

用于获取请求体内容。直接使用,获取到的字符串是key=value&key=value的结构的数据。

属性

  • required:是否必须有请求体,默认为true

请求

1
2
3
4
5
6
<form action="/testRequestBody" method="post">
<input name="name" placeholder="名">
<input name="age" placeholder="年龄">
<input name="money.money" placeholder="钱">
<button type="submit">submit</button>
</form>

控制器

1
2
3
4
5
6
7
8
@Controller
public class AnnotationController {
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String body) {
System.out.println(body);
return "success";
}
}

4.3 PathVariable

@PathVariable接收两个参数

作用

绑定url中的占位符。例如/findById/{id},这个{id}就是url中的占位符。

url支持占位符是spring3.0之后加入的,是springmvc支持rest风格url的一个重要标志。参考1.4

属性

  • value:url中占位符名称
  • required:是否必须提供占位符

发送请求

1
<a href="/testPathVariable/ccc">pathVariable</a>

控制器

1
2
3
4
5
@RequestMapping("/testPathVariable/{id}")
public String testPathVariable(@PathVariable(value = "id") String id) {
System.out.println(id);
return "success";
}

注意事项

因为form表单只支持get、post请求,所以如果想要使用restful的话,那么就需要用到别的请求方式。这边SpringMVC提供了HiddenHttpMethodFilter过滤器,参考文章Spring MVC过滤器-HiddenHttpMethodFilter - 一个坚果 - 博客园

4.4 RequestHeader

作用

获取请求消息头

属性

  • value:提供消息头名称
  • required:是否必须有此消息头

没啥用,就是获取请求头,倒不如用Servlet原生api

1
2
3
4
5
6
@RequestMapping("/testPathVariable/{id}")
public String testPathVariable(@PathVariable(value = "id") String id, @RequestHeader("User-Agent") String userAgent) {
System.out.println(id);
System.out.println(userAgent);
return "success";
}

4.5 CookieValue

作用

获取指定名称cookie

属性

  • value:指定cookie名称
  • required:是否必须有该Cookie

没啥用,就是获取请求头,倒不如用Servlet原生api

1
2
3
4
5
6
7
@RequestMapping("/testPathVariable/{id}")
public String testPathVariable(@PathVariable(value = "id") String id, @RequestHeader("User-Agent") String userAgent,@CookieValue("JSESSIONID") String session) {
System.out.println(id);
System.out.println(userAgent);
System.out.println(session);
return "success";
}

4.5 ModelAttribute

作用

  1. 放在方法上,表示当前方法会在控制器的方法执行前先执行。它可以修饰没有返回值的方法,也可以修改有具体返回值的方法。
  2. 放在参数上,获取指定的参数给数据赋值。

应用场景

  1. 当前端的表单提交数据,无法获取和提交完整的数据时,可以通过@ModelAttribute通过数据库补全缺少的数据。比如,前端在执行变更时,只需要传递过来要变更的id,以及要修改的字段内容,后台通过@ModelAttribute来补全未变更的数据,之后直接保存对象到数据库里。

没有特定条件的@ModelAttribute会在所有控制器的方法执行前执行

如:

1
2
3
4
@ModelAttribute
public void showMsg(){
System.out.println("@ModelAttribute执行");
}

如果想要让控制器的方法获取到@ModelAttribute返回的对象,有两种方式

法一:@ModelAttribute修饰的方法带有返回值

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping("/testDog")
public String testDog(Dog dog) {
System.out.println(dog);
return "success";
}
@ModelAttribute
public Dog modelAttribute(@RequestParam(value = "username1") String username1) {
System.out.println("modelAttribute执行了");
System.out.println(username1);
return new Dog("aaa", new Date());
}

上面这个例子,如果要访问testDog接口,会先访问modelAttribute方法,并且要求必传username1,然后再执行testDog方法。

如果传了username1,又传了Dog对象,那么username1就只是作为一个必传的入参了,优先级没有直接传入的Dog对象优先级高。

法二:@ModelAttribute修饰的方法不带返回值

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping("/testDog")
public String testDog(@ModelAttribute("dog") Dog dog) {
System.out.println(dog);
return "success";
}
@ModelAttribute
public void modelAttribute(@RequestParam(value = "username1") String username1, Map<String, Dog> map) {
System.out.println("modelAttribute执行了");
System.out.println(username1);
map.put("dog", new Dog("aaa", new Date()));
}

最终的效果其实跟法一是一样的。并没有因为换了方法优先级就高了之类。

4.6 SessionAttribute

作用

用于多次执行控制器方法间的参数在Session间共享。

属性

  • value:存入的属性名称
  • type:存入的数据类型

success.jsp

1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>入门成功</h3>
${requestScope.msg}
</body>
</html>

Controller中方法

1
2
3
4
5
6
7
@RequestMapping("/testSession")
public String testSession(Model model){
System.out.println("测试SessionAttributes注解");
//底层会给存储到requestScope对象中,可以通过跳转后的jsp来获取
model.addAttribute("msg","彩麟yyds");
return "success";
}

Model是存储在RequestScope中的,如果想要存到SessionScope,需要通过@SessionAttributes。

查看@SessionAttributes源码,可知该注解只能作用于类上。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SessionAttributes {
@AliasFor("names")
String[] value() default {};

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

Class<?>[] types() default {};
}

通过@SessionAttributes将RequestScope中的msg存入到SessionScope中

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
@Controller
@SessionAttributes(value = "msg")
public class AnnotationController {
@RequestMapping("/testSession")
public String testSession(Model model){
System.out.println("测试SessionAttributes注解");
//底层会给存储到requestScope对象中,可以通过跳转后的jsp来获取
model.addAttribute("msg","彩麟yyds");
return "success";
}

@RequestMapping("/getMsg")
public String getMsg(ModelMap modelMap){
//ModelMap用于获取SessionScope中的内容
System.out.println(modelMap.get("msg"));
return "success";
}

@RequestMapping("/delMsg")
public String delMsg(SessionStatus status){
//结束当前会话,清除Session中内容
status.setComplete();
return "success";
}
}

4.7 ResponseBody

@ResponseBody作用于方法上,表示该方法的返回结果直接写入到响应中,一般用于前端异步获取数据时,也就是接口。

@RestController=@Controller+@ResponseBody

@Controller可以返回到指定的jsp、html等模板引擎页面

springBoot的转发和重定向_anjunshuang-CSDN博客_springboot转发

发布:2021-08-10 21:39:14
修改:2021-12-23 21:58:43
链接:https://meethigher.top/blog/2021/springmvc/
标签:java spring 
付款码 打赏 分享
Shift+Ctrl+1 可控制工具栏