摘要
SpringMVC表现层这块的东西,倒是不难使用,直接上手就行。
但是经常会有东西不理解怎么实现的,可能是封装的太好了,会用却不理解,然后就有去翻源码的操作,所以打算系统学习一下。
正文
例子中很多都是接口形式的,虽然案例中使用的是html发送请求的方式,但是还是更推荐使用postman来调试,会方便许多。
一、基本概念
1.1 三层架构
我们的开发模式,一般都是基于两种形式,一种是C/S架构,也就是客户端/服务器;另一种是B/S架构,也就是浏览器/服务器。
在JavaEE开发中,几乎全都是基于B/S架构的开发。在B/S架构中,系统标准的三层架构包括:表现层、业务层、持久层。
- 表现层:如SpringMVC。负责接收客户端的请求、向客户端响应结果。表现层包括展示层和控制层。
- 业务层:如Spring。也就是service层,负责业务逻辑处理,表现层依赖业务层,业务层并不依赖表现层。
- 持久层:如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编程风格。
优势
- 清晰的角色划分
- 前端控制器DispatcherServlet
- 请求到处理器映射HandlerMapping
- 处理器适配器HandlerAdapter
- 视图解析器ViewResolver
- 处理器或页面控制器Controller
- 验证器Validator
- 命令对象Command(请求参数绑定到的对象就叫命令对象)
- 表单对象FormObject(提供给表单展示和提交到的对象叫表单对象)
- 命令对象就是一个pojo,直接就能作为业务对象
- 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 实现
需求
- index.jsp发送请求
- 转发到success.jsp成功页面
步骤
- 搭建开发环境:使用maven提供的骨架搭建webapp
- 编写程序
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
<%@ 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
<%@ 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既可以放在类上也可以放在方法上。并且其中的value与path作用是相同的。
如果只有一个属性需要赋值,并且属性的名称是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";
}
}
|
实体参数
属性为基本或实体类型
如果是实体参数内部都是基本属性,可以直接在控制器方法中写对应实体,直接接收。
如果是实体参数内部嵌套了实体属性,就需要在发送时用对象属性.属性来发送请求,控制器那边直接写实体接收即可。
发送请求
<%@ 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这个方式。
属性为集合类型
直接看例子吧。
请求
<%--
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>
|
发送请求
<%@ 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。
发送请求
<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
请求
<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
请求
<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 - 一个坚果 - 博客园
作用
获取请求消息头
属性
- 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
作用
- 放在方法上,表示当前方法会在控制器的方法执行前先执行。它可以修饰没有返回值的方法,也可以修改有具体返回值的方法。
- 放在参数上,获取指定的参数给数据赋值。
应用场景
- 当前端的表单提交数据,无法获取和提交完整的数据时,可以通过@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
<%@ 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转发