SpringMVC框架技术总结
Posted 生命是有光的
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC框架技术总结相关的知识,希望对你有一定的参考价值。
✍SSM框架之行🔥
🔥SSM三大框架🔥 | 地址 |
---|---|
Spring框架技术总结(一) 🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/118753656 |
Spring框架技术总结(二) 🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/118890002 |
Spring框架技术总结(三) 🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/119009144 |
Mybatis框架技术总结(一) 🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/118891409 |
Mybatis框架技术总结(二) 🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/118895554 |
SpringMVC框架技术总结(一) 🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/119205766 |
SpringMVC框架技术总结(二)🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/119488738 |
SpringMVC框架技术总结(三)🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/119488927 |
✍目录总览
1.拦截器
1.1 应用场景
如果我们想在多个Handler方法执行之前或者之后都进行一些处理,甚至某些情况下需要拦截掉,不让Handler方法执行。那么可以使用SpringMVC为我们提供的拦截器。
1.2 拦截器和过滤器的区别
过滤器是在Servlet执行之前或者之后进行处理。而拦截器是对Handler(处理器)执行前后进行处理。
- 归属不同: Filter属于Servlet技术, Interceptor属于SpringMVC技术
- 拦截内容不同: Filter对所有访问进行增强, Interceptor仅针对SpringMVC的访问进行增强
如图:
1.3 创建并配置拦截器
1.3.1、创建类实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
}
1.3.2、实现方法
public class MyInterceptor implements HandlerInterceptor {
//在handler方法执行之前会被调用
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
//返回值代表是否放行,如果为true则放行,如果为fasle则拦截,目标方法执行不到
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
1.3.3、配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<!--开启具体的拦截器的使用,可以配置多个-->
<mvc:interceptor>
<!--
配置拦截器要拦截的路径
/* 代表当前一级路径,不包含子路径(表示拦截所有/开头的映射)
/** 代表当前一级路径和多级路径,使用的更多(表示拦截所有映射)
例如:
/test/* 这种会拦截下面这种路径/test/add /test/delete
但是拦截不了多级路径的情况例如 /test/add/abc /test/add/abc/bcd
/test/** 这种可以拦截多级目录的情况,无论 /test/add还是/test/add/abc/bcd 都可以拦截
-->
<mvc:mapping path="/**"/>
<!--配置排除拦截的路径-->
<mvc:exclude-mapping path="/"/>
<!--配置拦截器对象注入springmvc容器-->
<bean class="com.sangeng.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
1.4、拦截器配置项
<mvc:interceptors>
<!--开启具体的拦截器的使用,可以配置多个-->
<mvc:interceptor>
<!--设置拦截器的拦截路径,支持*通配-->
<!--/** 表示拦截所有映射-->
<!--/* 表示拦截所有/开头的映射-->
<!--/user/* 表示拦截所有/user/开头的映射-->
<!--/user/add* 表示拦截所有/user/开头,且具体映射名称以add开头的映射-->
<!--/user/*All 表示拦截所有/user/开头,且具体映射名称以All结尾的映射-->
<mvc:mapping path="/*"/>
<mvc:mapping path="/**"/>
<mvc:mapping path="/handleRun*"/>
<!--设置拦截排除的路径,配置/**或/*,达到快速配置的目的-->
<mvc:exclude-mapping path="/b*"/>
<!--指定具体的拦截器类-->
<bean class="MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
1.5、 拦截器方法及参数详解
- preHandle方法会在Handler方法执行之前执行,我们可以在其中进行一些前置的判断或者处理。
- postHandle方法会在Handler方法执行之后执行,我们可以在其中对域中的数据进行修改,也可以修改要跳转的页面。
- afterCompletion方法会在最后执行,这个时候已经没有办法对域中的数据进行修改,也没有办法修改要跳转的页面。我们在这个方法中一般进行一些资源的释放。
1.5.1、前置处理方法
/**
* 在handler方法执行之前会被调用
* @param request 当前请求对象
* @param response 响应对象
* @param handler 相当于是真正能够处理请求的handler方法封装成的对象,对象中有这方法的相关信息
* @return 返回值代表是否放行,如果为true则放行,如果为fasle则拦截,目标方法执行不到
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
//返回值代表是否放行,如果为true则放行,如果为fasle则拦截,目标方法执行不到
return true;
}
1.5.2、后置处理方法
/**
* postHandle方法会在Handler方法执行之后执行
* @param request 当前请求对象
* @param response 响应对象
* @param handler 相当于是真正能够处理请求的handler方法封装成的对象,对象中有这方法的相关信息
* @param modelAndView handler方法执行后的modelAndView对象,我们可以修改其中要跳转的路径或者是域中的数据
* @throws Exception
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
1.5.3、完成处理方法
拦截器最后执行的方法,无论原始方法是否执行
/**
* afterCompletion方法会在最后执行
* @param request 当前请求对象
* @param response 响应对象
* @param handler 相当于是真正能够处理请求的handler方法封装成的对象,对象中有这方法的相关信息
* @param ex 异常对象
* @throws Exception
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
1.6 案例-登录状态拦截器
1.6.1需求
我们的接口需要做用户登录状态的校验,如果用户没有登录则跳转到登录页面,登录的情况下则可以正常访问我们的接口。
1.6.2 分析
怎么判断是否登录?
登录时往session写入用户相关信息,然后在其他请求中从session中获取这些信息,如果获取不到说明不是登录状态。
很多接口都要去写判断的代码,难道在每个Handler中写判断逻辑?
用拦截器,在拦截器中进行登录状态的判断。
登录接口是否应该进行拦截?
不能拦截
静态资源是否要进行拦截?
不能拦截
1.6.3 步骤分析
①登录页面,请求发送给登录接口
②登录接口中,校验用户名密码是否正确(模拟校验即可,先不查询数据库)。
如果用户名密码正确,登录成功。把用户名写入session中。
③定义其他请求的Handler方法
④定义拦截器来进行登录状态判断
如果能从session中获取用户名则说明是登录的状态,则放行
如果获取不到,则说明未登录,要跳转到登录页面。
1.6.4 代码实现
1.6.4.1 登录功能代码实现
①编写登录页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="/login">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit">
</form>
</body>
</html>
②编写登录接口
接口中,校验用户名密码是否正确(模拟校验即可,先不查询数据库)。如果用户名密码正确,登录成功。把用户名写入session中。
@Controller
public class LoginController {
@PostMapping("/login")
public String longin(String username, String password, HttpSession session){
//往session域中写入用户名用来代表登录成功
session.setAttribute("username",username);
return "/WEB-INF/page/success.jsp";
}
}
1.6.4.2 登录状态校验代码实现
①定义拦截器
public class LoginInterceptor implements HandlerInterceptor {
}
②重写方法,在preHandle方法中实现状态校验
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//从session中获取用户名,判断是否存在
HttpSession session = request.getSession();
String username = (String) session.getAttribute("username");
if(StringUtils.isEmpty(username)){
//如果获取不到说明未登录 ,重定向跳转到登录页面
String contextPath = request.getServletContext().getContextPath();
response.sendRedirect(contextPath+"/static/login.html");
}else{
//如果获取到了,说明之前登录过。放行。
return true;
}
return false;
}
}
③配置拦截器
- 登录相关接口不应该拦截
- 静态资源不拦截
<mvc:interceptors>
<mvc:interceptor>
<!--要拦截的路径-->
<mvc:mapping path="/**"/>
<!--排除不拦截的路径-->
<mvc:exclude-mapping path="/static/**"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/WEB-INF/page/**"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/login"></mvc:exclude-mapping>
<bean class="com.sangeng.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
1.7、单个拦截器执行流程
1.8、多拦截器执行顺序
如果我们配置了多个拦截器,拦截器的顺序是按照配置的先后顺序的。
这些拦截器中方法的执行顺序如图(preHandler都返回true的情况下):
执行顺序为:
preHandle1、preHandle2、preHandle3
testPath
postHandle3、postHandle2、postHandle1
afterCompletion3、afterCompletion2、afterCompletion1
如果拦截器3的preHandle方法返回值为false。执行顺序如图:
- 只有所有拦截器都放行了,postHandle方法才会被执行。
- 只有当前拦截器放行了,当前拦截器的afterCompletion方法才会执行。
执行顺序为:
preHandle1、preHandle2、preHandle3
afterCompletion2、afterCompletion1
2.统一异常处理
-
我们在实际项目中Dao层和Service层的异常都会被抛到Controller层。但是如果我们在Controller的方法中都加上异常的try…catch处理也会显的非常的繁琐。
-
所以SpringMVC为我们提供了统一异常处理方案。可以把Controller层的异常进行统一处理。这样既提高了代码的复用性也让异常处理代码和我们的业务代码解耦。
-
一种是实现
HandlerExceptionResolver
接口的方式,一种是使用@ControllerAdvice
注解的方式。
2.1、HandlerExceptionResolver
①实现接口
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
}
②重写方法
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
//如果handler中出现了异常,就会调用到该方法,我们可以在本方法中进行统一的异常处理。
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//获取异常信息,把异常信息放入域对象中
String msg = ex.getMessage();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg",msg);
//跳转到error.jsp
modelAndView.setViewName("/WEB-INF/page/error.jsp");
return modelAndView;
}
}
③注入容器
可以使用注解注入也可以使用xml配置注入。这里使用注解注入的方式。在类上加 @Component 注解,注意要保证类能被组件扫描扫描到。
@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
//....省略无关代码
}
2.2、注解开发异常处理器(重要)
①创建类加上@ControllerAdvice注解进行标识
@ControllerAdvice
public class MyControllerAdvice {
}
②定义异常处理方法
定义异常处理方法,使用 @ExceptionHandler 标识可以处理的异常。
@ControllerAdvice
public class MyControllerAdvice {
@ExceptionHandler({NullPointerException.class,ArithmeticException.class})
public ModelAndView handlerException(Exception ex){
//如果出现了相关的异常,就会调用该方法
String msg = ex.getMessage();
ModelAndView modelAndView = new ModelAndView();
//把异常信息存入域中
modelAndView.addObject("msg",msg);
//跳转到error.jsp
modelAndView.setViewName("/WEB-INF/page/error.jsp");
return modelAndView;
}
}
③注入容器
可以使用注解注入也可以使用xml配置注入。这里使用注解注入的方式。在类上加 @Component 注解,注意要保证类能被组件扫描扫描到。
@ControllerAdvice
@Component
public class MyControllerAdvice {
//省略无关代码
}
2.3、注解详解
2.3.1、@ControllerAdvice
使用注解实现异常分类管理
- 名称:
@ControllerAdvice
- 类 型: 类注解
- 位置:异常处理器类上方
- 作用:设置当前类为异常处理器类
- 范例:
@Component
@ControllerAdvice
public class MyControllerAdvice {
}
2.3.2、@ExceptionHandler
- 名称:
@ExceptionHandler
- 类型: 方法注解
- 位置:异常处理器类中针对指定异常进行处理的方法上方
- 作用:设置指定异常的处理方式
- 说明:处理器方法可以设定多个
- 范例:
@ExceptionHandler(Exception.class)
public String doOtherException(Exception ex){
return "出错啦,请联系管理员! ";
}
2.3 总结
我们在实际项目中一般会选择使用@ControllerAdvice 来进行异常的统一处理。
因为如果在前后端不分离的项目中,异常处理一般是跳转到错误页面,让用户有个更好的体验。而前后端分离的项目中,异常处理一般是把异常信息封装到Json中写入响应体。无论是哪种情况,使用@ControllerAdvice的写法都能比较方便的实现。
例如下面这种方式就是前后端分离的异常处理方案,把异常信息封装到对象中,转换成json写入响应体。
@ControllerAdvice
@Component
public class MyControllerAdvice {
@ExceptionHandler({NullPointerException.class,ArithmeticException.class})
@ResponseBody
public Result handlerException(Exception ex){
Result result = new Result();
result.setMsg(ex.getMessage());
result.setCode(500);
return result;
}
}
3.文件上传
3.1 文件上传三要素
- Http协议规定了我们在进行文件上传时的请求格式要求。
- 表单项
type = "file"
- 表单的提交方式是
post
- 表单的
enctype
属性是多部份表单形式,及enctype = "multipart/form-data"
3.1.1、请求方式为POST请求
如果是使用表单进行提交的话,可以把form标签的method属性设置为POST。例如:
<form action="/upload" method="post">
</form>
3.1.2、请求头Content-Type必须为multipart/form-data
如果是使用表单的话可以把表单的entype属性设置成multipart/form-data。例如:
<form action="/upload" method="post" enctype="multipart/form-data">
</form>
3.2、SpringMVC接收上传过来的文件
SpringMVC使用commons-fileupload的包对文件上传进行了封装,我们只需要引入相关依赖和进行相应配置就可以很轻松的实现文件上传的功能。
3.2.1、导入依赖
<!--commons文件上传,如果需要文件上传功能,需要添加本依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
3.2.2、配置文件上传解析器
SpringMVC容器中配置文件上传解析器
<!--
文件上传解析器
注意:id 必须为 multipartResolver
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置默认字符编码 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 一次请求上传的文件的总大小的最大值,单位是字节 100MB-->
<property name="maxUploadSize" value="#{1024*1024*100}"/>
<!-- 每个上传文件大小的最大值,单位是字节 50MB-->
<property name="maxUploadSizePerFile" value="#{1024*1024*50}"/>
</bean>
3.2.3、接收上传的文件数据并处理
上传表单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/upload" methodSpringMVC框架技术总结
Spring+SpringMVC+MyBatis+Maven框架整合