springmvc
1 springmvc初识
1.1 第一个基于注解的 SpringMVC程序
所谓 SpringMVC 的注解式开发是指,在代码中通过对类与方法的注解,便可完成处理器在 springmvc 容器的注册。注解式开发是重点
完成功能:用户提交一个请求,服务端处理器在接收到这个请求后,给出一条欢迎信息,在响应页面中显示该信息
1.1.1 新建maven项目导入web骨架支持

1.1.2 pom.xml
servlet依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
springmvc依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
插件
<build>
<plugins>
<!-- 编码和编译和JDK版本 -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
1.1.3 注册中央调度器
在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">
<!--
1、声明和注册springmvc的核心对象DispatcherServlet
2、需要在tomcat服务器启动后,创建DispatcherServlet对象的实例。
3、为什么要创建DispatcherServlet对象的实例呢?
4、因为DispatcherServlet在他的创建过程中, 会同时创建springmvc容器对象,
读取springmvc的配置文件,把这个配置文件中的对象都创建好, 当用户发起
请求时就可以直接使用对象了。
servlet的初始化会执行init()方法。
DispatcherServlet在init()中{
//创建容器,读取配置文件
WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
//把容器对象放入到ServletContext中
getServletContext().setAttribute(key, ctx);
}
启动tomcat报错,读取这个文件 /WEB-INF/springmvc-servlet.xml(/WEB-INF/myweb-servlet.xml)
springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/<servlet-name>-servlet.xml
-->
<servlet>
<servlet-name>myweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义springmvc读取的配置文件的位置-->
<init-param>
<!--springmvc的配置文件的位置的属性-->
<param-name>contextConfigLocation</param-name>
<!--指定自定义文件的位置-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--
在tomcat启动后,创建Servlet对象
load-on-startup:表示tomcat启动后创建对象的顺序。它的值是整数,数值越小,
tomcat创建对象的时间越早。 大于等于0的整数。
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myweb</servlet-name>
<!--
使用框架的时候, url-pattern可以使用两种值
1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等
不能使用 *.jsp
http://localhost:8080/myweb/some.do
http://localhost:8080/myweb/other.do
2.使用斜杠 "/"
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
1.1.3.1 全限定性类名
该中央调度器为一个 Servlet,名称为 DispatcherServlet。
中央调度器的全限定性类名在导入的 Jar 文件 spring-webmvc-5.2.5.RELEASE.jar 的第一个包 org.springframework.web.servlet下可找到
1.1.3.2 <load-on-startup/>
在<servlet/>中添加<load-on-startup/>的作用是,标记是否在Web服务器(这里是Tomcat)启动时会创建这个 Servlet 实例,即是否在 Web 服务器启动时
调用执行该 Servlet 的 init()方法,而不是在真正访问时才创建。它的值必须是一个整数。
1、当值大于等于 0 时,表示容器在启动时就加载并初始化这个 servlet,数值越小,该 Servlet的优先级就越高,其被创建的也就越早;
2、当值小于 0 或者没有指定时,则表示该 Servlet 在真正被使用时才会去创建。
3、当值相同时,容器会自己选择创建顺序
1.1.3.3 <url-pattern/>
对于<url-pattern/>,可以写为 / ,建议写为*.do 的形式
1.1.4 创建 SpringMVC 配置文件
在工程的类路径即 src/main/resources 目录下创建 SpringMVC 的配置文件 springmvc.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"
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">
<!--声明组件扫描器-->
<context:component-scan base-package="com.zwh.controller"/>
<!--声明 springmvc框架中的视图解析器, 帮助开发人员设置视图文件的路径-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀:视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/" />
<!--后缀:视图文件的扩展名-->
<property name="suffix" value=".jsp" />
</bean>
</beans>
1.1.5 创建处理器
//@Controller:创建处理器对象,对象放在springmvc容器中。
@Controller
public class MyController {
/**
* 准备使用doSome方法处理some.do请求。
*
* @RequestMapping: 请求映射,作用是把一个请求地址和一个方法绑定在一起,一个请求指定一个方法处理。
* 属性: 1. value 是一个String,表示请求的uri地址的(some.do)。
* value的值必须是唯一的, 不能重复。 在使用时,推荐地址以“/”
* 位置:1.在方法的上面,常用的。 2.在类的上面
* <p>
* 说明: 使用RequestMapping修饰的方法叫做处理器方法或者控制器方法。
* 使用@RequestMapping修饰的方法可以处理请求的,类似Servlet中的doGet, doPost
* <p>
* 返回值:ModelAndView 表示本次请求的处理结果
* Model: 数据,请求处理完成后,要显示给用户的数据
* View: 视图, 比如jsp等等。
*/
@RequestMapping(value = "/hello.do")
public ModelAndView doSome() {
System.out.println("处理hello请求中");
// 调用service处理请求,把处理结果放入到返回值ModelAndView
ModelAndView mv = new ModelAndView();
// addObject()方法用于向其 Model 中添加数据。Model 的底层为一个 HashMap。
mv.addObject("msg", "使用注解的springMVC应用");
mv.setViewName("hello");
// Model 中的数据存储在 request 作用域中,SringMVC 默认采用转发的方式跳转到视图,本次请求结束,模型中的数据被销毁
return mv;
}
}
1.1.6 声明组件扫描器
<!--声明组件扫描器-->
<context:component-scan base-package="com.zwh.controller"/>
1.1.7 自定义目标页面
这里我们将目标jsp页面都放在web-inf下的view包中,目的是让用户不能直接访问
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>msg数据:${msg}</h1>
</body>
</html>
测试结果:msg数据:使用注解的springMVC应用
1.2 SpringMVC 执行 执行 流程 ( 理解)
流程图:

执行流程简单分析:
- 浏览器提交请求到中央调度器
- 中央调度器直接将请求转给处理器映射器。
- 处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器。
- 中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器
- 处理器适配器调用执行处理器。
- 处理器将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器
- 处理器适配器直接将结果返回给中央调度器。
- 中央调度器调用视图解析器,将 ModelAndView 中的视图名称封装为视图对象
- 视图解析器将封装了的视图对象返回给中央调度器
- 中央调度器调用视图对象,让其自己进行渲染,即进行数据填充,形成响应对象。
- 中央调度器响应浏览器。
2 SpringMVC注解式开发
2.1 @RequestMapping 定义请求规则
@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。
用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
通俗讲就好比商品的增删改:都会有前缀,goods/add goods/delete goods/update
这时候就可以在类上添加@RequestMapping(value = “/goods”)
那么方法上就可以直接@RequestMapping(value = “/add”)@RequestMapping(value = “/update”),但是他们都同时具有了父路径/goods
对请求提交方式的定义:
对于@RequestMapping,其有一个属性 method,就是限制请求是采用post还是get方式
当然,若不指定 method 属性,则无论是 GET 还是 POST 提交方式,均可匹配。即对于请求的提交方式无要求。
- 对请求中携带参数的定义
@RequestMapping 中 params 属性中定义了请求中必须携带的参数的要求。
@RequestMapping(value=”/xxx.do”, params={“name”,”age”}) :要求请求中必须携带请求参数 name 与 age
@RequestMapping(value=”/xxx.do”, params={“!name”,”age”}) :要求请求中必须携带请求参数 age,但必须不能携带参数 name
@RequestMapping(value=”/xxx.do”, params={“name=zs”,”ag=23”}) :要求请求中必须携带请求参数 name,且其值必须为 zs;必须携带参数 age,其其值必须为 23
@RequestMapping(value=”/xxx.do”, params=“name!=zs”) :要求请求中必须携带请求参数name,且其值必须不能为 zs
2.2 接收前端参数及post乱码问题
1、若表单的name属性值与方法参数一致直接取即可
2、若表单的name属性值与方法参数不一致,则在参数前添加@RequestParam(value = “aname”),进行指定name的属性值即可
post请求乱码问题(web.xml):
<!--注册声明过滤器,解决post请求乱码的问题-->
<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>
<!--强制请求对象(HttpServletRequest)使用encoding编码的值-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制应答对象(HttpServletResponse)使用encoding编码的值-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!--
/*:表示强制所有的请求先通过过滤器处理。
-->
<url-pattern>/*</url-pattern>
</filter-mapping>
2.3 处理器方法的返回值
1、 ModelAndView
若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回 ModelAndView 比较好
2、String
返回内部资源逻辑视图名,这个字符串与视图解析器中的 prefix、suffix 相结合,即可形成要访问的 URI。
当然,也可以直接返回资源的物理视图名。不过,此时就不需要再在视图解析器中再配置前辍与后辍了。
3、 object
演示:返回学生对象到浏览器
-
导入 jackson 依赖
<!--Jackson应该是目前比较好的json解析工具了--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency> -
引入 jQuery 库
<script src="js/jquery-3.4.1.js"></script> -
定义 index 页面
<html> <head> <title>$Title$</title> </head> <script src="js/jquery-3.4.1.js"></script> <script type="text/javascript"> $(function () { $("button").click(function () { $.ajax({ url: "/json1.do", success: function (data) { // JSON.stringify将json数据转换成json字符串 document.write(JSON.stringify(data)); } }) }) }) </script> <body> <button>提交ajax请求</button> </body> </html> -
定义对象 Student
@Data public class Student { private String name; private Integer age; } -
修改处理器类 MyController
//在类上直接使用 @RestController ,这样子,里面所有的方法都只会返回 json 字符串了,不用再每一个都添加@ResponseBody @RestController public class MyController { //produces:指定响应体返回类型和编码 @RequestMapping(value = "/json1.do") public String json1() throws JsonProcessingException { //创建一个jackson的对象映射器,用来解析数据 ObjectMapper mapper = new ObjectMapper(); //创建一个对象 Student student = new Student("张三", 10); //将我们的对象解析成为json格式 String str = mapper.writeValueAsString(student); //由于@ResponseBody注解,这里会将str转成json格式返回;十分方便 return str; } } -
springmvc配置文件
<?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 http://www.springframework.org/schema/mvc/spring-mvc.xsd "> <!--注册mvc注解驱动以及处理JSON乱码问题配置--> <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8"/> </bean> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> <property name="failOnEmptyBeans" value="false"/> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> <!--声明组件扫描器--> <context:component-scan base-package="com.zwh.controller"/> <!--声明 springmvc框架中的视图解析器, 帮助开发人员设置视图文件的路径--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀:视图文件的路径--> <property name="prefix" value="/WEB-INF/view/"/> <!--后缀:视图文件的扩展名--> <property name="suffix" value=".jsp"/> </bean> </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">
<servlet>
<servlet-name>myweb</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>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myweb</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!--注册声明过滤器,解决post请求乱码的问题-->
<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>
<!--强制请求对象(HttpServletRequest)使用encoding编码的值-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制应答对象(HttpServletResponse)使用encoding编码的值-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!--
/*:表示强制所有的请求先通过过滤器处理。
-->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
4、 void
对于处理器方法返回 void 的应用场景,主要有两种。
- 通过 ServletAPI 传递数据并完成跳转
通过在处理器方法的参数中放入的 ServletAPI 参数,来完成资源跳转时所要传递的数据及跳转。
可在方法参数中放入 HttpServletRequest 或 HttpSession,使方法中可以直接将数据放入到 request、session 的域中,也可通过 request.getServletContext()获取到 ServletContext,从而将数据放入到 application 的域中。

- AJAX 响应
对若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。
以对象方式整体接收:

单个逐个接收:

jQuery.ajax:
jQuery.ajax(...)
部分参数:
url:请求地址
type:请求方式,GET、POST(1.9.0之后用method)
headers:请求头
data:要发送的数据
contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
async:是否异步
timeout:设置请求超时时间(毫秒)
beforeSend:发送请求前执行的函数(全局)
complete:完成之后执行的回调函数(全局)
success:成功之后执行的回调函数(全局)
error:失败之后执行的回调函数(全局)
accepts:通过请求头发送给服务器,告诉服务器当前客户端课接受的数据类型
dataType:将服务器端返回的数据转换成指定类型
"xml": 将服务器端返回的内容转换成xml格式
"text": 将服务器端返回的内容转换成普通文本格式
"html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
"script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
"json": 将服务器端返回的内容转换成相应的JavaScript对象
"jsonp": JSONP 格式使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数
这里就不做演示,和返回对象类型差不多,只是处理器方法返回值为void,不能表示数据,也没有视图。
所以可以通过httpServletResponse的输出对象,把数据输出到浏览器
5、json输出时间对象
- 默认日期格式会变成一个数字,是1970年1月1日到当前日期的毫秒数!
- Jackson 默认是会把时间转成timestamps形式
解决方法:封装工具类
public class JsonUtils {
public static String getJson(Object object) {
return getJson(object, "yyyy-MM-dd HH:mm:ss");
}
public static String getJson(Object object, String dateFormat) {
ObjectMapper mapper = new ObjectMapper();
//不使用时间差的方式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//自定义日期格式对象
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
//指定日期格式
mapper.setDateFormat(sdf);
try {
return mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
2.4 FastJson
fastjson.jar是阿里开发的一款专门用于Java开发的包,可以方便的实现json对象与JavaBean对象的转换,实现JavaBean对象与json字符串的转换,实现json对象与json字符串的转换。实现json的转换方法很多,最后的实现结果都是一样的。
fastjson 的 pom依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
fastjson 三个主要的类:
- JSONObject 代表 json 对象
- JSONArray 代表 json 对象数组
- JSON 代表 JSONObject和JSONArray的转化
代码测试,我们新建一个MyTest类
public class MyTest {
public static void main(String[] args) {
//创建一个对象
Student Student1 = new Student("1号", 3);
Student Student2 = new Student("2号", 3);
Student Student3 = new Student("3号", 3);
Student Student4 = new Student("4号", 3);
List<Student> list = new ArrayList<Student>();
list.add(Student1);
list.add(Student2);
list.add(Student3);
list.add(Student4);
System.out.println("*******Java对象 转 JSON字符串*******");
String str1 = JSON.toJSONString(list);
System.out.println("JSON.toJSONString(list)==>" + str1);
String str2 = JSON.toJSONString(Student1);
System.out.println("JSON.toJSONString(Student1)==>" + str2);
System.out.println("\n****** JSON字符串 转 Java对象*******");
Student jp_Student1 = JSON.parseObject(str2, Student.class);
System.out.println("JSON.parseObject(str2,Student.class)==>" + jp_Student1);
System.out.println("\n****** Java对象 转 JSON对象 ******");
JSONObject jsonObject1 = (JSONObject) JSON.toJSON(Student2);
System.out.println("(JSONObject) JSON.toJSON(Student2)==>" + jsonObject1.getString("name"));
System.out.println("\n****** JSON对象 转 Java对象 ******");
Student to_java_Student = JSON.toJavaObject(jsonObject1, Student.class);
System.out.println("JSON.toJavaObject(jsonObject1, Student.class)==>" + to_java_Student);
}
}
2.5 解读<url-pattern/>
1、配置详解
-
*.do
在没有特殊要求的情况下,SpringMVC 的中央调度器 DispatcherServlet 的
<url-pattern/>常使用后辍匹配方式,如写为*.do 或者 *.action, *.mvc 等 -
/
可以写为/,因为 DispatcherServlet 将会向静态资源的获取请求,例如.css、.js、.jpg、.png等资源的获取请求,当作是一个普通的 Controller 请求。中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报 404 错误.
*保持<url-pattern/>的值为 .do,扩展名方式,图片会正常显示,将<url-pattern/>的值修改为 / ,则图片将无法显示。
2、静态资源访问
<url-pattern/>的值并不是说写为/后,静态资源就无法访问了。经过一些配置后,该问题也是可以解决的。
方法一:
在web.xml中配置:
<!--处理静态资源访问-->
<mvc:default-servlet-handler/>
方法二:
在web.xml中配置:
<mvc:resources mapping="/img/**" location="/img/"/>
location 表示静态资源所在目录。当然,目录不要使用/WEB-INF/及其子目录
mapping 表示对该资源的请求
2.6 校正请求参数名@RequestParam
所谓校正请求参数名,是指若请求 URL 所携带的参数名称与处理方法中指定的参数名不相同时,则需在处理方法参数前,添加一个注解@RequestParam(“请求参数名”),指定请求 URL 所携带参数的名称。该注解是对处理器方法参数进行修饰的。
@RequestParam()有三个属性:
- value:指定请求参数的名称。
- required:指定该注解所修饰的参数是否是必须的,boolean 类型。若为 true,则表示请求中所携带的参数中必须包含当前参数。若为 false,则表示有没有均可。
- defaultValue:指定当前参数的默认值。若请求 URI 中没有给出当前参数,则当前方法参数将取该默认值。即使 required 为 true,且 URI 中没有给出当前参数,该处理器方法参数会自动取该默认值,而不会报错。
2.7 路径变量@PathVariable
对于处理器方法中所接收的请求参数,可以来自于请求中所携带的参数,也可以来自于请求的 URI 中所携带的变量,即路径变量。
不过,此时,需要借助@PathVariable 注解。
@PathVariable 在不指定参数的情况下,默认其参数名,即路径变量名与用于接收其值的属性名相同。
若路径变量名与用于接收其值的属性名不同,则@PathVariable 可通过参数指出路径变量名称。
@RequestMapping("/{pname}/{age}/register.do")
public MOdelAndView register(@pathVariable("pname") String name,
@pathVariable int age){
}
3 springmvc核心技术
3.1 请求重定向和转发
3.1.1 返回 ModelAndView 时的请求转发
- 请求转发到其它 Controller
mv.setViewName("forward:other.do")
- 请求转发到页面
mv.setViewName("forward:/WEB-INF/jsp/hello.jsp")
重定向使用redirect
注意:
对于请求转发的页面,可以是WEB-INF中页面;
而重定向的页面,是不能为WEB-INF中的页面。
因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的。
3.2 异常处理
3.2.1 SimpleMappingExceptionResolver 异常处理器
该方式只需要在 SpringMVC 配置文件中注册该异常处理器 Bean 即可。该 Bean 比较特殊,没有 id 属性,无需显式调用或被注入给其它<bean/>,当异常发生时会自动执行该类。
当请求参数的值与接收该参数的处理器方法形参的类型不匹配时,会抛出类型匹配有误异常 TypeMismatchException。
- 自定义异常类
// 其他异常类的父类
public class MyStudentException extends Exception {
public MyStudentException() {
super();
}
public MyStudentException(String message) {
super(message);
}
}
// 名字异常类
public class NameException extends MyStudentException {
public NameException() {
super();
}
public NameException(String message) {
super(message);
}
}
// 年龄异常类
public class AgeException extends MyStudentException {
public AgeException() {
super();
}
public AgeException(String message) {
super(message);
}
}
- 修改 Controller
@RequestMapping(value = "/exception.do")
public ModelAndView doSome(String name, Integer age) throws MyStudentException {
ModelAndView mv = new ModelAndView();
//根据请求参数抛出异常
if (!"张三".equals(name)) {
throw new NameException("姓名不正确!!!");
}
if (age == null || age > 80) {
throw new AgeException("年龄比较大!!!");
}
mv.addObject("myname", name);
mv.addObject("myage", age);
mv.setViewName("show");
return mv;
}
- 注册异常处理器
// 配置异常处理器
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="com.zwh.exception.NameException">nameError</prop>
<prop key="com.zwh.exception.AgeException">ageError</prop>
</props>
</property>
<property name="defaultErrorView" value="defaultErr"/>
<property name="exceptionAttribute" value="ex"/>
</bean>
- 定义异常响应页面
3.2.2 自定义异常处理器
使用 SpringMVC 定义好的 SimpleMappingExceptionResolver 异常处理器,可以实现发生指定异常后的跳转。
但若要实现在捕获到指定异常时,执行一些操作的目的,它是完成不了的。此时,就需要自定义异常处理器。
自定义异常处理器,需要实现HandlerExceptionResolver接口,并且该类需要在SpringMVC配置文件中进行注册。
- 定义异常处理器
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView mv = new ModelAndView();
// 将异常对象加入到数据模型中
mv.addObject("ex", e);
// 设置默认错误响应页面
mv.setViewName("defaultError");
// 设置name异常
if (e instanceof NameException) {
mv.setViewName("nameError");
}
// 设置age异常
if (e instanceof AgeException) {
mv.setViewName("ageError");
}
return mv;
}
}
- 注册异常处理器
<!--注册异常处理器-->
<bean class="com.zwh.exception.MyExceptionResolver"/>
3.2.3 @ExceptionHandler 注解
使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。
该注解只有一个可选属性 value,为一个 Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。
而被注解的方法,其返回值可以是 ModelAndView、String,或 void,方法名随意参数可以是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值。
- 定义全局异常处理类
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception exception) {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "姓名必须是张三,其它用户不能访问");
mv.addObject("ex", exception);
mv.setViewName("nameError");
return mv;
}
@ExceptionHandler(value = AgeException.class)
public ModelAndView doAgeException(Exception exception) {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "你的年龄不能大于80");
mv.addObject("ex", exception);
mv.setViewName("ageError");
return mv;
}
//处理其它异常, NameException, AgeException以外,不知类型的异常
@ExceptionHandler
public ModelAndView doOtherException(Exception exception) {
//处理其它异常
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "others 异常");
mv.addObject("ex", exception);
mv.setViewName("defaultError");
return mv;
}
}
- 定义 Spring 配置文件
// 注册组件扫描器
<context:component-scan base-package="com.zwh.exception"/>
// 注册注解驱动
<mvc:annotation-driven />
3.2 拦截器
3.2.1 拦截器的介绍
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
- 过滤器
过滤器依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。
使用过滤器的目的,是用来做一些过滤操作,获取我们想要获取的数据,比如:在Javaweb中,对传入的request、response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者Controller进行业务逻辑操作。
通常用的场景是:在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符等。
- 拦截器
拦截器依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。
在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在service或者一个方法前,调用一个方法,或者在方法后,调用一个方法,比如动态代理就是拦截器的简单实现,在调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在调用方法后打印出字符串,甚至在抛出异常的时候做业务逻辑的操作。
由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。
但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。
3.2.2 拦截器的实现
- 自定义拦截器
public class MyInterceptor implements HandlerInterceptor {
//在请求处理的方法之前执行
//如果返回true执行下一个拦截器
//如果返回false就不执行下一个拦截器
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
System.out.println("------------preHandle处理前------------");
String loginName = "";
// 从session中获取name值
Object name = request.getSession().getAttribute("name");
if (name != null) {
loginName = (String) name;
}
// 判断登陆的账户是否符合要求
if (!"zs".equals(loginName)) {
// 不能够访问系统
request.getRequestDispatcher("/fail.jsp").forward(request, response);
return false;
}
return true;
}
//在请求处理方法执行之后执行
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
// 对原来的dosome执行结果进行二次调整
// if (modelAndView != null) {
// // 改变数据
// modelAndView.addObject("madate", new Date());
// // 改变视图
// modelAndView.setViewName("other");
// }
System.out.println("------------postHandle处理后------------");
}
//在dispatcherServlet处理后执行,做清理工作.
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("------------afterCompletion清理------------");
}
}
- 注册拦截器
<!--关于拦截器的配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--/** 包括路径及其子路径-->
<!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
<!--/admin/** 拦截的是/admin/下的所有-->
<mvc:mapping path="/**"/>
<!--bean配置的就是拦截器-->
<bean class="com.zwh.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
3.3 文件上传
- 导入文件上传的jar包
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<!--servlet-api导入高版本的-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
- 编写前端页面
<form action="upload.do" enctype="multipart/form-data" method="post">
文件: <input type="file" name="file"/>
<input type="submit" value="上传">
</form>
<a href="download.do">点击下载</a>
- 配置bean
<!--文件上传配置-->
<!--这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以 便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件大小上限,单位为字节(10485760=10M) -->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
- Controller
/*
* 采用file.Transto 来保存上传的文件
*/
@RequestMapping("/upload.do")
public String fileUpload2(@RequestParam("file") MultipartFile photo) throws IOException {
if (!photo.isEmpty()) {
photo.transferTo(new File("D:\\IDEA\\springmvc-code\\springmvc-05-file\\web\\img\\" + photo.getOriginalFilename()));
return "success";
}
return "fail";
}
3.4 Restful风格
3.4.1 Restful介绍
- 传统方式操作资源
http://127.0.0.1/item/queryUser.action?id=1 查询,GET
http://127.0.0.1/item/saveUser.action 新增,POST
http://127.0.0.1/item/updateUser.action 更新,POST
http://127.0.0.1/item/deleteUser.action?id=1 删除,GET或POST
- rest风格操作资源
【GET】 /users # 查询用户信息列表
【GET】 /users/1001 # 查看某个用户信息
【POST】 /users # 新建用户信息
【PUT】 /users/1001 # 更新用户信息(全部字段)
【PATCH】 /users/1001 # 更新用户信息(部分字段)
【DELETE】 /users/1001 # 删除用户信息
分析:
传统方式你每次请求的接口或者地址,都在做描述,例如查询的时候用了queryUser,新增的时候用了saveUser ,修改的时候用了updateUser,其实完全没有这个必要。
我使用了get请求,就是查询,使用post请求,就是新增的请求,PUT就是修改,delete就是删除,我的意图很明显,完全没有必要做描述,这就是为什么有了restful。
3.4.2 springmvc实现rest风格
- 前端页面
<form action="user.do" method="get">
<input type="submit" value="REST-GET 提交">
</form>
<form action="user.do" method="post">
<input type="submit" value="REST-POST 提交">
</form>
<form action="user.do" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="REST-DELETE 提交">
</form>
<form action="user.do" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="REST-PUT 提交">
</form>
- web.xml
<!--Restful: HiddenHttpMethodFilter过滤器可以将POST请求转化为put请求和delete请求-->
<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>
- controller
@Controller
public class RestFulController {
@GetMapping("/user.do")
@ResponseBody
public String getUser() {
return "GET-张三";
}
@PostMapping("/user.do")
@ResponseBody
public String saveUser() {
return "POST-张三";
}
@PutMapping("/user.do")
@ResponseBody
public String putUser() {
return "PUT-张三";
}
@DeleteMapping(value = "/user.do")
@ResponseBody
public String deleteUser() {
return "DELETE-张三";
}
}
本文介绍了SpringMVC注解式开发的基础知识,包括搭建环境、处理请求、接收参数、异常处理等内容,帮助读者快速掌握SpringMVC的核心概念和技术。
1972

被折叠的 条评论
为什么被折叠?



