文章目录
SpringMVC——基于 Spring 的 Web 层 MVC 框架
SpringMVC介绍
SpringMVC就是接管服务端的web层的一个web框架。
SpringMVC基本介绍
- SpringMVC 是 WEB 层框架【老韩解读: SpringMVC 接管了 Web 层组件, 比如控制器, 视图, 视图解析, 返回给用户的数据格式, 同时支持 MVC 的开发模式/开发架构】
- SpringMVC 通过注解,让 POJO 成为控制器,不需要继承类或者实现接口
- SpringMVC 采用低耦合的组件设计方式,具有更好扩展和灵活性. 5. 支持 REST 格式的 URL 请求. 6.SpringMVC 是基于 Spring 的, 也就是 SpringMVC 是在 Spring 基础上的。SpringMVC 的核心包 spring-webmvc-xx.jar 和 spring-web-xx.jar
梳理 Spring SpringMVC SpringBoot 的关系
- Spring MVC 只是 Spring 处理 WEB 层请求的一个模块/组件, Spring MVC 的基石是Servlet[Java WEB]
- Spring Boot 是为了简化开发者的使用, 推出的封神框架(约定优于配置,简化了 Spring的配置流程), SpringBoot 包含很多组件/框架,Spring就是最核心的内容之一,也包含 SpringMVC
- 他们的关系大概是: Spring Boot > Spring > Spring MVC
SpringMVC-快速入门
需求说明: 完成一个最基本的测试案例,登录案例, 使用 SpringMVC 完成
SpringMVC 登录流程分析
流程:
- 创建 springmvc web 工程并配置 tomcat, 前面我们学习过
- 导入 SpringMVC 开发需要的 jar 包
- 创建 src/applicationContext-mvc.xml 文件(就是 spring 的容器文件),文件名自己定
- 配置 WEB-INF/web.xml
<?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>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!--springmvc的配置文件和以前spring的配置文件类似-->
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
<!--在web项目启动时,就加载这个servlet实例。 1 表示加载的顺序号。-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!--为了支持rest风格的url,这里的url-pattern需要写成/-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 创建 D:\idea_java_projects\springmvc\web\login.jsp
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/2/16
Time: 14:02
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<h3>登录页面</h3>
<form action="login">
u:<input name="username" type="text"> <br/>
p:<input name="password" type="password"><br/>
<input type="submit" value="登录">
</form>
</body>
</html>
- 创建 D:\idea_java_projects\springmvc\src\com\hspedu\web\UserServlet.java
package com.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Author: GQLiu
* @DATE: 2024/2/16 14:06
* @Controller 将其当作Servlet使用。
* 这里配置@Controller就已经被springMVC框架接管。
*/
@Controller
public class UserServlet {
@RequestMapping(value = "/login")
public String login() {
System.out.println("login ok~");
return "login_ok";
}
}
- 创建 D:\idea_java_projects\springmvc\web\WEB-INF\pages\login_ok.jsp
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/2/16
Time: 14:08
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录成功</title>
</head>
<body>
<h1>恭喜 登录成功!</h1>
</body>
</html>
- 配置 applicationContext-mvc.xml
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!--配置自动扫描的包-->
<context:component-scan base-package="com.web"/>
<!--配置SpringMVC的视图解析器,比如Servlet返回的login_ok页面就是/WEB-INF/pages/ok.jsp-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--prefix表示前缀-->
<property name="prefix" value="/WEB-INF/pages/"/>
<!--suffix表示后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 测试:
注意事项和细节说明
- 重点学习如何搭建一个 springmvc 项目,初步理解 springmvc 工作流程
- 这里的 UserServlet 需要注解成@Controller ,我们称为一个 Handler 处理器
- UserServlet 指定 url 时,还可以这样
4. 关 于 SpringMVC 的 DispatcherServlet 的 配 置 文 件 , 如 果 不 在 web.xml 指 定applicationContext-mvc.xml, 默认在 /WEB-INF/springDispatcherServlet-servlet.xml 找这个配置文件【简单看下 DispatcherServlet 的源码】。(推荐使用, 我们做下修改 , 并完成测试)
<init-param>
<param-name>contextConfigLocation</param-name>
<!--springmvc的配置文件和以前spring的配置文件类似-->
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
上面就是在指定关于 SpringMVC 的 DispatcherServlet 的 配 置 文 件
修改步骤:
- 修改 web.xml, 注销 init-param 配置节点
- 剪 切 原 applicationContext-mvc.xml 到 /WEB-INF 目 录 下 , 文 件 名 为 : 你 配 置 的DispatcherSerlvet 名 字 -servlet.xml
按照上面步骤后同样可以登陆成功。
SpringMVC 工作流程分析
第一步:用户发送请求
用户发起请求到:前端控制器DispatcherServlet(也叫中央控制器)。
第二步:请求查询
前端控制器收到请求调用处理器映射器:请求处理器映射器HandlerMappering去查找处理器Handler(快速入门中的servlet),通过xml配置或者注解进行查找。
第三步:返回处理器执行链
找到以后处理器映射器:HandlerMappering 向前端控制器返回执行链:HandlerExecutionChain。
第四步:前端控制器调用处理器适配器
前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)。
第五步:处理器适配器去执行Handler
第六步:处理器执行完成并返回结果给前端控制器
Handler执行完给处理器适配器返回ModelAndView。
第七步:前端控制器接受到结果,并讲结果传给视图解析器
处理器适配器向前端控制器返回ModelAndView。
第八步:进行视图解析
前端控制器请求视图解析器(ViewResolver)去进行视图解析。
第九步:返回View
视图解析器像前端控制器返回View。
第十步:渲染
前端控制器对视图进行渲染。
第十一步:返回用户结果
前端控制器向用户响应结果
RequestMapping
就是在请求的url后面添加东西,在类中用RequestMapping()是什么就需要在url后面添加什么。
有很多其他注解方式:
基本使用
@RequestMapping 注解可以指定控制器/处理器的某个方法的请求的 url, 基本用法前面我们讲过了, RequestMapping : 请求映射
@RequestMapping 可以修饰方法和类
- 说明: @RequestMapping 注解可以修饰方法,还可以修饰类 当同时修饰类和方法时,
请求的 url 就是组合 /类请求值/方法请求值 .
实例:
- 创建 D:\idea_java_projects\springmvc\src\com\hspedu\web\UserHandler.java
package com.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @Author: GQLiu
* @DATE: 2024/2/16 15:21
*/
@RequestMapping(value = "/user")
@Controller
public class UserHandler {
/**
* 1. method=RequestMethod.POST 表示请求buy目标方法必须是post
* 2. RequestMethod 四个选项 POST, GET, PUT, DELETE
* 3. SpringMVC控制器默认支持GET和POST两种方式
* 4. buy 请求的url 为 http:localhost:8080/工程路径/user/buy
* @return
*/
@RequestMapping(value = "/buy", method = RequestMethod.POST)
public String buy() {
System.out.println("购买商品");
return "success";
}
}
- 创建 D:\idea_java_projects\springmvc\web\request.jsp
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/2/16
Time: 15:23
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<body>
<h1>购买商品</h1>
<%--
1. action=user/buy 对应url为 http://localhost:8080/工程路径/user/buy
--%>
<form action="user/buy" method="POST">
购买人:<input type="text" name="username"><br>
够买量:<input type="text" name="nums"><br>
<input type="submit" value="购买">
</form>
</body>
</html>
- 创建 D:\java_projects\springmvc\web\WEB-INF\pages\success.jsp
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/2/16
Time: 15:23
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>操作成功</title>
</head>
<body>
<h1>恭喜, 操作成功~</h1>
</body>
</html>
- 完成测试:访问界面
成功跳转:
@RequestMapping 可以指定请求方式
- 说明: @RequestMapping 还可以指定请求的方式(post/get/put/delete…), 请求的方式需要和指定的一样,否则报错
- SpringMVC 控制器默认支持GET和POST两种方式, 也就是你不指定 method , 可以接收GET 和 POST 请求
- 当明确指定了 method , 则需要按指定方式请求, 否则会报错,比如
@RequestMapping 可指定 params 和 headers 支持简单表达式
- param1: 表示请求必须包含名为 param1 的请求参数
- !=param1: 表示请求不能包含名为 param1 的请求参数
- param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1
- {“param1=value1”, “param2”}: 请求必须包含名为 param1 和 param2 的两个请求参数,且 param1 参数的值必须为 value1
- 应用实例:
- 修改 UserHandler.java , 增加方法
/**
* @param bookId
* params = "bookId=100" 表示参数bookId必须设定,且值必须是100.如果不是就不匹配.
* @return
*/
@RequestMapping(value = "/find", params = "bookId", method = RequestMethod.GET)
public String search(String bookId) {
System.out.println("查询书籍 bookId= " + bookId);
return "success";
}
- 修改 request.jsp , 增加代码
<hr>
<h1>演示 params 的使用</h1>
<a href="user/find?bookId=100">查询书籍</a>
- 小的细节说明和测试
- 需要有 bookId 参数,并且值为 100, 否则报错
- 需要有 bookId 参数,并且值不为 100, 否则报错
- 需要有 bookId 参数,并且值为 100, 否则报错
@RequestMapping 支持 Ant 风格资源地址
-
?:匹配文件名中的一个字符
-
*:匹配文件名中的任意字符
-
**:匹配多层路径
-
Ant 风格的 url 地址举例
/user/*/createUser: 匹配 /user/aaa/createUser、/user/bbb/createUser 等 URL
/user/**/createUser: 匹配 /user/createUser、/user/aaa/bbb/createUser 等 URL
/user/createUser??: 匹配 /user/createUseraa、/user/createUserbb 等 URL -
应用实例
@RequestMapping(value = "/message/**")
public String im() {
System.out.println("发送消息");
return "success";
}
@RequestMapping 可配合 @PathVariable 映射 URL 绑定的占位符
@RequestMapping 还可以配合 @PathVariable 映射 URL 绑定的占位符。这样就不需要在 url 地址上带参数名了,更加的简洁明了。
比如:当我们想设置name=kristina,id=300 就可以直接访问url为:http://localhost:8080/SpringMVC/user/reg/kristina/300而不需要添加?name=kristina&id=300
PathVariable 就可以根据请求的url路径获取相应的参数
应用实例
- 修改 UserHandler.java, 增加方法, 注意 @PathVariable(“username”) 不能少.
// 添加方法:
@RequestMapping(value = "reg/{username}/{userid}")
public String register(@PathVariable("username") String name, @PathVariable("userid") String id) {
System.out.println("接收到的参数 username=" + name + " userid=" + id);
return "success";
}
- 修改 前端页面 request.jsp, 增加代码
<hr><h1>占位符的演示</h1>
<a href="user/reg/kristina/300">占位符的演示</a>
注意事项和使用细节
- 映射的 URL, 不能重复
- 各种请求的简写形式
- 说明
@RequestMapping(value = "/buy",method = RequestMethod.POST) 等 价 @PostMapping(value = "/buy") 简写方式一览: @GetMapping @PostMapping @PutMapping @DeleteMapping
- 说明
postmapping表示以post方式发送表单。
2) 应用实例
// @RequestMapping(value = "/buy",method = RequestMethod.POST)
@PostMapping(value = "/buy")
public String buy() {
System.out.println("postmapping.. 购买商品");
return "success";
}
-
如果我们确定表单或者超链接 会提交某个字段数据比如(email), 要求提交的参数名和目标方法的参数名保持一致.(参数一致)
- 应 用 实 例 , 修 改D:\hspedu_ssm_temp\springmvc\src\com\hspedu\web\UserHandler.java , 增加方法
@GetMapping(value = "/hello3") public String hello3(String email) { System.out.println("hello3 " + email); return "success"; }
-
- 测试 输入 localhost:9998/user/hello3?email=tom@sohu.com, 一定要注入提交参数名和后台方法的形参名保持一致, 否则后端接收不到参数
- 测试 输入 localhost:9998/user/hello3?email=tom@sohu.com, 一定要注入提交参数名和后台方法的形参名保持一致, 否则后端接收不到参数
课后作业
编写一个表单, 以 Post 提交 Computer 信息, 后端编写 ComputerHandler, 可以接收到信息:
(本题用到了上面第 3 条的知识点:: 3. 如果我们确定表单或者超链接会提交某个字段数据比如(email), 要求提交的参数名和目标方法的参数名保持一致.)
- 编写表单:
<form action="computer/info" method="post">
品牌:<input type="text" name="brand"><br/>
价格:<input type="text" name="price"><br/>
数量:<input type="text" name="sum"><br/>
<input type="submit" value="提交">
</form>
- 编写Servlet:
/**
* @Author: GQLiu
* @DATE: 2024/2/16 16:53
*/
@RequestMapping("/computer") // 大的是computer
@Controller
public class ComputerHandler {
@PostMapping(value = "/info") // 小的是info, 所以上面的表单提交的地方是computer/info, 前面会自动补全ip+port+工程路径
public String buyComputer(String brand, String price, String sum) {
System.out.println("接收到的参数 brand=" + brand + " price=" + price + " sum=" + sum);
return "success";
}
}
测试成功:
小结:只需要让表单提交的参数名和后台写的形参名保持一致即可实现该数值的传递。
Postman(接口测试工具)
用于测试发送http请求
Rest-优雅的 url 请求风格
Rest-基本介绍
● 说明
- REST:即 Representational State Transfer。(资源)表现层状态转化。是目前流行的请求方式。它结构清晰, 很多网站采用
- HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
传统的请求方法:
getBook?id=1 GET
delete?id=1 GET
update POST
add POST
传统的 url 是通过参数来说明 crud 的类型,rest 是通过 get/post/put/delete 来说
明 crud 的类
● REST 的核心过滤器
- 当前的浏览器只支持 post/get 请求,因此为了得到 put/delete 的请求方式需要使用 Spring提供的 HiddenHttpMethodFilter 过滤器进行转换.
- HiddenHttpMethodFilter:浏览器 form 表单只支持 GET 与 POST 请求,而 DELETE、PUT等 method 并不支持,Spring 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求
- HiddenHttpMethodFilter 只能对 post 请求方式进行转换
- 这个过滤器需要在 web.xml 中配置
需求 Rest 风格的 url-完成增删改查
Rest 应用案例-代码实现
- 修改 web.xml 添加 HiddenHttpMethodFilter
<!--配置HiddenHttpMethodFilter
1. 作用是将post方式提交的delete请求和put请求进行转换
2. 配置url-patter 为/* 是所有请求都经过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>
- 修改 springDispatcherServlet-servlet.xml
<!--加入两个常规配置
可以支撑SpringMVC的高级功能,比如JSR303校验,映射动态请求等-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--将SpringMVC不能处理的请求,交给tomcat处理-->
<mvc:default-servlet-handler/>
- 创建 D:\idea_java_projects\springmvc\web\rest.jsp, 注意需要引入 jquery, 测试的时候查询/添加/删除/修改一个一个的来
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/2/24
Time: 10:52
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>rest </title>
<script type="text/javascript" src="script/jquery-3.7.1.min.js"></script>
<script type="text/javascript">
$(function () { // $ 不识别?????
$("#deleteBook").click(function () {
alert("ok");
var href = this.href;
$("#hiddenForm").attr("action", href);
$(":hidden").val("DELETE"); // 赋值这里: <input type="hidden" name="_method" value="PUT">
$("#hiddenForm").submit();//这里就是提交删除请求了
//这里必须返回 false,否则会提交两次
return false;
})
})
</script>
</head>
<body>
<h3>Rest 风格的 crud 操作案例</h3>
<br>
<hr>
<h3>rest 风格的 url 查询书籍[get]</h3>
<a href="user/query/100">点击查询书籍</a>
<br>
<hr>
<h3>rest 风格的 url 添加书籍[post]</h3>
<form action="user/book" method="post">
name:<input name="bookName" type="text"><br>
<input type="submit" value="添加书籍">
</form>
<br>
<hr>
<h3>rest 风格的 url, 删除一本书</h3>
<!-- 1. 这里我们需要将删除方式(get)转成 delete 的方式,需要使用过滤器和 jquery 来完成
2. name="_method" 名字需要写成 _method 因为后台的 HiddenHttpMethodFilter
就是按这个名字来获取 hidden 域的值,从而进行请求转换的. -->
<a href="user/book/1656145" id="deleteBook">删除指定 id 的书</a>
<form action="" method="post" id="hiddenForm">
<input type="hidden" name="_method"/>
</form>
<br>
<hr>
<h3>rest 风格的 url 修改书籍[put]~</h3>
<form action="user/book/100" method="post">
<input type="hidden" name="_method" value="PUT"> <!--隐藏域-->
<input type="submit" value="修改书籍~">
</form>
</body>
</html>
- 创建 D:\idea_java_projects\springmvc\src\com\hspedu\web\rest\BookHandler.java , 处理 rest 风格的请求
package com.web.rest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @Author: GQLiu
* @DATE: 2024/2/24 10:57
*/
@RequestMapping("/user")
@Controller
public class BookHandler {
// 查询[GET]
@RequestMapping(value = "/query/{id}", method = RequestMethod.GET)
public String getBook(@PathVariable("id") String id) {
System.out.println("查询书籍 id=" + id);
return "success";
}
// 添加[POST]
@PostMapping(value = "/book")
public String addBook(String bookName) {
System.out.println("添加书籍 bookName== " + bookName);
return "success";
}
// 删除[DELETE]
@RequestMapping(value = "/book/{id}", method = RequestMethod.DELETE)
public String delBook(@PathVariable("id") String id) {
System.out.println("删除书籍 id= " + id);
// return "success"; //[如果这样返回会报错 JSPs only permit GET POST or HEAD]
// 老师解读
// 1. redirect:/user/success重定向
// 2. 会被解析成 /springmvc/user/success
return "redirect:/user/success";
}
// 如果请求是 /user/success , 就转发到 success.jsp
// successGenecal对应的url http://ip:port/springmvc/user/success
@RequestMapping(value = "/success")
public String successGeneral() {
return "success"; // 由该方法 转发到success.jsp页面
}
// 修改[PUT]
@PutMapping(value = "/book/{id}")
public String updateBook(@PathVariable("id") String id) {
System.out.println("修改书籍 id=" + id);
// redirect:/user/success 会被解析为 工程路径/user/success
return "redirect:/user/success";
}
}
- 结果:
结果一:
结果二:
有乱码
结果三:
结果四:
注意事项和细节说明
1、HiddenHttpMethodFilter,在将 post 转成 delete / put 请求时,是按_method 参数名 来读取的
2、如果 web 项目是运行在 Tomcat 8 及以上,会发现被过滤成 DELETE 和 PUT 请求,到达控制器时能顺利执行,但是返回时(forward)会报 HTTP 405 的错误提示:消息 JSP 只允许 GET、POST 或 HEAD。
-
- 解决方式 1: 使用 Tomcat7
-
- 解决方式 2: 将请求转发(forward)改为请求重定向(redirect):重定向到一个 Handler,由 Handler 转发到页面
- 解决方式 2: 将请求转发(forward)改为请求重定向(redirect):重定向到一个 Handler,由 Handler 转发到页面
SpringMVC 映射请求数据
传来的参数的参数名和函数形参中的形参名不一致,如何映射过去?
也是需要这两个参数名都已知的。
应用实例
- 创 建D:\idea_java_projects\springmvc\src\com\hspedu\web\requestparam\VoteHandler.java
package com.web.requestparam;
import com.web.requestparam.entity.Master;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @Author: GQLiu
* @DATE: 2024/2/25 15:05
*/
@Controller
@RequestMapping("/vote")
public class VoteHandler {
//获取到超链接传递的数据
/**
*
* @RequestParam(value = "name", required = false)
* 使用RequestParam可以让提交的参数名和函数中的参数名不一致,但是还是要知道是什么参数名的
* value=name 表示接收的参数名是name
* required=false表示该参数并不必须,可有可无。如果设为true表示必须传递该参数。
* @return
*/
@RequestMapping(value = "/vote01")
public String test01(@RequestParam(value = "name", required = false) String username) {
System.out.println("得到的username=" + username);
return "success";
}
}
- 创建 D:\idea_java_projects\springmvc\web\request_parameter.jsp
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/2/25
Time: 15:08
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试 request parameter</title>
</head>
<body>
<h2>获取到超链接参数值</h2>
<hr/>
<a href="vote/vote01?name=hspedu">获取超链接的参数</a>
<hr><hr>
<hr/>
</html>
访问http://localhost:8080/SpringMVC/request_parameter.jsp
,即可获取username=hspedu
获取 http 请求消息头
- 开发中,如何获取到 http 请求的消息头信息
- 使用较少
- 修改 VoteHandler.java, 增加方法
@RequestMapping("/vote02")
public String test02(@RequestHeader("Accept-Encoding") String ae,
@RequestHeader("Host") String Host){
System.out.println("Accept-Encoding=" + ae);
System.out.println("Host=" + Host);
return "success";
}
- 修改 request_parameter.jsp, 增加代码
<hr>
<h1>获取到消息头</h1>
<hr>
<a href="vote/vote02">获取http消息头消息</a>
测试成功:
获取 javabean 形式的数据
● 开发中,如何获取到 javaben 的数据,就是以前的 entity/pojo 对象数据
- 创 建D:\idea_java_projects\springmvc\src\com\hspedu\web\requestparam\entity\Pet.java
这里注意,由于用到反射机制,所以必须加上无参构造器!!!
public class Pet {
private Integer id;
private String name;
}
- 创 建D:\idea_java_projects\springmvc\src\com\hspedu\web\requestparam\entity\Master.java
这里注意,由于用到反射机制,所以必须加上无参构造器!!!
public class Master {
private Integer id;
private String name;
private Pet pet; // 级联属性
}
- 修改 VoteHandler.java, 增加方法
/**
*
* @param master 生成的主人对象
* 1. 方法的形参用对应的类型来指定即可, SpringMVC会自动的进行封装
* 2. 如果自动的完成封装, 要求提交的数据,参数名和对象的字段名保持一致
* 3. 如果属性是对象,这里就是仍然是通过 字段名.字段名 比如Master [pet]
* , 即提交的数据 参数名 是 pet.id pet.name, 这就是级联操作
* 4. 如果提交的数据 的参数名和对象的字段名不匹配,则对象的属性值就是null
* 5. 小伙伴疑惑,怎么就封装成功[底层仍然是反射+注解..
* @return
*/
@RequestMapping("/vote03")
public String test03(Master master) {
System.out.println("封装后的master=" + master);
return "success";
}
- 修改 前端 request_parameter.jsp, 增加演示代码
<hr>
<form action="vote/vote03" method="post">
主人id:<input type="text" name="id"><br/>
主人名:<input type="text" name="name"><br/>
宠物id:<input type="text" name="pet.id"><br/>
宠物名:<input type="text" name="pet.name"><br/>
<input type="submit" value="提交">
</form>
<hr>
后端拿到Bean:
注意事项
- 支持级联数据获取
- 表单的控件名称 name 需要和 javabean 对象字段对应, 否则就是 null
获取 servlet api
● 说明
- 开发中, 我们可能需要使用到原生的 servlet api ,看看如何获取
- 使用 servlet api , 需要引入 tomcat/lib 下的 servlet-api.jar
- 修改 VoteHandler.java, 增加方法
/**
* 使用servlet api, 来获取提交的数据
*
* @return
*/
@RequestMapping("/vote04")
public String test04(HttpServletRequest request, HttpServletResponse response,
HttpSession hs) {
// 获取session
HttpSession session = request.getSession();
System.out.println("session=" + session);
System.out.println("HttpSession=" + hs);
// 上面两种方式得到的session都是同一个。
// 获取参数
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
System.out.println("用户名=" + username + " 密码=" + pwd);
return "success";
}
- 修改 request_parameter.jsp,增加如下代码:
<form action="vote/vote04" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="pwd"><br/>
<input type="submit" value="提交用户">
</form>
后端拿到数据:
使用注意事项
- 除了 HttpServletRequest, HttpServletResponse ,其它对象也可以这样的形式获取 HttpSession、java.security.Principal,InputStream,OutputStream,Reader,Writer
- 其中一些对象也可以通过 HttpServletRequest / HttpServletResponse 对象获取,比如Session 对象 ,既可以通过参数传入,也以通过 request.getSession() 获取,效果一样,推荐使用参数形式传入,更加简单明了
模型数据
模型数据处理-数据放入 request
方式 1: 通过 HttpServletRequest 放入 request 域
- 创建 D:\idea_java_projects\springmvc\web\model_data.jsp
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/2/25
Time: 17:52
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试 模型数据</title>
</head>
<body>
<h1>添加主人信息</h1>
<!-- 老韩解读
1. 这里的字段名称和对象的属性名保持一致,级联添加属性也是一样保持名字对应关系
2. 如果只是添加主人信息,则去掉宠物号和宠物名输入框即可 ,pet 为 null-->
<form action="vote/vote05" method="post">
主人号:<input type="text" name="id"><br>
主人名:<input type="text" name="name"><br>
宠物号:<input type="text" name="pet.id"><br>
宠物名:<input type="text" name="pet.name"><br>
<input type="submit" value="添加主人和宠物">
</form>
</body>
</html>
- 修改 VoteHandler.java, 增加方法
@RequestMapping("/vote05")
public String test05(Master master) {
System.out.println("提交主人信息成功");
return "vote_ok";
}
- 创建 D:\idea_java_projects\springmvc\web\WEB-INF\pages\vote_ok.jsp, 显示数据
使用el表达式
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/2/25
Time: 17:53
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>vote_ok </title>
</head>
<body>
<h1>获取的的数据显示页面</h1>
<hr>
取出 request 域的数据
<br>
address: ${address}<br>
主人名字= ${requestScope.master.name}
主人信息= ${requestScope.master}
宠物名字= ${requestScope.master.pet.name}
</body>
</html>
结果:
方式 2: 通过请求的方法参数 Map<String,Object> 放入 request 域
- 修改 model_data.jsp, 增加代码
<br/><hr/>
<h1>添加主人信息[测试 Map ]</h1>
<form action="vote/vote06" method="post">
主人号:<input type="text" name="id"><br>
主人名:<input type="text" name="name"><br>
宠物号:<input type="text" name="pet.id"><br>
宠物名:<input type="text" name="pet.name"><br>
<input type="submit" value="添加主人和宠物">
</form>
-
修改 VoteHandler.java , 增加方法
-
结果
// 放入map中
@RequestMapping("/vote06")
public String test06(Map<String,Object> map, Master master) {
map.put("master", master);
map.put("address","beijing");
return "vote_ok";
}
方式 3: 通过返回 ModelAndView 对象 实现 request 域数据
- 修改 model_data.jsp, 增加代码
<br/><hr/>
<h1>添加主人信息[测试 ModelAndView]</h1>
<form action="vote/vote07" method="post">
主人号:<input type="text" name="id"><br>
主人名:<input type="text" name="name"><br>
宠物号:<input type="text" name="pet.id"><br>
宠物名:<input type="text" name="pet.name"><br>
<input type="submit" value="添加主人和宠物">
</form>
- 修改 VoteHandler.java , 增加方法
// 将model放入到ModelAndView对象中,实现将数据放入request域中
@RequestMapping("/vote07")
public ModelAndView test07(Master master) {
// 创建一个ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
// 下面这句话就等价于将master对象放入request域中,属性名master
modelAndView.addObject("master", master);
modelAndView.addObject("address","北京");
modelAndView.setViewName("vote_ok");
return modelAndView;
}
使用注意事项
- 从本质看,请求响应的方法 return “xx”, 是返回了一个字符串,其实本质是返回了一个ModelAndView 对象,只是默认被封装起来的。
- ModelAndView 即可以包含 model 数据,也可以包含视图信息。
- ModelAndView 对象的 addObject 方法可以添加 key-val 数据,默认在 request 域中。
- ModelAndView 对象 setView 方法可以指定视图名称。
模型数据处理-数据放入 session
● 说明
开发中, 控制器/处理器中获取的数据如何放入 session 域,然后在前端(VUE/JSP/…)取出显示
- 修改 model_data.jsp, 增加代码
<h1>添加主人信息[测试 session]</h1>
<form action="vote/vote08" method="post">
主人号:<input type="text" name="id"><br>
主人名:<input type="text" name="name"><br>
宠物号:<input type="text" name="pet.id"><br>
宠物名:<input type="text" name="pet.name"><br>
<input type="submit" value="添加主人和宠物">
</form>
- 修改 VoteHandler.java, 增加方法
/**
* 将model(master对象)放入session域中
* @param map
* @param master
* @param session
* @return
*/
@RequestMapping(value = "/vote08")
public String test08(Map<String, Object> map, Master master,
HttpSession session) {
map.put("address", "guangzhou");
// 在session中也放入master对象
session.setAttribute("master2", master);
return "vote_ok";
}
- 修改 vote_ok.jsp , 增加代码
<hr>
取出 session 域的数据 <br>
主人名字= ${sessionScope.master2.name }
主人信息= ${sessionScope.master2 }
@ModelAttribute 实现 prepare 方法
开发中,有时需要使用某个前置方法(比如 prepareXxx(), 方法名由程序员定)给目标方法准备一个模型对象。
@ModelAttribute 注解可以实现 这样的需求在某个方法上,增加了@ModelAttribute 注解后那么在调用该 Handler 的任何一个方法时,都会先调用这个方法
修改 VoteHandler.java, 增加方法 并测试
/**
* 老韩解读
* 1. 当在某个方法上,增加了@ModelAttribute 注解
* 2. 那么在调用该 Handler 的任何一个方法时,都会先调用这个方法
*/
@ModelAttribute
public void prepareModel() {
System.out.println("prepareModel()----完成准备工作");
}
7视图和视图解析器
自定义视图
为什么需要自定义视图
- 在默认情况下,我们都是返回默认的视图, 然后这个返回的视图交由 SpringMVC 的InternalResourceViewResolver 视图处理器来处理的
- 在实际开发中,我们有时需要自定义视图,这样可以满足更多更复杂的需求
自定义视图实例-代码实现
- 配置 springDispatcherServlet-servlet.xml , 增加自定义视图解析器
<!--增加自定义视图解析器-->
<!--
1. BeanNameViewResolver这个就是可以解析自定义视图的解析器
2. name="order"表示给这个解析器设置的优先级,默认优先级很低。
3. 一般来说, 我们自己的视图解析优先级高,order值小。
-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="1"></property>
</bean>
- 创建 D:\idea_java_projects\springmvc\src\com\hspedu\web\viewresolver\MyView.java - 自定义视图类
// Component 表示该视图会被加载到容器中,id为myView
@Component(value = "lgqView")
public class MyView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
System.out.println("进入到我们自己的视图");
// 这时我们自己来确定到哪个页面去,默认的视图解析器机制就无效。
httpServletRequest.getRequestDispatcher("/WEB-INF/pages/my_view.jsp").forward(httpServletRequest, httpServletResponse);
}
}
- 创 建D:\idea_java_projects\springmvc\src\com\hspedu\web\viewresolver\GoodsHandler.java
@RequestMapping("/goods")
@Controller
public class GoodsHandler {
@RequestMapping(value = "/buy")
public String buy() {
System.out.println("buy========");
return "lgqView";
}
@RequestMapping(value = "/order")
public String order() {
System.out.println("========order===========");
// 这里直接指定转发到哪个页面
// 是重定向, 重定向不能重定向到/web-INF目录中.
return "redirect://login.jsp";
}
}
- 创建 D:\idea_java_projects\springmvc\web\view.jsp 和 /WEB-INF/pages/my_view.jsp
<%--
Created by IntelliJ IDEA.
User: 123
Date: 2024/2/28
Time: 10:08
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>view</title>
</head>
<body>
<h2> 测试自定义视图 </h2>
<a href="goods/buy" >测试自定义视图</a><br/>
<a href="goods/order">测试目标方法直接指定 重定向&请求转发</a><br/>
</body>
</html>
<%--
Created by IntelliJ IDEA.
User: 123
Date: 2024/2/28
Time: 10:09
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>my_view</title>
</head>
<body>
<h1> 从自己的视图过来的 </h1>
wekcime!
</body>
</html>
自定义视图工作流程小结
- 自定义视图: 创建一个 View 的 bean, 该 bean 需要继承自 AbstractView, 并实现renderMergedOutputModel 方法.
- 并把自定义 View 加入到 IOC 容器中
- 自定义视图的视图处理器,使用 BeanNameViewResolver, 这个视图处理器也需要配置到 ioc 容器
- BeanNameViewResolver 的调用优先级需要设置一下,设置 order 比 Integer.MAX_VAL小的值. 以确保其在 InternalResourceViewResolver 之前被调用
● 自定义视图-工作流程
- SpringMVC 调用目标方法, 返回自定义 View 在 IOC 容器中的 id
- SpringMVC 调用 BeanNameViewResolver 解析视图: 从 IOC 容器中获取 返回 id 值对应的 bean, 即自定义的 View 的对象
- SpringMVC 调用自定义视图的 renderMergedOutputModel 方法渲染视图
- 老韩说明: 如果在 SpringMVC 调用目标方法, 返回自定义 View 在 IOC 容器中的 id,不存在, 则仍然按照默认的视图处理器机制处理.
目标方法直接指定转发或重定向
● 目标方法中指定转发或者重定向
- 默认返回的方式是请求转发,然后用视图处理器进行处理,比如在目标方法中这样写:
- 也可以在目标方法直接指定重定向或转发的 url 地址
- 如果指定重定向,不能定向到 /WEB-INF 目录中
代码
- 修改 GoodsHandler.java, 增加方法 order()
@RequestMapping(value = "/order")
public String order() {
System.out.println("========order===========");
// 这里直接指定转发到哪个页面
// 是重定向, 重定向不能重定向到/web-INF目录中.
return "redirect://login.jsp";
}
- 修改 view.jsp,
<a href="goods/order">测试目标方法直接指定 重定向&请求转发</a><br/>
- 测试
作业
- 编写登录login.jsp页面
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/2/16
Time: 14:02
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<h3>登录页面</h3>
<form action="login/log">
u:<input name="username" type="text"> <br/>
p:<input name="password" type="password"><br/>
<input type="submit" value="登录">
</form>
</body>
</html>
- 编写SpringMVC/web/WEB-INF/pages/login_ok.jsp和SpringMVC/web/WEB-INF/pages/login_error.jsp
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/2/16
Time: 14:08
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录成功</title>
</head>
<body>
<h1>恭喜 登录成功!</h1>
</body>
</html>
<%--
Created by IntelliJ IDEA.
User: 123
Date: 2024/2/28
Time: 15:00
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登陆失败</title>
</head>
<body>
<h1>很遗憾,您的登录失败</h1>
<a href="/SpringMVC/login.jsp">点击重新登录</a>
</body>
</html>
- 编写LoginHandler
@RequestMapping(value = "/login")
@Controller
public class LoginHandler {
@RequestMapping("/log")
public String doLogin(User user) {
System.out.println("接受到的user=" + user);
if("Tom".equals(user.getUsername()) && "123".equals(user.getPassword()))
return "login_ok";
return "login_error";
}
}
- 编写User.java 的JavaBean. 不写了.
- 完成测试.OK/
9数据格式化
基本数据类型和字符串自动转换
● 说明: 基本数据类型可以和字符串之间自动完成转换
代码实现
- 创 建 JavaBean:
D:\idea_java_projects\springmvc\src\com\hspedu\web\datavalid\Monster.java
public class Monster {
private Integer id;
private String email;
private Integer age;
private String name;
}
- 创建 D:\idea_java_projects\springmvc\web\data_valid.jsp
<%--
Created by IntelliJ IDEA.
User: 123
Date: 2024/2/28
Time: 15:29
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SpringMVC[数据格式/验证]</title>
</head>
<body>
<h1>SpringMVC[数据格式/验证]</h1>
<hr/>
<a href="addMonsterUI">添加妖怪</a>
</body>
</html>
- 创 建
D:\idea_java_projects\springmvc\src\com\hspedu\web\datavalid\MonsterHandler.java
这里和其他上面不同的地方在于map.put("monster", new Monster());
,放入了这个对象到request域中。但是不写也能获得。
package com.web.datavalid;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
/**
* 该Handler用户响应用户提交的数据。
*/
@Controller
@Scope(value = "prototype") // Spring注解, 每次都创建全新的Bean实例,多例的.
public class MonsterHandler {
@RequestMapping(value = "/addMonsterUI")
public String addMonsterUI(Map<String, Object> map) {
// 向 Map<String, Object> map 中存放数据时,会默认存放到request域中。
map.put("monster", new Monster());
return "datavalid/monster_addUI";
}
// 编写方法, 处理添加妖怪任务
/**
*
* @param monster 在springmvc中,可以将提交的数据按照参数名和对象的属性名匹配,直接封装到对象中
* @return
*/
@RequestMapping(value = "/save")
public String save(Monster monster) {
System.out.println("Monster=" + monster);
return "datavalid/success";
}
}
- 创 建
D:\idea_java_projects\springmvc\web\WEB-INF\pages\datavalid\monster_addUI.jsp
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>添加妖怪~~</h3>
<!-- 这里的表单,我们使用 springMVC 的标签来完成
特别说明几点:
1. SpringMVC 表单标签在显示之前必须在 request 中有一个 bean, 该 bean 的属性
和表单标签的字段要对应!
request 中的 key 为 form 标签的 modelAttrite 属性值, 比如这里的 monsters
2. SpringMVC 的 form:form 标签的 action 属性值中的 / 不代表 WEB 应用的根目
录.
3.加上一个form:就是表示使用的springmvc标签,方便错误信息回显
-->
<form:form action="save" method="POST" modelAttribute="monster">
妖怪名字: <form:input path="name"/> <br><br>
妖怪年龄~: <form:input path="age"/> <br><br>
电子邮件: <form:input path="email"/> <br><br>
<input type="submit" value="添加妖怪"/>
</form:form>
</body>
</html>
-
创建 D:\idea_java_projects\springmvc\web\WEB-INF\pages\datavalid\success.jsp
不写了。 -
测试 , 浏览器: http://localhost:8080/springmvc/data_valid.jsp
特殊数据类型和字符串间的转换
- 特殊数据类型和字符串之间的转换使用注解(比如日期,规定格式的小数比如货币形式等)
- 对于日期和货币可以使用 @DateTimeFormat 和 @NumberFormat 注解. 把这两个注
解标记在字段上即可.
- 修改 Monster.java , 增加 birthday 和 salary 字段
- 修改 monster_addUI, 增加 birthday 和 salary 字段
- 测试
同上。
10验证以及国际化
- 对输入的数据(比如表单数据),进行必要的验证,并给出相应的提示信息。
- 对于验证表单数据,springMVC 提供了很多实用的注解, 这些注解由 JSR 303 验证框架提供.
● JSR 303 验证框架
- JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 中
- JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证
- JSR 303 提供的基本验证注解有:
● Hibernate Validator 扩展注解
- Hibernate Validator 和 Hibernate 没有关系,只是 JSR 303 实现的一个扩展. 2. Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:
- 扩展注解有如下
应用实例
-
引入验证和国际化相关的 jar 包
使用Maven的话,直接引入相关依赖即可。
-
修改 Monster.java
-
修改 MonsterHandler.java
// 编写方法, 处理添加妖怪任务
/**
* @param monster 在springmvc中,可以将提交的数据按照参数名和对象的属性名匹配,直接封装到对象中
* @param errors 表示如果校验出现错误, 将校验的错误信息保存到errors中
* @param map 表示如果校验出现错误, 将校验的错误信息保存map 同时保存monster对象。
* @Validated 表示进行校验
* 校验发生的动机: 在springmvc底层, 反射调用目标方法时,会接受到http请求的数据,然后根据注解来进行验证。
* 如果出现了错误,就把错误信息填充到errors和map中。
* @return
*/
@RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(@Validated Monster monster, Errors errors, Map<String, Object> map) {
System.out.println("Monster=" + monster);
if(errors.hasErrors()) {
System.out.println("验证出错");
for (ObjectError error: errors.getAllErrors()) {
System.out.println(error);
}
// 有错误返回初始界面。
return "datavalid/monster_addUI";
}
return "datavalid/success";
}
- 配置国际化文件 修改springDispatcherServlet-servlet.xml
<!-- 配置国际化错误信息的资源处理 bean -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- 配置国际化文件名字
如果你这样配的话,表示 messageSource 回到 src/i18nXXX.properties 去读取错误
信息
-->
<property name="basename" value="i18n"></property>
</bean>
- 创建国际化文件 D:\idea_java_projects\springmvc\src\i18n.properties 里面存放的就是错误后的回显信息。
NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a
typeMismatch.monster.age=\u5e74\u9f84\u8981\u6c42\u5728\u0031\u002d\u0031\u0035\u0030\u4e4b\u95f4
typeMismatch.monster.birthday=\u751f\u65e5\u683c\u5f0f\u4e0d\u6b63\u786e
typeMismatch.monster.salary=\u85aa\u6c34\u683c\u5f0f\u4e0d\u6b63\u786e
- 修改 monster_addUI.jsp , 回显错误信息
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>添加妖怪~~</h3>
<!-- 这里的表单,我们使用 springMVC 的标签来完成
特别说明几点:
1. SpringMVC 表单标签在显示之前必须在 request 中有一个 bean, 该 bean 的属性
和表单标签的字段要对应!
request 中的 key 为 form 标签的 modelAttrite 属性值, 比如这里的 monsters
2. SpringMVC 的 form:form 标签的 action 属性值中的 / 不代表 WEB 应用的根目
录.
3.加上一个form:就是表示使用的springmvc标签,方便错误信息回显
4. <form:errors path="name"/> 表示回显错误信息
-->
<form:form action="save" method="POST" modelAttribute="monster">
妖怪名字: <form:input path="name"/> <form:errors path="name"/> <br><br>
妖怪年龄~: <form:input path="age"/> <form:errors path="age"/><br><br>
电子邮件: <form:input path="email"/> <form:errors path="email"/><br><br>
妖怪生日:<form:input path="birthday"/> <form:errors path="birthday"/><br><br>
妖怪工资:<form:input path="salary"/> <form:errors path="salary"/><br><br>
<input type="submit" value="添加妖怪"/>
</form:form>
</body>
</html>
细节说明和注意事项
- 在需要验证的 Javabean/POJO 的字段上加上相应的验证注解.
- 目标方法上,在 JavaBean/POJO 类型的参数前, 添加 @Validated 注解. 告知 SpringMVC该 bean 是需要验证的
- 在 @Validated 注解之后, 添加一个 Errors 或 BindingResult 类型的参数, 可以获取到验证的错误信息
- 需要使用 <form:errors path=“email”></form:errors> 标签来显示错误消息, 这个标签,需要写在< form:form > 标签内生效.
- 错误消息的国际化文件 i18n.properties , 中文需要是 Unicode 编码,使用工具转码.
√格式: 验证规则.表单 modelAttribute 值.属性名=消息信息
- 注解@NotNull 和 @NotEmpty 的区别说明
-
- 查看源码可以知道 : @NotEmpty Asserts that the annotated string, collection, map or array is not {@code null} or empty.
-
- 查看源码可以知道 : @NotNull * The annotated element must not be {@code null}.* Accepts any type.
-
- 老韩解读:如果是字符串验证空, 建议使用 @NotEmpty
- SpingMVC 验证时,会根据不同的验证错误, 返回对应的信息
注解的结合使用
原理解析——数据类型转换校验核心类-DataBinder
● DataBinder 工作机制-了解
图例 Spring MVC 通过反射机制对目标方法进行解析,将请求消息绑定到处理方法的入参中。
数据绑定的核心部件是 DataBinder,运行机制如下
取消某个属性的绑定
● 说明
在默认情况下,表单提交的数据都会和 pojo 类型的 javabean 属性绑定,如果程序员在开发中,不希望取消某个属性的绑定,也就是说,不希望接收到某个表单对应的属性的值而是使用默认值,则可以通过 @InitBinder 注解取消绑定.
- 步骤
-
编写一个方法, 使用@InitBinder 标识的该方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定。
-
@InitBinder 标识的方法不能有返回值,它必须声明为 void。
-
@InitBinder 标识的方法的参数通常是是 WebDataBinder
-
修改 MonsterHandler.java , 增加方法
// 取消绑定monster的name表单提交的值.
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
//1. setDisallowedFields() 是可变形参,可以指定多个字段
//2. 当将一个字段/属性,设置为 disallowed,就不在接收表单提交的值
//3. 那么这个字段/属性的值,就是该对象默认的值 (具体看程序员定义时指定)
dataBinder.setDisallowedFields("name");
}
11 中文乱码处理
当表单提交数据为中文时,会出现乱码,我们来解决一下
1 自定义中文乱码过滤器
- 创 建 过 滤 器
D:\idea_java_projects\springmvc\src\com\hspedu\web\filter\MyCharacterFilter.java
public class MyCharacterFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
System.out.println("经过过滤器了!!!");
servletResponse.setCharacterEncoding("utf-8");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
- 配置 web.xml , 将该过滤器配置在最前面一定注意!要把这个过滤器的配置放在web.xml文件的最前面!不然不会经过改过滤器!!!在web.xml中的执行是按照先后顺序执行的。
<!--配置Web.xml, 将该过滤器配置在最前面-->
<!--配置中文乱码处理过滤器-->
<filter>
<filter-name>myCharacterFilter</filter-name>
<filter-class>com.web.filter.MyCharacterFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myCharacterFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 完成测试
2 Spring 提供的过滤器处理中文
修改web.xml这个文件仅仅。使用Spring自己提供的过滤器。
<!--配置中文乱码处理过滤器-->
<!--<filter>-->
<!-- <filter-name>myCharacterFilter</filter-name>-->
<!-- <filter-class>com.web.filter.MyCharacterFilter</filter-class>-->
<!--</filter>-->
<!--<filter-mapping>-->
<!-- <filter-name>myCharacterFilter</filter-name>-->
<!-- <url-pattern>/*</url-pattern>-->
<!--</filter-mapping>-->
<!--注释上面的,配置Spring提供的过滤器,解决中文乱码问题-->
<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>
同样可以处理中文乱码问题。
12 处理 json 和 HttpMessageConverter
项目开发中,我们往往需要服务器返回的数据格式是按照 json 来返回的, 我们看一下
SpringMVC 是如何处理的
@ResponseBody 与 @RequestBody
- 引入处理 json 需要的 jar 包,注意 spring5.x 需要使用 jackson-2.9.x.jar 的包
- 创建 D:\idea_java_projects\springmvc\web\json.jsp
<%--
Created by IntelliJ IDEA.
User: GQLiu
Date: 2024/3/3
Time: 14:53
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>json 提交</title>
<%-- // 引入jquery代码和请求--%>
<script type="text/javascript" src="script/jquery-3.7.1.min.js"></script>
<script type="text/javascript">
$(function () {
//绑定超链接点击事件
$("#getJson").click(function () {
var href= this.href;
alert(href);
var args={"time":new Date()}; // 防止浏览器有缓存
// 发出一个jquery的post请求,请求返回json
$.post(
href, // post所需的url参数
args, // post所需的其他参数
function (data) {
// data是返回的json对象
alert("name=" + data.name + " address=" + data.address);
// JSON.stringify(data) 将json转为字符串类型
alert("返回数据json" + data);
alert("返回数据=" + JSON.stringify(data));
}, "json"
);
return false; //这里返回false就不用href的默认机制提交, 防止重复提交
}),
})
</script>
</head>
<body>
<h1>请求一个json数据</h1>
<a href="<%=request.getContextPath()%>/json/dog" id="getJson">点击获取json数据</a><br/>
<hr/>
<h1>发出一个json数据</h1>
u:<input id="userName" type="text"><br/>
a:<input id="age" type="text"><br/>
<button name="butt1">添加用户</button>
</body>
</html>
-
创建 JavaBean: D:\idea_java_projects\springmvc\src\com\hspedu\web\json\Dog.java , 作为返回的数据
-
创建 D:\idea_java_projects\springmvc\src\com\hspedu\web\json\JsonHandler.java
@ResponseBody 将后端写入的对象可以以以json字符串形式返回给前端。
package com.web.json;
import com.web.homework.entity.User;
import com.web.json.entity.Dog;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Author: GQLiu
* @DATE: 2024/3/3 14:57
*/
@Controller
public class JsonHandler {
@RequestMapping(value = "/json/dog")
@ResponseBody
public Dog getDog() {
Dog dog = new Dog();
dog.setName("大黄狗");
dog.setAddress("小新家");
return dog;
}
}
测试结果:
处理 JSON-@RequestBody
- 前面老韩给大家讲解的是通过表单, 或者 url 请求携带 参数名=参数值 把数据提交给目
标方法
- 给大家举例客户端发送 json 字符串数据,
- 使用 SpringMVC 的 @RequestBody 将客户端提交的 json 数据,封装成 JavaBean 对象
- 再把这个 javabean 以 json 对象形式返回
- 修改 json.jsp, 增加发送 json 数据代码
// 绑定按钮点击事件
// 目的: 将userName和age封装成json字符串,发送给目标方法.
// 绑定按钮点击事件
$("button[name='butt1']").click(function () {
var url = "/SpringMVC/json/save2"
var userName = $("#userName").val()
var age = $("#age").val()
console.log("userName=" + userName + " age=" + age)
var args = JSON.stringify({"userName": userName, "age": age})
// 发送ajax请求
$.ajax({
url: url,
data: args,
type: "POST",
success: function (data) {
console.log("返回的data=", JSON.stringify(data));
},
contentType: "application/json;charset=utf-8"
});
})
- 修改 JsonHandler.java , 增加处理 json 代码, 注意:老韩用的是@PostMapping , 等
价:@RequestMapping(method = RequestMethod.POST)
@RequestBody 可以将客户端提交的 json 数据,封装成 JavaBean 对象
@RequestMapping(value = "/json/save2")
@ResponseBody
public User save2(@RequestBody User user) {
System.out.println("user=" + user);
return user;
}
- 增加 JavaBean : User.java
- 并完成测试(页面方式)
不知道为什么返回是null, 后台接收也是null
终于知道自己是那里错的了。
引用的User类,自己引用错了,IDEA自动引用到别的包下的User类了。
哭了。。。
返回一个集合,里面有多个json对象
- 编写方法
@RequestMapping(value = "/json/dogs")
@ResponseBody
public List<Dog> getDogs() {
ArrayList<Dog> dogs = new ArrayList<>();
dogs.add(new Dog("大黄狗", "1"));
dogs.add(new Dog("大黄狗2", "2"));
dogs.add(new Dog("大黄狗3", "3"));
return dogs;
}
- 测试成功
@ResponseBody 可以直接写在 controller(也就是Handler) 上,这样对所有方法生效
HttpMessageConverter
● 基本说明
SpringMVC 处理 JSON-底层实现是依靠 HttpMessageConverter来进行转换的
● 处理 JSON-底层实现(HttpMessageConverter)
- 使用 HttpMessageConverter 将请求信息转化并绑定到处理方法的入参中, 或将响应结果转为对应类型的响应信息,Spring 提供了两种途径:
- √ 使用 @RequestBody / @ResponseBody 对目标方法进行标注
- √ 使用 HttpEntity / ResponseEntity 作为目标方法的入参或返回值
- 当 控 制 器 处 理 方 法 使 用 到 @RequestBody/@ResponseBody 或HttpEntity/ResponseEntity 时, Spring 首先根据请求头或响应头的 Accept 属性选择匹配 的 HttpMessageConverter, 进而 根据参 数类型 或泛型 类型的 过滤得 到匹配 的HttpMessageConverter, 若找不到可用的 HttpMessageConverter 将报错
文件下载-ResponseEntity
- 修改 json.jsp, 添加如下代码:
<hr/>
<h1>下载文件的测试 </h1>
<a href="downloadFile">点击下载文件</a>
- 修改 JsonHandler.java, 增加方法
就是用个Response
@RequestMapping(value = "/downloadFile")
@ResponseBody
public ResponseEntity<byte[]> downFile(HttpSession session) throws IOException {
// 先获取到你要下载文件的inputstream去读取文件。
// session.getServletContext().getRealPath()是获取文件的真实路径
// C:\JavaCode\SpringMVC\out\artifacts\SpringMVC_war_exploded\img\bankCard.jpg
System.out.println(session.getServletContext().getRealPath("/img/bankCard.jpg"));
InputStream resourceAsStream = session.getServletContext().getResourceAsStream("/img/bankCard.jpg"); // 这次会话的servletcontext(可以访问共享资源) 里面的资源bankCard.jpg
//FileInputStream resourceAsStream = new FileInputStream(new File("http://localhost:8080/SpringMVC/img/bankCard.jpg")); // 真实的路径并不是这个.
// 开辟一个存放文件内容的byte数组,因为byte是字节,因此可以返回二进制文件。
// resourceAsStream.available()返回文件的字节长度。
byte[] bytes = new byte[resourceAsStream.available()];
// 读取
resourceAsStream.read(bytes);
// 构建返回的 ResponseEntity<byte[]>
HttpStatus httpStatus = HttpStatus.OK;
// 构建http头. 这个就是用来告诉浏览器, 这是返回的文件, 你浏览器就弹出小窗口下载就行了.
HttpHeaders httpHeaders = new HttpHeaders();
// content-type 指示响应内容的格式 content-disposition 指示如何处理响应内容。
httpHeaders.add("Content-DIsposition", "attacgment;filename=bankCard.jpg"); // 这里的 filename=bankCard.jpg 就是实际下载的文件名.
ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, httpHeaders, httpStatus);
return responseEntity;
}
文件下载响应头的设置
content-type 指示响应内容的格式
content-disposition 指示如何处理响应内容。
一般有两种方式:
inline:直接在页面显示
attchment:以附件形式下载
SpringMVC 文件上传
● 基本介绍
- Spring MVC 为 文 件 上 传 提 供 了 直 接 的 支 持 , 这 种 支 持 是 通 过 即 插 即 用 的MultipartResolver 实 现 的 。 Spring 用 Jakarta Commons FileUpload 技 术 实 现 了 一 个MultipartResolver 实现类:CommonsMultipartResovler
- Spring MVC 上下文中默认没有装配 MultipartResovler,因此默认情况下不能处理文件的
上传工作,如果想使用 Spring 的文件上传功能,需现在上下文中配置 MultipartResolver
需求
代码实现
- 引入 springmvc 文件上传需要的 jar 包
- 创建 D:\idea_java_projects\springmvc\web\fileUpload.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<h1>文件上传的演示</h1>
<form action="fileUpload" method="post" enctype="multipart/form-data"> <!--multipart/form-data可以提交二进制文件和文本文件 -->
文件介绍:<input type="text" name="introduce"><br>
选择文件:<input type="file" name="file"><br>
<input type="submit" value="上传文件">
</form>
</body>
</html>
- 配置中文过滤器,在 web.xml, 使用 Spring 提供的, 前面我们配置过了
别忘了要放在最前面。
<!--注释上面的,配置Spring提供的过滤器,解决中文乱码问题-->
<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>
- 配 置 文 件 上 传 解 析 器 , 在 springDispatcherServlet-servlet.xml , 简 单 看 一 下CommonsMultipartResolver 源码
<!-- 配置一个 springmvc 的文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
- 创 建
D:\idea_java_projects\springmvc\src\com\hspedu\web\fileupload\FileUploadHandler.java
@Controller
public class FileUploadHandler {
@RequestMapping(value = "/fileUpload")
// @RequestParam(value = "file") 表示接收从http请求中提取查询参数或表单参数
public String fileUpload(@RequestParam(value = "file")MultipartFile file, HttpServletRequest httpServletRequest) throws IOException {
String originalFilename = file.getOriginalFilename(); // 获取文件的原名
System.out.println("上传文件名=" + originalFilename);
String filepath = httpServletRequest.getServletContext().getRealPath("/img/" + originalFilename);
System.out.println(filepath);
// 转存到E:/kk.jpg
File saveToFile = new File("E://" + originalFilename);
file.transferTo(saveToFile);
return "success";
}
}
成功:
自定义拦截器
什么是拦截器
- Spring MVC 也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能.
- 自定义的拦截器必须实现 HandlerInterceptor 接口
● 自定义拦截器的三个方法
- preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求request 进行处理。
- postHandle():这个方法在目标方法处理完请求后执行
- afterCompletion():这个方法在完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作
自定义拦截器执行流程分析图
● 自定义拦截器执行流程说明
- 如果 preHandle 方法 返回 false, 则不再执行目标方法, 可以在此指定返回页面
- postHandle 在目标方法被执行后执行. 可以在方法中访问到目标方法返回的ModelAndView 对象
- 若 preHandle 返回 true, 则 afterCompletion 方法 在渲染视图之后被执行.
- 若 preHandle 返回 false, 则 afterCompletion 方法不会被调用
- 在配置拦截器时,可以指定该拦截器对哪些请求生效,哪些请求不生效
自定义拦截器应用实例
● 应用实例需求
完成一个自定义拦截器,学习一下如何配置拦截器和拦截器的运行流程
- 创 建
D:\idea_java_projects\springmvc\src\com\hspedu\web\interceptor\MyInterceptor01.java
@Controller
public class MyInterceptor01 implements HandlerInterceptor {
/**
* pre方法在目标方法执行前被调用
* 如果返回fanlse, 则不会再执行目标方法. 可以在此响应请求返回给页面
* 不管是返回true还是false, 都会执行当前拦截器之前的拦截器 afterCompletion方法, 但不会执行当前拦截器的 afterCompletion
*
**/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("====MyInterceptor01 preHandler()====");
return true;
}
/*
* 在目标方法执行后执行.可以在方法中访问到目标方法返回的ModelAndView对象.
* */
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("====MyInterceptor01 postHandler()====");
}
/*
若preHandler 返回true, 则方法在渲染视图之后被执行.
若preHandler 返回false, 则该方法不会执行.
若当前拦截器的下一个拦截器的 preHandle 返回 false, 则在执行下一个拦截器的preHandler方法后马上被执行.
可以访问到目标方法中出现的异常.
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("===MyInterceptor01 afterCompletion()==="); }
}
- 创 建
D:\idea_java_projects\springmvc\src\com\hspedu\web\interceptor\FurnHandler.java 作 为
目标方法
@Controller
public class FurnHandler {
@RequestMapping("/hi")
public String hi() {
System.out.println("FurnHandler HI()");
return "success";
}
@RequestMapping(value = "/hello")
public String hello() {
System.out.println("FurnHandler hello()");
return "success";
}
}
- 在 springDispatcherServlet-servlet.xml 配置拦截器
<!--
如何配置自定义的拦截器
1. 创建实现HandlerInterceptor接口的bean
2. 在mvc:interceptors 中配置拦截器
-->
<mvc:interceptors>
<!--下面这行注释不注释都可以跑通-->
<!--<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>-->
<!--第一种方式,自己在interceptors中配置一个引用,指向你需要使用的拦截器. 这样会对所有的请求都拦截.-->
<!--<ref bean="myInterceptor01"/>-->
<!--
第二种配置方式(1):也可以在interceptor中配置.
mvc:mapping 表示自定义拦截器, 只对 /hi请求拦截
<mvc:interceptor>
<mvc:mapping path="/hi"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
-->
<!--
第二种配置方式(2)
mvc:exclude-mapping 表示自定义拦截器, 不对哪些请求拦截.
比如 myInterceptor拦截器对 /h 开头请求拦截, 排除/hello
-->
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<mvc:exclude-mapping path="/hello"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
</mvc:interceptors>
- 创建 D:\idea_java_projects\springmvc\web\interceptor.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试自定义拦截器</title>
</head>
<body>
<h1>测试自定义拦截器</h1>
<a href="hi">测试自定义拦截器</a><br><br>
<a href="hello">登录</a>
</body>
</html>
注意事项和细节
1、默认配置是都所有的目标方法都进行拦截, 也可以指定拦截目标方法, 比如只是拦截 hi
2、mvc:mapping 支持通配符, 同时指定不对哪些目标方法进行拦截
3、拦截器需要配置才生效,不配置是不生效的. 4、如果 preHandler() 方法返回了 false, 就不会执行目标方法(前提是你的目标方法被拦截了), 程序员可以在这里根据业务需要指定跳转页面
多个拦截器
-
创 建
D:\idea_java_projects\springmvc\src\com\hspedu\web\interceptor\MyInterceptor02.java
和MyInterceptor01一样的结构。 -
配置 springDispatcherServlet-servlet.xml
3.测试
注意事项和细节
- 如果第 1 个拦截器的 preHandle() 返回 false , 后面都不在执行
- 如 果 第 2 个 拦 截 器 的 preHandle() 返 回 false , 就 直 接 执 行 第 1 个 拦 截 器 的afterCompletion()方法, 如果拦截器更多,规则类似
- 说明: 前面说的规则,都是目标方法被拦截的前提
应用实例 2
需求: 如果用户提交的数据有禁用词(比如 病毒),则,在第 1 个拦截器就返回,不执行目标方法, 功能效果示意图
- 代码实现: 创建 D:\hspedu_ssm_temp\springmvc\web\WEB-INF\pages\warning.jsp
在pages下创建warning.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>警告</title>
</head>
<body>
<h1>不要乱讲话~</h1>
</body>
</html>
- 代 码 实 现 : 修 改
D:\hspedu_ssm_temp\springmvc\src\com\hspedu\web\interceptor\MyInterceptor01.java
主要是preHandle方法
@Controller
public class MyInterceptor01 implements HandlerInterceptor {
/**
* pre方法在目标方法执行前被调用
* 如果返回fanlse, 则不会再执行目标方法. 可以在此响应请求返回给页面
* 不管是返回true还是false, 都会执行当前拦截器之前的拦截器 afterCompletion方法, 但不会执行当前拦截器的 afterCompletion
*
**/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if("病毒".equals(request.getParameter("topic"))) {
request.getRequestDispatcher("/WEB-INF/pages/warning.jsp").forward(request, response); // 请求转发
return false;
}
System.out.println("====MyInterceptor01 preHandler()====");
return true;
}
/*
* 在目标方法执行后执行.可以在方法中访问到目标方法返回的ModelAndView对象.
* */
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("====MyInterceptor01 postHandler()====");
}
/*
若preHandler 返回true, 则方法在渲染视图之后被执行.
若preHandler 返回false, 则该方法不会执行.
若当前拦截器的下一个拦截器的 preHandle 返回 false, 则在执行下一个拦截器的preHandler方法后马上被执行.
可以访问到目标方法中出现的异常.
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("===MyInterceptor01 afterCompletion()==="); }
}
测试:
15 异常处理
● 基本介绍
- Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
- 主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。
- ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话,会找@ControllerAdvice 类的@ExceptionHandler 注解方法, 这样就相当于一个全局异常处理器
局部异常
演示局部异常处理机制
- 如果不处理异常, 非常的不友好
- 创 建
D:\idea_java_projects\springmvc\src\com\hspedu\web\exception\MyExceptionHandler.java
@Controller
public class MyExceptionHandler {
@ExceptionHandler({ArithmeticException.class, NullPointerException.class}) // {}内的内容表示该方法专门用于处理ArithmeticException和NullPointerException这两种类型的异常
public String localException(Exception ex, HttpServletRequest request) {
System.out.println("异常信息是" + ex.getMessage());
// 如何将异常信息带到下个页面
request.setAttribute("reason", ex.getMessage());
return "exception_mes";
}
@RequestMapping(value = "/testException01")
public String test01(Integer num) {
int i = 9/num;
return "success";
}
}
- 创建 D:\idea_java_projects\springmvc\web\exception.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>异常信息</title>
</head>
<body>
<h1>测试异常</h1>
<a href="testException01?num=0">点击测试异常</a><br><br>
<a href="testGlobalException">点击测试全局异常</a><br><br>
</body>
</html>
- 创建显示异常信息页面 /WEB-INF/pages/exception_mes.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>异常提示信息</title>
</head>
<body>
<h1>朋友,发生异常,信息如下 :</h1>
${reason }
</body>
</html>
- 测试(页面方式)
全局异常
演 示 全 局 异 常 处 理 机 制 , ExceptionHandlerMethodResolver 内 部 若 找 不 到@ExceptionHandler 注解的话,会找 @ControllerAdvice 类的@ExceptionHandler 注解方法, 这样就相当于一个全局异常处理器
- 创 建
D:\idea_java_projects\springmvc\src\com\hspedu\web\exception\MyGlobalException.java
@ControllerAdvice
public class MyGlobalException {
@ExceptionHandler({ClassCastException.class, NumberFormatException.class})
public String localException(Exception ex, HttpServletRequest request) {
System.out.println("全局异常信息是=" + ex.getMessage());
request.setAttribute("reason", ex.getMessage());
return "exception_mes";
}
}
- 修改 MyExceptionHandler.java , 增加方法
@RequestMapping(value="/testGlobalException")
public String global(){
//老韩解读
//1. 模拟 NumberFormatException
//2. 在老韩定义的局部异常中,没有对 NumberFormatException
//3. 所有就会去找全局异常处理
int num = Integer.parseInt("hello");
return "success";
}
- 修改 exception.jsp
<a href="testException01?num=0">点击测试异常</a><br><br>
<a href="testGlobalException">点击测试全局异常</a><br><br>
自定义异常
通过@ResponseStatus 注解, 可以自定义异常的说明