- SpringMVC注解式开发
- @RequestMapping注解详解
- 五种数据提交方式
- 处理器方法的返回值
- SpringMVC的四种跳转方式
- ……
二、SpringMVC注解式开发
- 所谓 SpringMVC 的注解式开发是指,在代码中通过对类与方法的注解,便可完成处理器在 SpringMVC 容器的注册。
- 注解式开发是重点。
2.1 ★@RequestMapping注解详解
通过
@RequestMapping注解可以定义处理器对于请求的映射规则。value属性值常以 “/” 开始,@RequestMapping的value属性用于对应匹配请求的URI。一个
@Controller所注解的类中,可以定义多个处理器方法。当然,不同的处理器方法所匹配的URI是不同的。这些不同的URI被指定在注解于方法之上的@RequestMapping的value属性中。但若这些请求具有相同的
URI部分,则这些URI相同的部分可以被抽取到注解在类之上的@RequestMapping的value属性中。此时这个URI表示模块(相当于包的作用)的名称。此注解就是用来映射服务器访问的路径的。
@RequestMapping注解可以注解在方法上,也可以注解在类上,但意义是不同的。**用在方法上,是为此方法注册一个可以访问的名称(路径)**。
用在类上,相当于是“包名”的作用,用于区分不同类中相同的
action的名称。例如:有两个
Action类HelloAction和HiAction:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.f.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author fzy
* @date 2024/2/4 14:42
*/
// 由Spring来创建该类的实例对象
public class HelloAction {
public String hello() {
System.out.println("访问服务器hello成功!");
return "main"; // 这样可以直接跳到 /admin/main.jsp 页面上
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.f.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author fzy
* @date 2024/2/4 16:21
*/
public class HiAction {
public String hi() {
System.out.println("访问服务器hi成功!");
return "main";
}
}它们的方法的映射路径都是
/demo.action,为了区分访问的前者的action还是后者的action,在这两个Action类上添加@RequestMapping注解,用以区分。相应的,
index.jsp文件也要进行一些修改:1
2
3
4
5
6
7
8
9
10
11
12<%--index.jsp文件--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/hello/demo.action">访问hello服务器</a>
<br/>
<a href="${pageContext.request.contextPath}/hi/demo.action">访问hi服务器</a>
</body>
</html>
对于
@RequestMapping,其有一个属性method,用于对被注解方法所处理请求的提交方式进行限制,即只有满足该method属性指定的提交方式的请求,才会执行该被注解方法。method属性的取值为RequestMethod枚举常量。常用的为RequestMethod.GET与RequestMethod.POST,分别表示请求提交方式的匹配规则为GET请求与POST请求。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package com.f.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author fzy
* @date 2024/2/4 16:55
*/
public class ReqAction {
public String doGet() {
System.out.println("get请求处理");
return "main";
}
public String doPost() {
System.out.println("post请求处理");
return "main";
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<%--index.jsp文件--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<h1>get请求</h1>
<form action="${pageContext.request.contextPath}/req.action" method="get">
用户名:<input name="getUsername" value="">
密 码:<input name="getPassword" value="">
<input type="submit" value="get请求提交">
</form>
<br/>
<h1>post请求</h1>
<form action="${pageContext.request.contextPath}/req.action" method="post">
用户名:<input name="postUsername" value="">
密 码:<input name="postPassword" value="">
<input type="submit" value="post请求提交">
</form>
</body>
</html>- 可以发现,即使
get请求和post请求的请求路径一样,@RequestMapping也能根据不同的请求类型找到对应的方法,对请求进行处理。
- 可以发现,即使
2.2 五种数据提交方式
- 这里对应
1.3小节SpringMVC优化的方向中的 “数据提交的优化”。
2.2.1 单个提交数据
在方法中声明一个和表单提交的参数名称相同的参数,由框架按照名称直接注入。
1
2
3
4
5
6
7
8
9
10
11
12
13/**
* <form action="${pageContext.request.contextPath}/one.action" method="post">
* 姓名:<input name="name" value="">
* 年龄:<input name="age" value="">
* <input type="submit" value="提交">
* </form>
*/
public String one(String name, int age) { // 只需保证参数名和前端对应的名称一致即可
System.out.println("姓名为:" + name);
System.out.println("年龄为:" + age);
return "main";
}
2.2.2 ★对象封装提交数据
在方法中声明一个自定义的实体类参数,框架调用实体类中相应的
setter方法注入属性值,需要保证实体类中成员变量的名称与提交请求的name属性值一致。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18/**
* <form action="${pageContext.request.contextPath}/two.action" method="post">
* 姓名:<input name="uname" value="">
* 年龄:<input name="uage" value="">
* <input type="submit" value="提交">
* </form>
*
* public class User {
* private String uname;
* private int uage;
* ...
* }
*/
public String two(User user) { // 需要保证类中的字段名和前端对应的名称一致
System.out.println(user);
return "main";
}- 在提交请求中,保证请求参数的名称与实体类中成员变量的名称一致,则可以自动创建对象,则可以自动提交数据,自动类型转换,自动封装数据到对象中。
2.2.3 动态占位符提交
使用框架提供的一个注解
@PathVariable,将请求url中的值作为参数进行提取,只能是超链接。是restful风格下的数据提取方式。restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
1
2
3
4
5
6
7
8
9
10
11
12
13/**
* <a href="${pageContext.request.contextPath}/three/张三/22.action">动态占位符提交</a>
*/
public String three(
String aaa, // 类似于起别名
int bbb) { // 前端一杠一值,后端一杠一大括号
System.out.println("姓名为:" + aaa);
System.out.println("年龄为:" + bbb);
return "main";
}- 仅限于超链接或地址拦提交数据。它是“前端一杠一值,后端一杠一大括号”,使用注解
@PathVariable来解析。
2.2.4 ★映射名称不一致
提交请求参数与
action方法的形参的名称不一致,可以使用注解@RequestParam来解析。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/**
* <form action="${pageContext.request.contextPath}/four.action" method="post">
* 姓名:<input name="uname" value="">
* 年龄:<input name="uage" value="">
* <input type="submit" value="提交">
* </form>
*/
public String four(
String name,
int age) {
System.out.println("姓名为:" + name);
System.out.println("年龄为:" + age);
return "main";
}@RequestParam专门用于解决前端请求参数和后端方法的形参名称不一致的问题。
2.2.5 手工提取数据
在方法参数中声明一个
request对象,使用request的getParameter()获取表单提交的数据,这样得到的数据还要手工进行数据类型的转换。1
2
3
4
5
6
7
8
9
10
11
12
13/**
* <form action="${pageContext.request.contextPath}/five.action" method="post">
* 姓名:<input name="uname" value="">
* 年龄:<input name="uage" value="">
* <input type="submit" value="提交">
* </form>
*/
public String five(HttpServletRequest request) {
System.out.println("姓名为:" + request.getParameter("uname"));
System.out.println("年龄为:" + Integer.parseInt(request.getParameter("uage")));
return "main";
}
2.3 处理器方法的返回值
- 这里对应
1.3小节SpringMVC优化的方向中的 “携带数据优化” 之一。
2.3.1 String
- 客户端资源的地址,可以自动拼接前缀和后缀,也可以屏蔽自动拼接字符串,指定返回的路径。
- 处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址。
2.3.2 Object
- 返回
json格式的对象,自动将对象或集合转为json。- 使用
jackson工具进行转换,必须要添加jackson依赖。一般用于ajax请求。
- 使用
- 处理器方法也可以返回
Object对象。这个Object可以是Integer,自定义对象,Map,List等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。- 返回对象需要使用
@ResponseBody注解,将转换后的JSON数据放入到响应体中。
- 返回对象需要使用
Ajax请求多用于Object返回值类型。由于转换器底层使用了Jackson转换方式将对象转换为JSON数据,所以需要添加Jackson的相关依赖。
2.3.3 void
- 无返回值,一般用于
ajax请求。 - 对于处理器方法返回
void的应用场景,应用在AJAX响应处理。若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回void。
2.3.4 基本数据类型
- 用于
ajax请求。
2.3.5 ModelAndView
- 若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回
ModelAndView比较好。当然,若要返回ModelAndView,则处理器方法中需要定义ModelAndView对象。 - 在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的
Ajax异步响应),此时若返回ModelAndView,则将总是有一部分多余:要么Model多余,要么View多余。即此时返回ModelAndView将不合适。 - 较少使用。
2.4 ★SpringMVC的四种跳转方式
- 默认的跳转是请求转发,直接跳转到
jsp页面展示。- 还可以使用框架提供的关键字
redirect:,进行一个重定向操作,包括重定向页面和重定向action。 - 还可以使用框架提供的关键字
forward:,进行服务器内部转发操作,包括转发页面和转发action。- 当使用
redirect:和forward:关键字时,视图解析器中前缀后缀的拼接就自动无效了。
- 当使用
- 还可以使用框架提供的关键字
2.4.1 请求转发页面
2.4.2 请求转发action
2.4.3 重定向页面
2.4.4 重定向action
本质还是两种跳转:请求转发和重定向,衍生出四种是:
请求转发页面
1
2
3
4
5
public String one() {
System.out.println("默认为请求转发页面...");
return "main"; // 默认是请求转发,使用视图解析器拼接前缀后缀,然后进行页面跳转
}- 也可以使用
"forward:/admin/main.jsp"来请求转发页面。那就不局限于视图解析器中配置的前缀后缀,而是可以任意转发页面了。
- 也可以使用
请求转发
action1
2
3
4
5
6
public String two() {
System.out.println("请求转发action...");
// 使用"forward:"可以屏蔽视图解析器前缀后缀的拼接
return "forward:/other.action"; // 请求转发action
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.f.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author fzy
* @date 2024/2/5 17:25
*/
public class OtherAction {
public String other() {
System.out.println("跳转到了/other.action");
return "main";
}
}重定向页面
1
2
3
4
5
6
public String three() {
System.out.println("重定向页面...");
// 使用"redirect:"可以屏蔽视图解析器前缀后缀的拼接
return "redirect:/admin/main.jsp"; // 重定向页面
}重定向
action1
2
3
4
5
6
public String four() {
System.out.println("重定向action...");
// 使用"redirect:"可以屏蔽视图解析器前缀后缀的拼接
return "redirect:/other.action"; // 重定向action
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.f.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author fzy
* @date 2024/2/5 17:25
*/
public class OtherAction {
public String other() {
System.out.println("跳转到了/other.action");
return "main";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<%--index.jsp文件--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<h1>SpringMVC的四种跳转方式</h1>
<a href="${pageContext.request.contextPath}/one.action">1.请求转发页面(默认)</a>
<br/>
<a href="${pageContext.request.contextPath}/two.action">2.请求转发action</a>
<br/>
<a href="${pageContext.request.contextPath}/three.action">3.重定向页面</a>
<br/>
<a href="${pageContext.request.contextPath}/four.action">4.重定向action</a>
</body>
</html>1
2
3
4
5
6
7
8
9
10<%--main.jsp文件--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>main</title>
</head>
<body>
<h1>main</h1>
</body>
</html>
2.5 SpringMVC支持的默认参数类型
- 不需要去创建,直接拿来使用即可。
2.5.1 HttpServletRequest
2.5.2 HttpServletResponse
2.5.3 HttpSession
2.5.4 Model
2.5.5 Map
2.5.6 ModelMap
- 注意:
Map,Model,ModelMap和request一样,都使用请求作用域进行数据传递。所以服务器端的跳转必须是请求转发。
1 | <%--index.jsp文件--%> |
1 | package com.f.springmvc.controller; |
1 | <%--main.jsp文件--%> |
2.6 日期处理
2.6.1 日期的提交处理
单个日期处理
要使用注解
@DateTimeFormat,此注解必须搭配springmvc.xml文件中的<mvc:annotationdriven标签>,该标签用于启动注解驱动。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.f.springmvc.controller"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/admin/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--启用注解驱动-->
<mvc:annotation-driven/>
</beans>1
2
3
4
5
6
7
8
9
10
11
12
13
14<%--index.jsp文件--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/mydate.action">
日期:<input type="date" name="mydate">
<br/>
<input type="submit" value="提交">
</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
24package com.f.springmvc.controller;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Date;
/**
* @author fzy
* @date 2024/2/5 20:22
*/
public class MyDateAction {
public String myDate(
// 使用DateTimeFormat注解
Date mydate) {
System.out.println(mydate);
return "main";
}
}
类中全局日期处理
上面那种解决方案要在每个使用日期类型的地方都去添加
@DateTimeFormat注解,比较麻烦,我们可以使用@InitBinder注解来进行类中统一日期类型的处理。用
@InitBinder注解注册日期类型,用来解析本类中所有的日期类型,自动转换。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
36package com.f.springmvc.controller;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author fzy
* @date 2024/2/5 20:22
*/
public class MyDateAction {
// 注册一个全局的日期处理注解
public void initBinder(WebDataBinder binder) {
// 注册一个自定义的转换器
binder.registerCustomEditor(Date.class, new CustomDateEditor(
new SimpleDateFormat("yyyy-MM-dd"), true
));
}
public String myDate(
Date mydate) {
System.out.println(mydate);
return "main";
}
}- 用这种方法,甚至不需要
<mvc:annotation-driven/>了。
- 用这种方法,甚至不需要
2.6.2 日期的显示处理
要在页面上显示好看的日期:
- 如果是单个日期对象,可以在处理器中用
SimpleDateFormat将日期直接转为好看的格式化的字符串,然后转发给前端进行显示。 - 如果是实体类对象的成员变量,并且是日期类型,而且是将实体类对象转发给前端,则必须使用
JSTL标签库才能显示好看的日期。
- 如果是单个日期对象,可以在处理器中用
具体代码看别人笔记吧,主要是前端来做。
2.7 <mvc:annotation-driven/>标签的使用
<mvc:annotation-driven/>会自动注册两个bean,分别为DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter。是 SpringMVC 为@Controller分发请求所必须的。除了注册了这两个bean以外,还提供了很多支持。- 支持使用
ConversionService实例对表单参数进行类型转换。 - 支持使用
@NumberFormat、@DateTimeFormat注解完成数据类型的格式化。 - 支持使用
@RequestBody和@ResponseBody注解。 - 静态资源的分流也使用这个标签。
- 支持使用
2.8 ★访问WEB-INF目录下的资源
WEB-INF目录下的动态资源,不能直接访问,只能通过请求转发的方式进行访问。- 重定向无法访问动态资源。
很多企业会将动态资源放在
WEB-INF目录下,这样可以保证资源的安全性。在WEB-INF目录下的动态资源不可以直接访问,必须要通过请求转发的方式进行访问。这样避免了通过地址栏直接对资源进行访问。1
2
3
4
5
6
7
8
9
10<%--index.jsp文件--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/access.action">访问WEB-INF目录下的资源</a>
</body>
</html>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.f.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author fzy
* @date 2024/2/5 22:14
*/
public class AccessAction {
public String access() {
System.out.println("开始访问WEB-INF目录下的资源...");
// 通过请求转发的方式访问WEB-INF目录下的资源
return "forward:/WEB-INF/jsp/main.jsp";
}
}
2.9 去掉action后缀
在对
DispatcherServlet的<url-pattern>进行配置时,如果是/的配置,则表示拦截所有请求,但放行静态资源。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<servlet>
<servlet-name>springmvc</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>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>- 在将
*.action更改为/以后,DispatcherServlet会拦截所有请求(放行静态资源),而不仅仅只拦截.action结尾的请求。- 那以后请求可以不用再带
.action后缀了。
- 那以后请求可以不用再带
- 在将
另:
/*和/的区别:/*拦截所有请求(包括.jsp)。/拦截所有请求,但放行静态资源。- 如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的
Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。
- 如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的