1.SpringMVC 从易用性,效率上 比曾经流行的Struts2更好
2.SpringMVC 是WEB层(框架SpringMVC接管了Web层组件,比如控制器,视图,视图解析,返回给用户的数据格式)
3.SpringMVC通过注解,让POJ0成为控制器,不需要实现任何接口。
4.SpringMVC采用低耦合的组件设计方式,具有更的扩展和灵活性
5.支持REST 格式的URL请求.
6.讲的SpringMVC是基于Spring5.x的,也就是SpringMVC是在Spring基础上的SpringMVC的核心包spring-webmvc-5.3.8.jar 和spring-web-5.3.8.jar
Spring SpringMVc SpringBoot 的关系

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.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.hspedu.web"/>
<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置属性suffix 和 prefix-->
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
<!--调整优先级-->
<property name="order" value="10"/>
</bean>
<!--
老师解读
1. 配置自定义视图解析器BeanNameViewResolver
2. BeanNameViewResolver可以去解析我们自定义的视图
3. 配置 属性 order, 表示视图解析器执行的顺序, 值越小, 优先级越高
4. 属性 order 的默认值是最低优先级 ,值为 Integer.MAX_VALUE
int LOWEST_PRECEDENCE = 2147483647
-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="99"/>
</bean>
<!-- 配置国际化错误信息的资源处理bean -->
<bean id="messageSource" class=
"org.springframework.context.support.ResourceBundleMessageSource">
<!-- 配置国际化文件名字
如果你这样配的话,表示messageSource回到 src/i18nXXX.properties去读取错误信息
-->
<property name="basename" value="i18n"></property>
</bean>
<!--配置文件上传需要的bean-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
id="multipartResolver"/>
<!--配置自定义拦截器-spring配置文件-->
<mvc:interceptors>
<!--
老韩解读
1. 第一种配置方式
2. 使用ref 引用到对应的myInterceptor01
3. 这种方式,会拦截所有的目标方法
-->
<!--<ref bean="myInterceptor01"/>-->
<!--老韩解读
1. 第二种配置方式
2. mvc:mapping path="/hi" 指定要拦截的路径
3. ref bean="myInterceptor01" 指定对哪个拦截器进行配置
-->
<!--<mvc:interceptor>-->
<!-- <mvc:mapping path="/hi"/>-->
<!-- <ref bean="myInterceptor01"/>-->
<!--</mvc:interceptor>-->
<!--解读
1. 第3种配置方式
2. mvc:mapping path="/h*" 通配符方式 表示拦截 /h 打头的路径
3. mvc:exclude-mapping path="/hello" /hello不拦截
4. ref bean="myInterceptor01" 指定对哪个拦截器配置
-->
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<mvc:exclude-mapping path="/hello"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
<!--老师解读
1.配置的第二个拦截器
2.多个拦截器在执行时,是顺序执行
-->
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<ref bean="myInterceptor02"/>
</mvc:interceptor>
</mvc:interceptors>
<!--配置统一处理异常Bean-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--<prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>-->
<!--<prop key="java.lang.Exception">allEx</prop>-->
</props>
</property>
</bean>
<!--加入两个常规配置-->
<!--支持SpringMVC的高级功能,比如JSR303校验, 映射动态请求-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--将springmvc不能处理的请求,交给tomcat处理,比如css, js-->
<mvc:default-servlet-handler/>
</beans>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">
<!--配置处理中文乱码的过滤器
拦截所有请求,处理编码, 提醒,把过滤器配置到web.xml前面
-->
<!--<filter>-->
<!-- <filter-name>MyCharacterFilter</filter-name>-->
<!-- <filter-class>com.hspedu.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>
<!--配置HiddenHttpMethodFilter
1. 作用是 把 以post方式提交的delete和put请求进行转换
2. 配置url-pattern 是 /* 表示请求都经过 hiddenHttpMethodFilter过滤
3. 后面通过debug源码,就看的很清楚
-->
<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>
<!--配置前端控制器/中央控制器/分发控制器
1.用户的请求都会经过它的处理
-->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--解读
1. 如果我们没有配置 contextConfigLocation
2. 默认按照这样的位置去定位spring配置文件 /WEB-INF/springDispatcherServlet-servlet.xml
-->
<!--配置属性 contextConfigLocation, 指定DispatcherServlet 去操作的spring配置文件-->
<!--<init-param>-->
<!-- <param-name>contextConfigLocation</param-name>-->
<!-- <param-value>classpath:applicationContext-mvc.xml</param-value>-->
<!--</init-param>-->
<!--在web项目启动时,就自动的加载DispatcherServlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!--说明
1. 这里我们配置的url-pattern是 / ,表示用户的请求都经过 DispatcherServlet
2. 这样配置也这次rest 风格的url请求
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>SpringMVC 工作流程

如上图,Spring MVC 程序的完整执行流程如下:
1.用户通过浏览器发送请求,请求会被 Spring MVC 的『前端控制器』DispatcherServlet 接收。
2.DispatcherServlet 拦截到请求后,会调用『处理器映射器』HandlerMapping 。
3.『处理器映射器』根据请求 URL 找到具体的『处理器』Handler ,生成『处理器』对象(如果有,还会生成拦截器对象)并返回给 DispatcherServlet 。
4.DispatcherServlet 根据返回信息(Handler)选择合适的『处理器适配器』HandlerAdapter 。
5.HandlerAdapter 会调用并指定 Handler 。此处和上述所说的处理器 Handler ,就是我们所编写的 Controller 类。
6.Controller 执行完成后,会返回一个 ModelAndView 对象,该对象中会包含『视图名』和『模型对象』。
7.HandlerAdapter 将 ModelAndView 返回给 DispatcherServlet 。
8.DispatcherServlet 会根据返回信息(ModelAndView)选择一个合适的『视图解析器』ViewResolver 。
9.视图解析器 ViewResolver 解析视图后,会向 DispatcherServlet 返回具体的 View 对象。
10.DispatcherServlet 对 View 进行渲染。即,将『模型数据』填充至『视图』中。
11.DispatcherServlet 将渲染后的结果返回、发送给客户端浏览器。
@RequestMapping
@RequestMapping 是 Spring MVC 中常用的注解,用于映射 HTTP 请求到处理器方法上。它可以用在类级别和方法级别上,用来指定处理请求的路径和方法。
@RequestMapping 的常用参数包括:
1. value / path:
- @RequestMapping(value = "/path") 或 @RequestMapping(path = "/path")
- 指定请求的路径,可以是一个字符串或字符串数组。例如,`"/home"` 或 {"home", "index"}。
2. method:
- @RequestMapping(method = RequestMethod.GET)
- 指定处理请求的 HTTP 方法,如 GET、POST、PUT、DELETE 等。默认是支持所有方法的。
3. params:
- @RequestMapping(params = "param=value")
- 指定请求参数的条件,例如指定某个参数必须存在或者等于特定值才处理请求。
4. headers:
- @RequestMapping(headers = "Content-Type=text/html")
- 指定请求头的条件,例如指定请求的 Content-Type。
5. consumes:
- @RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
- 指定处理请求的 Content-Type,限定请求的 MIME 类型。
6. produces:
- @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
- 指定返回的 Content-Type,限定响应的 MIME 类型。
7. name:
- @RequestMapping(name = "handlerName")
- 指定处理器的名称,用于识别不同的处理器方法。
8. paramsCondition:
- @RequestMapping(paramsCondition = "param1=value1")
- 进一步指定请求参数的条件,支持 SpEL 表达式。
9. headersCondition:
- @RequestMapping(headersCondition = "Content-Type=text/html")
- 进一步指定请求头的条件,支持 SpEL 表达式。
这些参数可以组合使用,以更精确地定义请求映射规则。通过这些参数,`@RequestMapping` 注解可以灵活地定义处理器方法如何响应特定的 HTTP 请求。
10.Ant风格路径:

注意事项


为了简化常见的请求映射,Spring 提供了 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping 和 @PatchMapping 等注解,这些注解是 @RequestMapping 的简写形式,分别对应常用的 HTTP 方法。

注意!!!!
如果在所有的配置都对的情况下,还是出现404错误,那就一定要加上这个配置,因为web.xml默认匹配.jsp文件 大坑!!!!
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>Rest风格请求
基本说明


<%--
Created by IntelliJ IDEA.
User: zfc
Version: 1.0
Filename: rest
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>rest </title>
<%-- 引入jquery--%>
<script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function () { //当页面加载完成后,就执行=> 如果你完全忘记,去回顾我们讲过的jquery
//给删除超链接绑定一个点击事件
$("#deleteBook").click(function (){
//alert("点击。。。。");
//我们自己定义给提交的行为
$("#hiddenForm").attr("action", this.href);
$(":hidden").val("DELETE");
$("#hiddenForm").submit();
return false; //改变点击超链接的行为, 不在提交
})
})
</script>
</head>
<body>
<h3>Rest风格的crud操作案例</h3>
<br><hr>
<h3>rest风格的url 查询书籍[get]</h3>
<a href="user/book/200">点击查询书籍</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. 默认情况下 <a href="user/book/600">删除指定id的书</a> 是get
2. 怎么样将 get 请求转成 springmvc 可以识别的 delete 就要考虑HiddenHttpMethodFilter机制
public static final String DEFAULT_METHOD_PARAM = "_method";
---------------------------------------------------
private static final List<String> ALLOWED_METHODS =
Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
---------------------------------------------------
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
3. 上面代码可以看到 HiddenHttpMethodFilter 过滤器可以对以Post方式提交的delete,put,patch进行转换,成springmvc
识别的 RequestMethod.DELETE / RequestMethod.PUT /...
4. 我们需要将 get <a href="user/book/600">删除指定id的书</a> 以post方式提交给后端handler, 这样过滤器还会生效
5. 我们可以同jquery来处理-引入jquery
--%>
<a href="user/book/600" 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/666" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="修改书籍~">
</form>
</body>
</html>package com.zfc.web.rest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @author zfc
* @version 1.0
* BookHandler处理rest风格的请求-增删改查
*/
@RequestMapping("/user")
@Controller
public class BookHandler {
//查询[GET]
@RequestMapping(value = "/book/{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 successGenecal() {
return "success"; //由该方法 转发到success.jsp页面
}
//修改[PUT]
@PutMapping(value = "/book/{id}")
public String updateBook(@PathVariable("id") String id) {
System.out.println("修改书籍 id=" + id);
return "redirect:/user/success";
}
}
细节(model and view)
package com.zfc.web.requestparam;
import com.hspedu.web.requestparam.entity.Master;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;
/**
* @author 韩顺平
* @version 1.0
*/
@RequestMapping("/vote")
@Controller
public class VoteHandler {
/**
* 解读 @RequestParam(value="name", required=false)
* 1. 获取到超链接传递的数据 请求 http://localhost:8080/springmvc/vote/vote01?name=xx
* 2. @RequestParam 表示会接收提交的参数
* 3. value="name" 表示提交的参数名是name
* 4. required=false 表示该参数可以没有, 默认是true,表示必须有这个参数
* 5. 当我们使用了@RequestParam(value="name", required=false)后就请求的参数名和方法的形参名可以不一致
*/
@RequestMapping(value = "/vote01")
public String test01(@RequestParam(value = "name", required = false) String username) {
System.out.println("得到的username= " + username);
//返回到一个结果
return "success";
}
/**
* 需求: 获取http请求头信息, 获取到Accept-Encoding 和 Host
* 1. 这里涉及到前面讲过的http协议,小伙伴可以进行回顾
*
* @RequestHeader("Http请求头字段")
*/
@RequestMapping(value = "/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";
}
/**
* 演示如果获取到提交数据->封装成java对象
*
* @return 说明
* 1. 方法的形参用对应的类型来指定即可, SpringMVC会自动的进行封装
* 2. 如果自动的完成封装, 要求提交的数据,参数名和对象的字段名保持一致
* 3. 如果属性是对象,这里就是仍然是通过 字段名.字段名 比如Master [pet]
* , 即提交的数据 参数名 是 pet.id pet.name, 这就是级联操作
* 4. 如果提交的数据 的参数名和对象的字段名不匹配,则对象的属性值就是null
* 5. 小伙伴疑惑,怎么就封装成功[底层仍然是反射+注解..]
*/
@RequestMapping(value = "/vote03")
public String test03(Master master) {
System.out.println("master=" + master);
//返回结果
return "success";
}
/**
* 使用servlet api, 来获取提交的数据
*
* @return
*/
@RequestMapping(value = "/vote04")
public String test04(HttpServletRequest request,
HttpServletResponse response,
HttpSession hs) {
//获取到session
//servlet原生的方式
HttpSession session = request.getSession();
System.out.println("session=" + session);
//注意:通过参数传入的 hs 和 通request.getSession() 得到的对象是
//同一个
System.out.println("hs= " + hs);
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
System.out.println("username= " + username);
System.out.println("pwd= " + pwd);
//返回结果
return "success";
}
/**
* 解读
* 1. 演示将提交的数据->springmvc封装到java对象->springmvc 会自动的将其放入到request域
* 2. 这样我们就可以在跳转到的页面取出数据.
*
* @return
*/
@RequestMapping(value = "/vote05")
public String test05(Master master, HttpServletRequest request) {
//老韩解读
//1. springmvc会自动把获取的model模型,放入到request域中,名字就是master
//2. 也可以手动将master放入到request[一会在讲]
request.setAttribute("address", "beijing");
//3. 如果我们希望修改master的属性值
master.setName("nono");
//4. 分析一下springmvc默认存放对象到request域中,属性名是
// request域 ("master", master) 属性名是类名/类型名 首字母小写
//返回到一个结果
return "vote_ok";
}
/**
* 演示通过Map<String,Object> 设置数据到request域
*
* @return
*/
@RequestMapping(value = "/vote06")
public String test06(Master master, Map<String, Object> map) {
System.out.println("------test06-----");
//老韩解读
//1. 需求是通过map对象,添加属性到request中
//2. 原理分析:springmvc会遍历map,然后将map的k-v, 存放到request域
map.put("address", "beijing...");
//map.put("master", null);
//返回到一个结果
return "vote_ok";
}
/**
* 演示通过返回ModelAndView对象,将数据放入到request域
* @return
*/
@RequestMapping(value = "/vote07")
public ModelAndView test07(Master master) {
System.out.println("----test07----");
ModelAndView modelAndView = new ModelAndView();
//放入属性到modelAndView对象
modelAndView.addObject("address", "shanghai");
//modelAndView.addObject("master", null);
//可以把从数据库得到的数据->对象-》放入modelAndView[Service-dao-db]
//这里指定跳转的视图名称
modelAndView.setViewName("vote_ok");
//返回结果
return modelAndView;
}
/**
* 演示如何将数据设置到session域中
* @return
*/
@RequestMapping(value = "/vote08")
public String test08(Master master, HttpSession httpSession) {
System.out.println("----test08----");
//master对象是默认放在request域
//我们将master对象放入到session域
httpSession.setAttribute("master", master);
httpSession.setAttribute("address", "guangzhou");
return "vote_ok";//请求转发
}
/**
* 解读
* 1. 当Handler的方法被标识 @ModelAttribute,就视为一个前置方法
* 2. 当调用该Handler的其它的方法时,都会先执行该前置方法
* 3. 类似我们前面讲解Spring时,AOP的前置通知[底层是AOP机制]
* 4. prepareModel前置方法,会切入到其它方法前执行..
*/
@ModelAttribute
public void prepareModel(){
System.out.println("prepareModel()-----完成准备工作-----");
}
}
视图和视图解析器
1.在默认情况下,我们都是返回默认的视图,然后返回的视图交由 SpringMVC的InternalResourceViewResolver 视图解析器器来处理的
2.在实际开发中,我们有时需要自定义视图,这样可以满足更多更复杂的需求
配置自定义视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置属性suffix 和 prefix-->
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
<!--调整优先级-->
<property name="order" value="10"/>
</bean>
<!--
解读
1. 配置自定义视图解析器BeanNameViewResolver
2. BeanNameViewResolver可以去解析我们自定义的视图
3. 配置 属性 order, 表示视图解析器执行的顺序, 值越小, 优先级越高
4. 属性 order 的默认值是最低优先级 ,值为 Integer.MAX_VALUE
int LOWEST_PRECEDENCE = 2147483647
-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="99"/>
</bean>自定义视图总结


重定向(Redirect)不能直接跳转到 WEB-INF 目录下的路径是因为 WEB-INF 目录是受保护的,客户端无法直接访问该目录中的内容。
1. WEB-INF 目录的特殊性
在 Java Web 应用中,WEB-INF 目录是一个特殊的目录,位于这个目录中的资源(如 JSP 文件、HTML 文件)不能被直接通过 URL 访问。这是为了安全考虑,目的是防止用户直接通过浏览器访问某些敏感页面或资源。通常,WEB-INF 中的内容包括配置文件、类文件、JSP 文件等,只有通过服务器端(如请求转发)才能访问这些资源。
请求转发(forward) 是服务器内部行为,允许服务器从
WEB-INF中调取资源,因为它不涉及客户端的直接访问。重定向(redirect) 是客户端行为,浏览器会发起新的 HTTP 请求,但由于
WEB-INF目录不对外开放,所以重定向时无法访问该目录中的资源。
2. 为什么重定向不能访问 WEB-INF 目录?
重定向的本质是让客户端(浏览器)发送一个新的请求去访问新的 URL,而 WEB-INF 目录下的文件无法被客户端直接请求访问。服务器会自动阻止任何尝试通过浏览器直接访问 WEB-INF 路径的请求。
举个例子:
如果在浏览器地址栏中直接输入
http://localhost:8080/YourApp/WEB-INF/views/success.html,你会得到一个 404 错误,因为服务器不允许直接访问WEB-INF目录下的文件。当你使用
response.sendRedirect("/WEB-INF/views/success.html"),实际上会告诉浏览器发起一个新的请求去访问这个路径。但由于客户端无法直接访问WEB-INF,因此重定向到这个路径会失败并返回 404 Not Found 错误。
数据格式化

特殊数据类型的转换

package com.zfc.web.datavalid.entity;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
import javax.validation.constraints.NotNull;
import java.util.Date;
/**
* @author zfc
* @version 1.0
*/
public class Monster {
private Integer id;
//email是string,使用@NotEmpty
@NotEmpty
private String email;
//@Range(min = 1,max = 100)
//表示接收的age值,在 1-100之间
@NotNull(message = "age不能为空")
@Range(min = 1,max = 100)
private Integer age;
//@NotEmpty 表示name不能为空
//Asserts that the annotated string, collection, map or array is not {@code null} or empty.
@NotEmpty
private String name;
@NotNull(message = "生日不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NotNull(message = "薪水不能为空")
@NumberFormat(pattern = "###,###.##")
private Float salary;
public Monster(Integer id, String email, Integer age, String name, Date birthday, Float salary) {
this.id = id;
this.email = email;
this.age = age;
this.name = name;
this.birthday = birthday;
this.salary = salary;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Float getSalary() {
return salary;
}
public void setSalary(Float salary) {
this.salary = salary;
}
public Monster() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Monster{" +
"id=" + id +
", email='" + email + '\'' +
", age=" + age +
", name='" + name + '\'' +
", birthday=" + birthday +
", salary=" + salary +
'}';
}
}
package com.zfc.web.datavalid;
import com.hspedu.web.datavalid.entity.Monster;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
/**
* @author zfc
* @version 1.0
* MonsterHandler 处理器响应用户提交数据
* @Scope(value = "prototype") 表示每次请求MonsterHandler会生成一个新的对象
*/
@Controller
@Scope(value = "prototype")
public class MonsterHandler {
/**
* 显示添加monster的界面
* 1. 这里Map<String, Object> map
* 2. 当我们向map添加的数据时,会默认存放到request域
*
* @param map
* @return
*/
@RequestMapping(value = "/addMonsterUI")
public String addMonsterUI(Map<String, Object> map) {
/**解读:
1. 这里的表单,我们使用springMVC的标签来完成
2. SpringMVC 表单标签在显示之前必须在 request 中有一个 bean, 该 bean 的属性和表单标签的字段要对应!
request 中的 key 为: form 标签的 modelAttrite 属性值, 比如这里的monsters
3. SpringMVC 的 form:form 标签的 action 属性值中的 / 不代表 WEB 应用的根目录.
4. <form:form action="?" method="POST" modelAttribute="monster">
//这里需要给request增加一个 monster,因为jsp 页面 的modelAttribute="monster"需要
//这时是springMVC的内部的检测机制 即使是一个空的也需要,否则报错.
*/
//老韩再次说明,如果你跳转的页面使用springmvc标签
//就需要准备一个对象,放入request域中,这个对象的属性名 monster, 对于
//springmvc表单标签的 modelAttribute="monster"
map.put("monster", new Monster());
return "datavalid/monster_addUI";
}
/**
* 编写方法,处理添加妖怪
* 1. springmvc可以将提交的数据,按照参数名和对象的属性名匹配
* 2. 直接封装到对象中->老师前面讲解模型数据时,讲过
* String => Integer
* 3. @Valid Monster monster :表示对monster接收的数据进行校验
* 4. Errors errors 表示如果校验出现错误,将校验的错误信息保存 errors
* 5. Map<String, Object> map 表示如果校验出现错误, 将校验的错误信息保存 map 同时保存monster对象
* 6. 校验发生的时机: 在springmvc底层,反射调用目标方法时,会接收到http请求的数据,然后根据注解来进行验证
* , 在验证过程中,如果出现了错误,就把错误信息填充errors 和 map
*
* @param monster
* @return
*/
@RequestMapping(value = "/save")
public String save(@Valid Monster monster, Errors errors, Map<String, Object> map) {
System.out.println("----monster---" + monster);
//我们为了看到验证的情况,我们输出map 和 errors
System.out.println("===== map ======");
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " value=" + entry.getValue());
}
System.out.println("===== errors ======");
if (errors.hasErrors()) {//判断是否有错误
List<ObjectError> allErrors = errors.getAllErrors();
for (ObjectError error : allErrors) {
System.out.println("error=" + error);
}
return "datavalid/monster_addUI";
}
return "datavalid/success";
}
//取消绑定 monster的name表单提交的值给monster.name属性
@InitBinder
public void initBinder(WebDataBinder webDataBinder) {
/**
* 解读
* 1. 方法上需要标注 @InitBinder springmvc底层会初始化 WebDataBinder
* 2. 调用 webDataBinder.setDisallowedFields("name") 表示取消指定属性的绑定
* 即:当表单提交字段为 name时, 就不在把接收到的name值,填充到model数据monster的name属性
* 3. 机制:springmvc 在底层通过反射调用目标方法时, 接收到http请求的参数和值,使用反射+注解技术
* 取消对指定属性的填充
* 4. setDisallowedFields支持可变参数,可以填写多个字段
* 5. 如果我们取消某个属性绑定,验证就没有意义了,应当把验证的注解去掉, name属性会使用默认值null
* //@NotEmpty
* private String name;
*
*/
//webDataBinder.setDisallowedFields("name");
}
}
验证及国际化
概述
1.对输入的数据(比如表单数据),进行必要的验证并给出相应的提示信息。
2.对于验证表单数据,springMVC提供了很多实用的注解,这些注解由JSR 303验证框架提供。
JSR 303 验证框架
供的标准框架,它已经包含在JavaEE中1.JSR 303 是Java为 Bean 数据合法性校验otNul、@Max 等标准的注解指定校验规则,并通过标准2.JSR 303 通过在 Bean 属性上标注类似于 @的验证接口对 Bean 进行验证
3.JSR 303 提供的基本验证注解有:


细节

DataBinder

取消属性绑定
说明
在默认情况下,表单提交的数据都会和pojo类型的iavabean属性绑定,如果程序员在开发中,希望取消某个属性的绑定,也就是说,不希望接收到某个表单对应的属性的值,则可以通过@InitBinder 注解取消绑定.
1.编写一个方法,使用@InitBinder 标识的该方法,可以对 WebDataBinder 对象进行初始化。NebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定
2.@InitBinder方法不能有返回值,它必须声明为void。
3.@InitBinder方法的参数通常是是 WebDataBinder
配置解决中文乱码问题的过滤器
<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>处理JSON-@ResponseBody
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // 这里使用了 @RestController,等同于 @Controller + @ResponseBody
@RequestMapping("/api")
public class DemoController {
// 返回一个简单的字符串
@GetMapping("/hello")
@ResponseBody // @ResponseBody 注解表示返回的是数据而不是视图
public String sayHello() {
return "Hello, World!";
}
// 返回一个 JSON 对象
@GetMapping("/user")
@ResponseBody
public User getUser() {
return new User(1, "John Doe", "john@example.com");
}
}
处理JSON-@RequestBody
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
// 处理 POST 请求,并通过 @RequestBody 接收 JSON 数据
@PostMapping("/api/user")
public String createUser(@RequestBody User user) {
// 接收到的 User 对象已经由 JSON 自动转换而来
return "User created: " + user.getName() + ", Email: " + user.getEmail();
}
}
HttpMessageconverter<T>


文件下载

package com.zfc.web.json;
import com.hspedu.web.json.entity.Dog;
import com.hspedu.web.json.entity.User;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* @author zfc
* @version 1.0
*/
//@Controller
//@ResponseBody
@RestController
public class JsonHandler {
/**
* 解读
* 1. 目标方法 @ResponseBody,表返回的数据是json格式
* 2. springmvc底层根据目标方法@ResponseBody, 返回指定格式, 根据的http请求来进行处理
* 3. 底层原理我们在前面自定义@ResponseBody讲过, 这里原生的springmvc使用转换器
* 4. HttpMessageConverter [一会老师debug]
*
* @return
*/
@RequestMapping(value = "/json/dog")
//@ResponseBody
public Dog getJson() {
//返回对象
//springmvc会根据你的设置,转成json格式数据返回
Dog dog = new Dog();
dog.setName("大黄狗");
dog.setAddress("小新的家");
return dog;
}
//编写方法,以json格式返回多个Dog
@RequestMapping(value = "/json/dogs")
//@ResponseBody
public List<Dog> getJsons() {
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog("大黄狗", "小新的家"));
dogs.add(new Dog("大黄狗2", "小新2的家"));
dogs.add(new Dog("大黄狗3", "小新3的家"));
return dogs;
}
/**
* 老师解读
* 1. @RequestBody User user 在形参指定了 @RequestBody
* 2. springmvc就会将提交的json字符串数据填充给指定Javabean
*
* @param user
* @return
*/
@RequestMapping(value = "/save2")
//@ResponseBody
public User save2(@RequestBody User user) {
//将前台传过来的数据 以json的格式相应回浏览器
System.out.println("user~= " + user);
return user;
}
//响应用户下载文件的请求
@RequestMapping(value = "/downFile")
public ResponseEntity<byte[]> downFile(HttpSession session)
throws Exception {
//1. 先获取到下载文件的inputStream
InputStream resourceAsStream =
session.getServletContext().getResourceAsStream("/img/2.jpg");
//2. 开辟一个存放文件的byte数组, 这里老师使用byte[] 是可以支持二进制数据(图片,视频。)
byte[] bytes = new byte[resourceAsStream.available()];
//3. 将下载文件的数据,读入到byte[]
resourceAsStream.read(bytes);
//public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) {}
//4. 创建返回的HttpStatus
HttpStatus httpStatus = HttpStatus.OK;
//5. 创建 headers
HttpHeaders headers = new HttpHeaders();
//指定返回的数据,客户端应当以附件形式处理
headers.add("Content-Disposition", "attachment;filename=2.jpg");
//构建一个ResponseEntity 对象1. 的http响应头headers 2. http响应状态 3. 下载的文件数据
ResponseEntity<byte[]> responseEntity =
new ResponseEntity<>(bytes, headers, httpStatus);
//如果出现找不到文件,解决方法 rebuild project -> 重启tomcat
return responseEntity;
}
}
文件的上传
基本介绍
1.Spring MVC 为文件上传提供了直接的支持,这种支持是通过即插即用的 MultipartResolver 实现的。Spring 用 Jakarta Commons FileUpload 技术实现了一个 MultipartResolver 实现类:CommonsMultipartResovler
2.Spring MVC 上下文中默认没有装配 MultipartResovler,因此默认情况下不能处理文件的上传工作,如果想使用 Spring 的文件上传功能,需现在上下文中配置 MultipartResolver
package com.zfc.web.fileupload;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
/**
* @author zfc
* @version 1.0
* 处理文件上传的handler
*/
@Controller
public class FileUploadHandler {
//编写方法,处理文件上传的请求
@RequestMapping(value = "/fileUpload")
public String fileUpload(@RequestParam(value = "file") MultipartFile file,
HttpServletRequest request, String introduce) throws IOException {
//接收到提交的文件名
String originalFilename = file.getOriginalFilename();
System.out.println("你上传的文件名= " + originalFilename);
System.out.println("introduce=" + introduce);
//得到要把上传文件保存到哪个路径[全路径:包括文件名]
String fileFullPath =
request.getServletContext().getRealPath("/img/" + originalFilename);
//创建文件
File saveToFile = new File(fileFullPath);
//将上传的文件,转存到saveToFile
file.transferTo(saveToFile);
return "success";
}
}
自定义拦截器
1.Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能..自定义的拦截器必须实现HandlerInterceptor接口
自定义拦截器的三个方法
1.preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理
2.postHandle():这个方法在目标方法处理完请求后执行
3.afterCompletion():这个方法在完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
自定义拦截器执行流程说明
1.如果preHandle 方法 返回 false,则不再执行目标方法,可以在此指定返回页面
2.postHandle 在目标方法被执行后执行,可以在方法中访问到目标方法返回的 ModelAndView 对象
3.若 preHandle 返回 true,则afterCompletion 方法 在渲染视图之后被执行,
4.若 preHandle 返回 false,则aftercompletion 方法不会被调用在配置拦截器时,可以指定该拦截器对哪些请求生效,哪些请求不生效

多个拦截器的执行流程


异常处理

@ExceptionHandler({ArithmeticException.class,NullPointerException.class,NumberFormatException.class})
public String localException(Exception ex, HttpServletRequest request){
System.out.println("局部异常信息是-" + ex.getMessage());
//如何将异常的信息带到下一个页面.
request.setAttribute("reason", ex.getMessage());
return "exception_mes";
}全局异常
// 如果类上标注了@ControllerAdvice,就是一个全局异常处理类
@ControllerAdvice
public class MyGlobalException {
/**
* 解读
* 1. 全局异常就不管是哪个Handler抛出的异常,都可以捕获 , @ExceptionHandler({异常类型})
* 2. 这里处理的全局异常是NumberFormatException.class,ClassCastException.class
* 3. Exception ex 接收抛出的异常对象
*
* @return
*/
//
@ExceptionHandler({NumberFormatException.class, ClassCastException.class, AgeException.class})
public String globalException(Exception ex, HttpServletRequest request) {
System.out.println("全局异常处理-" + ex.getMessage());
//如何将异常的信息带到下一个页面.
request.setAttribute("reason", ex.getMessage());
return "exception_mes";
}
}自定义异常
通过@Responsestatus注解,可以自定义异常的说明
@ResponseStatus(reason = "年龄需要在1-120之间", value = HttpStatus.BAD_REQUEST)
public class AgeException extends RuntimeException {
public AgeException() {
}
public AgeException(String message) {
super(message);
}
}统一处理异常信息
基本说明
1.如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver
2.它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常
3.需要在ioc容器中配置
4.对未知异常进行统一处理,使用java.lang.Exception
<!--配置统一异常处理-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">error/error.jsp</prop>
</props>
</property>
</bean>异常处理的优先级处理
局部异常 > 全局异常 > SimpleMappingExceptionResolver>tomcat 默认机制
SpringMVC
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法