1.SpringMVC
1.1.概述
SpringMVC 也叫 Spring web mvc。是 Spring 框架的一部分,是在 Spring3.0 后发布的
SpringMVC的底层本质就是servlet
1.2.优势
-
基于 MVC 架构
基于 MVC 架构,功能分工明确。解耦合
-
容易理解,上手快;使用简单
-
Spring MVC通过一套MVC注解,让 POJO 成为处理请求的控制器,而无须实现任何接口
-
作 为Spring框 架 一 部 分 , 能 够 使 用Spring的IoC和Aop。方 便 整 合Strtus,MyBatis,Hiberate,JPA等其他框架
1.3.请求流程
对工作原理的解释
- 用户发送请求到springmvc框架提供的DispatcherServlet 这个前端控制器(中央调度器)
- 前端控制器会去找处理器映射器(HandlerMapping),处理器映射器根据请求url找到具体的后端控制器(Handler),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
- 根据处理器映射器返回的后端控制器(Handler),DispatcherServlet 会找“合适”的处理器适配器(HandlerAdapter)
- 处理器适配器HandlerAdpater会去执行后端控制器(Handler开发的时候会被叫成Controller也叫后端控制器) 执行之前会有转换器、数据绑定、校验器等等完成上面这些才会去正在执行Handler
- 后端控制器Handler执行完成之后返回一个ModelAndView对象
- 处理器适配器HandlerAdpater会将这个ModelAndView返回前端控制器DispatcherServlet。前端控制器会将ModelAndView对象交给视图解析器ViewResolver
- 视图解析器ViewResolver解析ModelAndView对象之后返回逻辑视图
前端控制器(DispatcherServlet)对逻辑视图进行渲染(数据填充)之后返回真正的物理View并响应给浏览器
2.SpringMVC入门程序
编写第一个springMVC的HelloWorld程序
2.1.准备工程
2.1.1.创建maven-web工程
2.1.2.添加web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
</web-app>
2.1.3.添加依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>
<!-- java web begin -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!-- java web end -->
</dependencies>
2.1.4.统一编码
在springMVC中有自带统一编码相关的过滤器类, 可以在web.xml文件中进行配置
<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>
<init-param>
<!--初始化强制编码-->
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.1.5.前端控制器
用户请求首先到达前端控制器, DispatherServlet是整个流程的核心,它来调用其他组件来处理用户的请求,前端控制器的存在降低了其他组件之间的耦合度
就是一个servlet,所有的请求都都先到它,然后再这个servlet取调用真正的业务代码来处理请求
在web.xml添加前端控制器配置
<!--配置前端拦截器-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--contextConfigLocation:指定springmvc配置的加载位置,
如果不指定则默认加 载WEB-INF/***-servlet.xml(例如springmvc-servlet.xml)。
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 可以拦截二种请求
第一种:拦截固定后缀的url,比如设置为 *.do、*.action,
例如:/user/add.action 此方法最简单,不会导致静态资源(jpg,js,css)被拦截
第二种:拦截所有 设置为/
例如:/user/add /user/add.action此方法可以实现REST风格的url
很多互联网类型的应用使用这种方式.
但是此方法会导致静态文件(jpg,js,css)被拦截后不能正常显示.需要特殊处理
错误设置:拦截所有,设置为/*,此设置方法错误,因为请求到Action,
当action转到jsp时再次被拦截,提示不能根据jsp路径mapping成功.
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
2.1.6.处理器映射器
它的作用就好比去看电影要拿着电影票根据电影票上面的座位号找到座位
其中座位就是后端处理器,电影票以及上面的座位号就是URL路径
处理器映射器负责根据用户请求找到Handler即后端控制器,
springmvc提供了不同的处理器映射器实现不同的映射方式
例如:配置文件方式,实现接口方式,注解方式等
在springmvc.xml的配置文件中配置如下
<!-- 处理器映射器 -->
<!--根据bean的name进行查找后端处理器 将请求的url配置在bean的name中 -->
<!-- 这是默认的映射处理器,可以不配置 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- BeanNameUrlHandlerMapping 将bean的name作为url路径 -->
2.1.7.处理器适配器(HandlerAdapter)
通过HandlerAdapter对后端控制器进行适配,处理器适配器HandlerAdpater会去执行后端控制器(Controller也叫后端控制器)
<!-- HandlerAdapter适配器 -->
<!-- 这是默认的适配器,即使不配置,那么也是默认就是这个-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
2.1.8.后端控制器(Handler)
在前端控制器的控制下后端控制器对具体的用户请求进行处理,Handler涉及到具体的用户业务请求,所以需要程序员根据业务需求开发
开发Controller需要实现org.springframework.web.servlet.mvc.Controller接口
HelloController类
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest Request, HttpServletResponse Response) throws Exception {
ModelAndView mv = new ModelAndView();
//和Request.setAttribute();一个效果
mv.addObject("helloworld", "hello Springmvc!");
//设置返回视图的路径名
mv.setViewName("/WEB-INF/JSP/hello.jsp");
return mv;
}
}
JSP视图类
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${helloworld}
</body>
</html>
springmvc-config.xml
<!-- 将控制类交由spring容器管理, name属性即是访问路径-->
<bean name="/hello" class="cn.spmvc.controller.HelloController"/>
可以开始配置Tomcat进行第一轮访问测试
2.1.9.视图解析器(ViewResolver)
ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户
<!-- 配置视图解析器 -->
<!-- InternalResourceViewResolver:支持JSP视图解析 -->
<!-- viewClass:JstlView表示JSP模板页面需要使用JSTL标签库,所以classpath中必须包含jstl的相关jar包; -->
<!-- prefix 和suffix:查找视图页面的前缀和后缀,最终视图的址为: -->
<!-- 前缀+逻辑视图名+后缀,逻辑视图名需要在controller中返回ModelAndView指定,比如逻辑视图名为hello,-->
<!-- 则最终返回的jsp视图地址 "WEB-INF/view/hello.jsp" -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/JSP/"/>
<property name="suffix" value=".jsp"/>
</bean>
修改Controller类的返回视图为
//设置返回视图的路径名
mv.setViewName("hello");
SpringMVC的MVC组件
2.2.内容总结
到以上快速入门就完成了,我们主要进行了如下配置:
1、 前端控制器DispatcherServlet
(在web.xml文件中配置,只需要配置一次);
2、 处理器映射器HandlerMapping
(可不配置,使用默认)
3、 处理器适配器HandlerAdapter
(可不配置,使用默认)
4、 处理器(后端控制器)Handler
(需要编程开发和配置)
5、 视图解析器ViewResolve
r(只需要配置一次)
6、 视图(需要编程开发)
3.SpringMVC注解开发
3.1.文件配置
注解扫描
和spring中使用注解一样, 如果在springMVC中要使用注解,一样要注解扫描,扫描指定的package
<!-- 注解扫描!!!-->
<context:component-scan base-package="cn.springmvc.controller"/>
注解驱动
<!-- springmvc使用<mvc:annotation-driven/>
自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter
可以用使用<mvc:annotation-driven/>替代注解处理器和适配器的配置
-->
<mvc:annotation-driven/>
最终配置
<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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 注解扫描!!!-->
<context:component-scan base-package="cn.springmvc.controller"/>
<!--注解驱动-->
<!-- springmvc使用<mvc:annotation-driven/>
自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter
可以用使用<mvc:annotation-driven/>替代注解处理器和适配器的配置
-->
<mvc:annotation-driven/>
<!-- 配置视图解析器 -->
<!-- InternalResourceViewResolver:支持JSP视图解析 -->
<!-- viewClass:JstlView表示JSP模板页面需要使用JSTL标签库,所以classpath中必须包含jstl的相关jar包; -->
<!-- prefix 和suffix:查找视图页面的前缀和后缀,最终视图的址为: -->
<!-- 前缀+逻辑视图名+后缀,逻辑视图名需要在controller中返回ModelAndView指定,比如逻辑视图名为hello,-->
<!-- 则最终返回的jsp视图地址 "WEB-INF/view/hello.jsp" -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/JSP/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
3.2.常见注解解析
3.2.1.@RequestMapping 定义请求规则
通过@RequestMapping 注解可以定义处理器对于请求的映射规则。该注解可以注解在方法上,也可以注解在类上,但意义是不同的。value 属性值常以“/”开始。
@RequestMapping 的 value 属性用于定义所匹配请求的 URI。
修改的后端控制器如下
@Controller
public class HelloAnnotationController {
@RequestMapping("/helloAnnoation")
public String helloSpringmvc(Model model){
model.addAttribute("hello", "Hello springmvc Annoation");
// 返回值发生改变
return "hello";
}
}
对于@RequestMapping,其有一个属性 method,用于对被注解方法所处理请求的提交方式进行限制,即只有满足该 method 属性指定的提交方式的请求,才会执行该被注解方法
Method 属性的取值为 RequestMethod 枚举常量。常用的为 RequestMethod.GET 与RequestMethod.POST,分别表示提交方式的匹配规则为 GET 与 POST 提交。
常见的几种请求的处理方式
3.2.2.处理器方法的返回值
使用@Controller 注解的处理器的处理器方法,其返回值常用的有四种类型
- ModelAndView
- String
- 无返回值 void
- 返回自定义类型对象
根据不同的情况,使用不同的返回值
返回 ModelAndView
若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回 ModelAndView 比较好。但是像我们常用的ajax实际上只接收数据不需要跳转的就不合适。
返回 String
处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物Java 框架 SpringMVC物理视图地址
返回 void(了解)
对于处理器方法返回 void 的应用场景,AJAX 响应。
若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。
返回对象 Object
处理器方法也可以返回 Object 对象。这个 Object 可以是 Integer,String,自定义对象,Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的
返回对象,需要使用==@ResponseBody 注解,将转换后的 JSON 数据放入到响应体中==(springMVC的json转换默认采用jackson后续会说)
将 Object 数据转化为 JSON 数据,需要由消息转换器 HttpMessageConverter 完成。而转换器的开启,需要由<mvc:annotation-driven/>
来完成
使用json数据转换注解需导入相关依赖
<!--springmvc-json依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
3.2.3.关于接收参数
默认请求接收
在SpringMVC中默认的请求接收方式是根据请求URL中的参数名来决定的。意味着请求链接中参数名与Controller类中方法的参数名相同即可自动封装
校正请求参数名@RequestParam
所谓校正请求参数名,是指若请求 URL 所携带的参数名称与处理方法中指定的参数名不相同时,则需在处理方法参数前,添加一个注解@RequestParam(“请求参数名”),指定请求 URL 所携带参数的名称。
- value:参数名
- required:是否必须。默认为 true, 表示请求参数中必须包含对应的参数,若不存在,将抛出异常
使用 @RequestHeader 绑定请求报头的属性值
请求头包含了若干个属性,服务器可据此获知客户端的信息,通过@RequestHeader 即可将请求头中的属性值绑定到处理方法的入参中
使用 POJO 对象绑定请求参数值
SpringMVC会根据自定义对象的字段名(成员属性名)来和请求URL中的参数名自动进行匹配绑定,自动为该对象填充属性。
常见的ServletAPI作为绑定参数
因为springMVC的底层本质是servlet,所以很多ServletAPI中的相关类都可以进行自动绑定,如下:
- HttpServletRequest
- HttpServletResponse
- HttpSession
- java.security.Principal
- Locale
- InputStream
- OutputStream
- Reader
- Writer
3.2.4.关于重定向
一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图名处理
如果返回的字符串中带 forward: 或 redirect: 前缀 时,SpringMVC 会对他们进行特殊处理:将 forward:
和redirect:
当成指示符,其后的字符串作为 URL 来处理
– redirect:success.jsp:会完成一个到 success.jsp 的重定向的操作
– forward:success.jsp:会完成一个到 success.jsp 的转发操作
3.3.实战应用
3.3.1.需求
开发一个StudentConrtoller控制器
/stu/list GET请求 跳转到学生列表(请求转发)
/stu/add GET请求 跳转到学生新增页面(请求转发)
/stu/add POST请求 保存学生信息,保存成功后跳转到列表页面(重定向)
/stu/update?id=10 GET请求 跳转到学生修改页面(请求转发)
/stu/update POST请求 保存学生信息,保存成功后跳转到列表页面(重定向)
/stu/delete?id=10 GET 请求 删除学生信息,删除成功跳转到列表页面(重定向)
3.3.2.预备工作
引入预先准备好的静态资源
学生entity实体类
public class Student {
private Integer stuSno;
private String stuSname;
private String stuSage;
private Date stuBirthday;
}
3.3.3.学生列表
在后台添加虚拟数据即可让前台页面拥有显示效果
@Controller
@RequestMapping("/stu")
public class StudentController {
/**
* list列表页面
* @param model
* @return
*/
@RequestMapping("/list")
public String list(Model model){
//模拟数据对象
Student stu1 = new Student(1, "张三", "18", new Date());
Student stu2 = new Student(1, "李四", "19", new Date());
Student stu3 = new Student(1, "王五", "20", new Date());
//封装数据对象
ArrayList<Student> list = new ArrayList<>();
list.add(stu1);
list.add(stu2);
list.add(stu3);
//加入到模型中
model.addAttribute("list", list);
//返回到指定视图
return "stu_list";
}
}
3.3.4.页面日期格式化
对日期类型,在页面展示的时候要进行日期格式化
引入jstl的格式化标签库和核心标签库
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
日期格式化标签
<fmt:formatDate value="${stu.stuBirthday }" pattern="yyyy-MM-dd HH:mm:ss"/>
3.3.5.添加学生
跳转到新增学生页面(直接写相对路径会报错如下)
<a href="user/add" class="btn btn-primary">新增</a>
问题原因:从/user/list 视图重定向,返回的是/user路径下的资源,但/user/add和/user/list是同一目录级别的资源。
解决:url前面加上项目地址相对tomcat容器的上下文路径,
如果发布地址是根目录 项目路径是 / (根据项目的实际路径来)
如何获取上下文路径?
El:表达式:${ pageContext.request.contextPath }
3.3.5.1.解决静态资源问题
给静态资源的路径也配上上下文路径的时候仍然无法加载前端资源,出现以上错误ERR_ABORTED:表示静态资源可以访问到了,但没权限
原因:web.xml中 dispatcherServlet配置了对所有资源进行请求转发
因为 DispatcherServlet 会将向静态资源的获取请求,==例如.css、.js、.jpg、.png等资源的获取请求,当作是一个普通的 Controller 请求。==中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报 404 错误。
方案一
使用<mvc:default-servlet-handler/>
,只需要在 springmvc.xml 中添加<mvc:default-servlet-handler/>
标签即可
<mvc:default-servlet-handler/>
声 明 了 <mvc:default-servlet-handler /> 后 , springmvc 框 架 会 在 容 器 中 创 建DefaultServletHttpRequestHandler 处理器对象。它会像一个检查员,对进入 DispatcherServlet的 URL 进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的Servlet 处理。一般的服务器都有默认的 Servlet。
在 Tomcat 中,有一个专门用于处理静态资源访问的 Servlet 名叫 DefaultServlet。其为 default。可以处理各种静态资源访问请求。该 Servlet 注册在 Tomcat 服务器的 web.xml 中。在 Tomcat 安装目录/conf/web.xml。
方案二
使用<mvc:resources/>
,需要在 springmvc 配置文件中添加如下形式的配置
<!-- 映射静态资源 方案2 -->
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/fonts/" mapping="/fonts/**"/>
<mvc:resources location="/plugin/" mapping="/plugin/**"/>
location
: 表示静态资源所在目录。当然,目录不要使用/WEB-INF/及其子目录。
mapping
: 表 示 对 该 资 源 的 请 求 (是在哪个目录下的资源)。注意,后面是两个星号******。
3.3.6.保存学生
/**
* 请求add页面
*/
@RequestMapping(value = "/add",method = RequestMethod.GET)
public String add(){
return "stu_add";
}
/**
* 保存新增信息
*/
@RequestMapping(value = "/add",method = RequestMethod.POST)
public String add(Student student){
System.out.println(student);
return "redirect:/stu/list";
}
正常保存信息写法如上,但是在使用过程中会出现异常
原因
无法解析Date的日期格式,想要后台能够看见异常需要引入log4j日志。
添加依赖
<!-- log4j 日志工具 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
配置文件
#全局配置
log4j.rootLogger=DEBUG,console
#debug 测试阶段使用
#控制台日志输出
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%p] %m %n
解决方案
对于日期的解析在springmvc框架中也有提供,可以在需要使用日期的实体类属性上添加 @DateTimeFormat
注解,可以对java.util.Date、java.util.Calendar、java.long.Long时间类型进行标注。
- pattern: 日期的解析格式,指定解析/格式化字段数据的模式,如yyyy-MM-dd
可以自主补充完成修改学生与删除学生
4.Restful支持
4.1.什么是Restful?
Restful就是一个==资源定位及资源操作的风格。==不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
**资源:**互联网所有的事物都可以被抽象为资源
**资源操作:**使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。 分别对应 添加、 删除、修改、查询。
4.1.1.不同操作方式的对比
传统方式操作资源
http://127.0.0.1/item/queryItem.action?id=1
查询,GET
http://127.0.0.1/item/saveItem.action
新增,POST
http://127.0.0.1/item/updateItem.action
更新,POST
http://127.0.0.1/item/deleteItem.action?id=1
删除,GET或POST
Restful风格操作资源
http://127.0.0.1/item/1
查询,GET
http://127.0.0.1/item
新增,POST
http://127.0.0.1/item
更新,PUT
http://127.0.0.1/item/1
删除,DELETE
4.2.实战操作
利用restful风格开发一个产品管理接口(ProductController),具体需求如下:
/product/list
GET请求 获取商品信息 返回json数据
/product/get/商品id /product/10
GET请求 根据商品ID获取商品明细信息 返回json数据
/api/product/add
POST请求 添加商品信息,发送json数据,返回处理结果的JSON数据(R对象)
/api/product/delete/商品id
GET请求 根据商品ID删除商品信息,返回处理结果的JSON数据(R对象)
4.2.1.准备工作
商品实体类
public class Product implements Serializable {
//主键
private Integer productId;
//商品名称
private String productName;
//商品价格
private Integer productPrice;
//商品描述
private String productDescription;
//创建日期
private Date createTime;
}
需要用到json的数据转换处理要导入相关的jackson依赖
<!--spring-json依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
返回结果封装类
public class ResultMap {
private String msg;
private Integer code;
private Object data;
public ResultMap() {
}
public ResultMap(String msg, Integer code, Object data) {
this.msg = msg;
this.code = code;
this.data = data;
}
public static ResultMap ok(Object data){
return new ResultMap("成功",200,data);
}
public static ResultMap fail(Object data){
return new ResultMap("失败",500,data);
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Integer getcode() {
return code;
}
public void setcode(Integer code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
4.2.2.其他注解介绍
处理日期
在处理日期的注解中除了我们之前介绍过的@DateTimeFormat
,用来将普通的日期字符串转换成日期对象。还有一个注解也是用来处理和日期相关的操作。
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)
用于json数据中日期对象和日期字符串之间转换,可以将json请求数据中的日期字符串转换成日期对象,并且也可以将响应结果中的日期对象再转换成JSON日期字符串。
接受json数据类型的参数
@RequestBody
在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。
注意细节:
如果某个参数没有value的话,在传json字符串给后端时,要么干脆就不把该字段写到json字符串中;要么写value时, 必须有值,null 或""都行,不能什么都不写。
{
"status": , #错误
"student": null, #正确
"name": "" #正确
}
请求规则快速处理
@GetMapping
等价于 @RequestMapping(value = “/list”,method = RequestMethod.GET)
@PostMapping
等价于 @RequestMapping(value = “/list”,method = RequestMethod.POST)
@RestController
这个注解标注在类上等于类中所有方法相当于都有了@ResponseBody注解
4.2.3.关于接收URL参数
以下关于@PathVariable介绍
后端接收参数有两种方式,一种是 GET ,一种是 POST ,这两种方式都是向一个 URL 传参 GET 方式体现到了地址栏里,POST 方式将内容放在了 body 里。
@RequestParam
和 @PathVariable
注解是用于从 request 中接收请求的,两个都可以接收参数,关键点不同的是@RequestParam
是从 request 里面拿取值,而 @PathVariable
是从一个URI模板里面来填充。
案例
如果我们访问的接口以restful风格书写,资源路径如下
proudct/get/{productId}
/**
* 模拟查询指定数据
*/
@ResponseBody
@RequestMapping("/get/{productId}")
public ResultMap get(@PathVariable Integer productId){
//模拟返回数据
Product product = new Product(productId, "查询商品", 100, "查询测试", new Date());
//返回数据
return ResultMap.ok(product);
}
注意:方法中参数名要与{}中的参数名一致
4.2.4.产品控制类
产品控制类具体代码
@Controller
@RequestMapping("/product")
public class ProductController {
/**
* 模拟查询list集合
*/
@GetMapping("/list")
@ResponseBody
public List<Product> list(){
//模拟返回数据
List<Product> list=new ArrayList<>();
Product product = new Product(1, "商品1", 100, "第一个商品", new Date());
Product product2 = new Product(2, "商品2", 200, "第二个商品", new Date());
//添加数据到集合中
list.add(product);
list.add(product2);
//返回集合
return list;
}
/**
* 模拟查询指定数据
*/
@ResponseBody
@RequestMapping("/get/{productId}")
public ResultMap get(@PathVariable Integer productId){
//模拟返回数据
Product product = new Product(productId, "查询商品", 100, "查询测试", new Date());
//返回数据
return ResultMap.ok(product);
}
/**
* 模拟新增数据
* 接受前台传送的json信息
*/
@ResponseBody
@PostMapping("/add")
public ResultMap add(@RequestBody Product product){
return ResultMap.ok(product);
}
}
5.文件上传
Spring MVC 为文件上传提供了直接的支持,这种支持是通过即插即用的 MultipartResolver 实现的。Spring 用Jakarta Commons FileUpload 技术实现了一个MultipartResolver 实现类:CommonsMultipartResovler
SpringMVC中默认是没有装配MultipartResovler类的,如果需要使用的SpringMVC中的上传文件需要手动进行配置
5.1.相关案例
5.1.1.相关依赖
导入相关的maven依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
5.1.2.配置文件
配置文件上传解析类CommonsMultipartResolver
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--允许上传的文件最大大小 单位是byte-->
<property name="maxUploadSize" value="60000000"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
可配置属性:defaultEncoding
如果配置则必须和用户 JSP 的 pageEncoding 属性一致,以便正确解析表单的内容
5.1.3.JSP页面
前台页面需要注意的是表单提交的enctype
属性需要进行配置
<body>
<form method="post" action="${ctx}/upload" enctype="multipart/form-data" id="data-form" >
<input type="file" name="uploadFile"><br>
<button>提交</button>
</form>
</body>
5.2.文件上传场景
5.2.1.单文件上传
关于单文件上传的后台代码如下
@RequestMapping("/upload")
@ResponseBody
public ResultMap upload(MultipartFile uploadFile){
try {
System.out.println(uploadFile.getName());//表单中的name名
System.out.println(uploadFile.getSize());//文件大小
System.out.println(uploadFile.getOriginalFilename());//获取文件名
//创建文件存储路径
File file = new File("D:/" + uploadFile.getOriginalFilename());
//保存文件到磁盘
uploadFile.transferTo(file);
return ResultMap.ok("上传文件成功");
} catch (IOException e) {
e.printStackTrace();
return ResultMap.fail("上传文件失败!");
}
}
参数直接为MultipartFile对象即可
5.2.2.多文件上传
因为多文件上传,那么数据流将会被以数组的方式进行存储,所以相关的后台代码如下
@RequestMapping("/upload2")
@ResponseBody
public ResultMap upload(MultipartFile[] uploadFile){
try {
for (MultipartFile multipartFile : uploadFile) {
//保存文件
File file = new File("D:/" + multipartFile.getOriginalFilename());
multipartFile.transferTo(file);
}
return ResultMap.ok("上传文件成功");
} catch (IOException e) {
e.printStackTrace();
return ResultMap.fail("上传文件失败!");
}
}
6.JSR303数据校验
6.1.什么是JSR303?
JSR是Java Specification Requests的缩写,意思是Java 规范提案。 是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。
JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。
6.2.相关的约束注解
Bean Validation 中内置的 constraint
Hibernate Validator 附加的 constraint
6.3.使用案例
在Spring MVC中,可直接通过注解驱动的方式进行数据校验
<mvc:annotation-driven/>
会默认装配好一个LocalValidatorFactoryBean
,通过在处理方法的入参上标注@valid注解即可让 Spring MVC 在完成数据绑定后执行数据校验的工作
6.3.1.导入相关依赖
Spring本身并没有直接支持JSR303的相关依赖,所以使用JSR303约束校验需要导入相关依赖
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
6.3.2.实战
实体类
public class Student {
@NotNull
private String name;
@Length(max = 2)
private String sex;
@Max(120)
@Min(18)
private Integer age;
//忽略getter setter 方法
}
写一个简单的数据校验控制类
@Controller
public class ValiDatorController {
@RequestMapping("/test")
@ResponseBody
public String valiMethod(@Valid Student student) {
return student.toString();
}
}
如果数据参数错误,则查看异常
链接案例:http://localhost:8080/test?sex=123&name=abc
,此连接在年龄不符合校验标准,所以异常会报错大致如下:
Field error in object 'student' on field 'sex': rejected value [123]; codes [Length.student.sex,Length.sex,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.sex,sex]; arguments []; default message [sex],2,0]; default message [长度需要在0和2之间]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:115)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
注意:如果想要有日志的异常打印的效果需要将日志相关配置导入项目才可以成功显示
7.SSM整合
SSM 编程,即 SpringMVC + Spring + MyBatis 整合,是当前最为流行的 JavaEE 开发技术架构。其实 SSM 整合的实质,仅仅就是将 MyBatis整合入 Spring。因为 SpringMVC原本就是 Spring的一部分,不用专门整合。
整合思路
- 导入相关依赖
- 配置文件
- web.xml中完成对spring配置文件加载的监听
- 完成sm的整合
- 完成整合后的项目测试
7.1.搭建开发环境
7.1.1.相关依赖
<dependencies>
<!-- log4j 日志工具 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>
<!-- java web begin -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!-- java web end -->
<!--spring-json依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.17.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>
<!--spring jdbc支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.1.RELEASE</version>
</dependency>
<!--spring和mybatis的整合包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--mybatis包-->
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--数据库连接池和mysql的连接包-->
<!-- 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
<scope>runtime</scope>
</dependency>
</dependencies>
7.1.2.web.xml
关于整合ssm框架需要添加的相关web.xml的内容有如下:
- 注册ContextLoaderListener监听器
- 注册 ServletContext 监听器的实现类 ContextLoaderListener,用于创建 Spring 容器及将创建好的 Spring 容器对象放入到 ServletContext 的作用域中。
- 注册字符集过滤器
- 注册字符集过滤器,用于解决请求参数中携带中文时产生乱码问题。
- 配置中央调度器(前端控制器)
- 配置中央调度器时需要注意,SpringMVC的配置文件名与其它 Spring配置文件名不相同。这样做的目的是 Spring 容器创建管理 Spring 配置文件中的 bean, SpringMVC 容器中负责视图层 bean 的初始。
<!--加载spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-config.xml</param-value>
</context-param>
<!-- 配置spring 监听器,加载spring-config.xml配置文件 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<!-- 统一编码 -->
<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>
<init-param>
<!--初始化强制编码-->
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置前端拦截器-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--contextConfigLocation:指定springmvc配置的加载位置,
如果不指定则默认加 载WEB-INF/***-servlet.xml(例如springmvc-servlet.xml)。
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 可以拦截二种请求
第一种:拦截固定后缀的url,比如设置为 *.do、*.action,
例如:/user/add.action 此方法最简单,不会导致静态资源(jpg,js,css)被拦截
第二种:拦截所有 设置为/
例如:/user/add /user/add.action此方法可以实现REST风格的url
很多互联网类型的应用使用这种方式.
但是此方法会导致静态文件(jpg,js,css)被拦截后不能正常显示.需要特殊处理
错误设置:拦截所有,设置为/*,此设置方法错误,因为请求到Action,
当action转到jsp时再次被拦截,提示不能根据jsp路径mapping成功.
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
7.1.3.配置文件
数据库配置文件
使用德鲁伊(druid)数据库连接池自动匹配Driver
jdbc.username=root
jdbc.password=123456
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis_product
日志配置文件
#全局配置
log4j.rootLogger=DEBUG,console
#debug 测试阶段使用
#控制台日志输出
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%p] %m %n
SpringMVC配置文件
关于springmvc-config.xml的配置文件
<!-- 注解扫描!!!-->
<context:component-scan base-package="cn.springmvc.controller"/>
<!--注解驱动-->
<!-- springmvc使用<mvc:annotation-driven/>
自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter
可以用使用<mvc:annotation-driven/>替代注解处理器和适配器的配置
-->
<mvc:annotation-driven/>
<!-- 映射静态资源 方案1 -->
<mvc:default-servlet-handler />
<!-- 映射静态资源 方案2 -->
<!--<mvc:resources location="/js/" mapping="/js/**"/>-->
<!--<mvc:resources location="/css/" mapping="/css/**"/>-->
<!--<mvc:resources location="/fonts/" mapping="/fonts/**"/>-->
<!--<mvc:resources location="/plugin/" mapping="/plugin/**"/>-->
<!-- 配置视图解析器 -->
<!-- InternalResourceViewResolver:支持JSP视图解析 -->
<!-- viewClass:JstlView表示JSP模板页面需要使用JSTL标签库,所以classpath中必须包含jstl的相关jar包; -->
<!-- prefix 和suffix:查找视图页面的前缀和后缀,最终视图的址为: -->
<!-- 前缀+逻辑视图名+后缀,逻辑视图名需要在controller中返回ModelAndView指定,比如逻辑视图名为hello,-->
<!-- 则最终返回的jsp视图地址 "WEB-INF/view/hello.jsp" -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/JSP/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--配置文件上传的解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--允许上传的文件最大大小 单位是byte-->
<property name="maxUploadSize" value="60000000"/>
</bean>
Spring配置文件
<!--注解扫描-->
<context:component-scan base-package="cn.springmvc.service" />
<!--加载配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!--sqlSessionFactroy的创建交由spring-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入数据源-->
<property name="dataSource" ref="dataSource" />
<!--把映射文件交由spring进行管理-->
<property name="mapperLocations" value="classpath:mapper/*Mapper.xml" />
</bean>
<!--把sqlSeesion会话创建交给spring进行管理-->
<!-- <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
<constructor-arg name="sqlSessionFactory" ref="sqlSeesionFactory"/>
</bean>-->
<!--让spring生成mapper接口代理实现类-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--传入工厂-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--扫描包哪些包下的接口-->
<property name="basePackage" value="cn.springmvc.dao"/>
</bean>
<!--事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置注解的事务AOP -->
<aop:aspectj-autoproxy/>
<tx:annotation-driven transaction-manager="txManager"/>
7.2.实战
需求:利用SSM框架完成简单的商品信息管理
8.异常处理
SpringMVC 框架处理异常的常用方式:使用@ExceptionHandler
注解处理异常。
8.1.异常的主要处理方式
异常处理思路
主要的处理方式
- 使用@ExceptionHandler注解实现异常处理.
- 使用@ControllerAdvice + @ExceptionHandler
8.2.@ExceptionHandler注解
使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。
而被注解的方法,其返回值可以是 ModelAndView、String,或 void,方法名随意,方法参数可以是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值。
对于异常处理注解的用法,也可以直接将异常处理方法注解于 Controller 之中。
如果单独使用@ExceptionHandler注解
- 作用域:局部
- 注有该注解的方法,和发生异常的Mapping方法,两者需要在同一个Controller里
简单案例
@Controller
@ResponseBody
public class Cors {
@RequestMapping("/cors")
public ResultCode reqShow(HttpServletRequest request){
// 发生 ArithmeticException 异常
int i = 1/0;
return ResultCode.SUCCESS;
}
// @ExceptionHandler 参数为 要处理器的 异常类(或子类)
@ExceptionHandler(ArithmeticException.class)
public String exInfo(ArithmeticException e){
System.out.println(e.getMessage());
return "seccess";
}
}
企业中如何处理异常码
在企业中通常有多个状态码的场景,可以采用枚举的方式来进行记录。
public enum ResultCode {
SUCCESS("0", "success"),
UNKNOWN_ERROR("0x10001", "unkonwn error"),
USERNAME_ERROR("0x10002", "username error or does not exist"),
PASSWORD_ERROR("0x10003", "password error"),
USERNAME_EMPTY("0x10004", "username can not be empty");
private String code;
private String msg;
ResultCode(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
8.3.使用 @ControllerAdvice 注解
使用@ControllerAdvice注解
- 全局,针对全部Controller中的指定异常类
- 该注解需要 和@ExceptionHandler配合,来完成全局异常的处理;
注意:两个注解的配合,发生异常Mapping方法 与 @ExceptionHandler注解的方法 就可以不用写在同一个Controller 里。
注意,注解描扫也要扫到自定义异常所在的包
案例
正常代码
@Controller
@ResponseBody
public class Cors {
@RequestMapping("/cors")
public ResultCode reqShow(HttpServletRequest request){
// 发生 ArithmeticException 异常
int i = 1/0;
return ResultCode.SUCCESS;
}
}
全局异常处理
@ControllerAdvice
public class AppAllException{
@ResponseStatus(HttpStatus.BAD_REQUEST) // 响应 状态码400
// @ExceptionHandler 参数为 要处理器的 异常类(或子类)
// 注解参数不声明指定异常类,则默认为方法列表中的异常参数类
@ExceptionHandler(ArithmeticException.class)
public void showInfo(Exception e){
System.out.println(e.getMessage());
}
}
当然异常的返回值也可以是json,只要在全局异常类上打上@ResponseBody注解即可
使用全局异常也可以在同一个Controller中写异常方法,只不过这回的作用范围是全局的
8.3.1.关于RestControllerAdvice
关于RestControllerAdvice是通常标注在异常类上,表示整个异常类中的所有方法返回值都是JSON数据,类似于@RestController
.
常见的业务异常处理代码
/**
* 统一异常处理器
*/
@RestControllerAdvice
public class ExceptionController {
Logger logger = Logger.getLogger(ExceptionController.class);
/**
R返回值表示是自定义封装的返回值类
*/
/**
* 400 - Bad Request
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public R handlerHttpMessageNotReadableException(
HttpMessageNotReadableException e) {
logger.error("参数解析失败", e);
return R.error("参数解析失败" + e.getMessage(),400);
}
/**
* 405 - Method Not Allowed
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public R handlerHttpRequestMethodNotSupportedException(
HttpRequestMethodNotSupportedException e) {
logger.error("不支持当前请求方法", e);
return R.error("不支持当前请求方法" + e.getMessage(),400);
}
/**
* 415 - Unsupported Media Type
*/
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public R handlerHttpMediaTypeNotSupportedException(
HttpMediaTypeNotSupportedException e) {
logger.error("不支持当前媒体类型", e);
return R.error("不支持当前媒体类型" + e.getMessage(),400);
}
/**
* 处理Exception异常
*
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
//@ResponseBody
public R handlerException(Exception e) {
e.printStackTrace();
logger.error(e.getMessage());
return R.error(e.getMessage());
}
/**
* 自定义业务异常
*
* @param e
* @return
*/
@ExceptionHandler(BizException.class)
public R handlerBizException(BizException e) {
e.printStackTrace();
logger.error(e.getMessage());
return R.error(e.getMessage());
}
}
9.拦截器
SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。当然,在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hnUmesam-1614912599313)(img/25.png)]
如何实现拦截器?
一般自定义拦截器实现HandlerInterceptor
接口即可
9.1.实现自定义拦截器
关于实现自定义拦截器如下
自定义拦截器,需要实现HandlerInterceptor 接口。而该接口中含有三个方法:
- preHandle(request,response, Object handler)
- 该方法在处理器方法执行之前执行。其返回值为 boolean,若为 true,则紧接着会执行处理器方法,且会将 afterCompletion()方法放入到一个专门的方法栈中等待执行
- postHandle(request,response, Object handler,modelAndView)
- 这个方法在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用。
- afterCompletion(request,response, Object handler, Exception ex)
- 当 preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法。即该方法是在中央调度器渲染(数据填充)了响应页面之后执行的,此时对 ModelAndView 再操作也对响应无济于事
9.2.简单拦截器案例
9.2.1.定义拦截器
创建一个AuthHandlerInterceptor类,该实现HandlerInterceptor接口
// 关于权限控制的拦截器案例
public class InterceptorDemo implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("执行preHandle....");
Object user = httpServletRequest.getSession().getAttribute("user");
// 如果有数据则成功放行
if (user != null) {
return true;
}
// 否则请求转发到登陆页面或者失败页面
//...
httpServletRequest.getRequestDispatcher("/WEB-INF/JSP/fail.jsp").forward(httpServletRequest,httpServletResponse);
return false;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
9.2.2.配置拦截器
在springmvc-config.xml配置文件中配置相关信息
<!--拦截器-->
<mvc:interceptors>
<!-- 匹配的是url路径, 如果不配置或/**,将拦截所有的Controller -->
<mvc:interceptor>
<!--拦截所有-->
<mvc:mapping path="/**"/>
<!--不拦截登录-->
<mvc:exclude-mapping path="/login"/>
<mvc:exclude-mapping path="/css/**"/>
<mvc:exclude-mapping path="/js/**"/>
<mvc:exclude-mapping path="/fonts/**"/>
<mvc:exclude-mapping path="/plugin/**"/>
<bean class="cn.springmvc.interceptor.AuthHandlerInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<mvc:mapping/>
用于指定当前所注册的拦截器可以拦截的请求路径,而/**表示拦截所有请求。
9.2.3.页面
登陆页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
用户:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
</body>
</html>
错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p> 未登录,无法访问!</p>
</body>
</html>
9.2.4.控制器
简单的关于登陆的Login控制类
@Controller
public class LoginController {
@GetMapping("/login")
public String loginView() {
return "login";
}
@PostMapping("/login")
@ResponseBody
public String login(String username, String password, HttpServletRequest request) {
if ("admin".equals(username) && "123456".equals(password)) {
//如果用户名密码正确应该存域
request.getSession().setAttribute("user",username);
return "login success";
}
return "login fail!";
}
}
9.3.多重拦截器
当有多个拦截器时,形成拦截器链。拦截器链的执行顺序,与其注册顺序一致。需要再次强调一点的是,当某一个拦截器的 preHandle()方法返回 true 并被执行到时,会向一个专门的方法栈中放入该拦截器的 afterCompletion()方法。
图解