技术标签: spring java interceptor 后端
在web开发中,拦截器是经常用到的功能。它可以帮我们预先设置数据以及统计方法的执行效率等等。
今天就来详细的谈一下spring中的拦截器。spring中拦截器主要分两种,一个是HandlerInterceptor,一个是MethodInterceptor。
HandlerInterceptor是springMVC项目中的拦截器,它拦截的目标是请求的地址,比MethodInterceptor先执行。其工作原理是当请求来时先进性预处理,如下。
这里我们可以实现一个通过HandlerInterceptor实现打印请求开始和结束的日志,如下。
1.依赖引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.实现类
拦截器类
@Component
public class EasyLogControllerInterceptor implements HandlerInterceptor {
/**
* 在controller调用之前执行
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println(request.getRequestURI()+"开始执行");
return true;
}
/**
* 在controller调用中执行
*/
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
/**
* 在controller调用后执行
*/
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println(request.getRequestURI()+"执行结束");
}
}
controller类
@RestController
public class TestController {
@GetMapping("/hello")
public Map<String,String> hello(){
Map<String,String> response=new HashMap<>();
response.put("msg","hello");
return response;
}
}
配置类
@Configuration
public class IntercepterConfig implements WebMvcConfigurer {
@Autowired
private EasyLogControllerInterceptor easyLogControllerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//addPathPatterns用于添加拦截路径
//excludePathPatterns用于添加不拦截的路径
registry.addInterceptor(easyLogControllerInterceptor).addPathPatterns("/hello");
}
//此方法用于配置静态资源路径
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/my/");
}
}
3.运行效果
实现一个HandlerInterceptor拦截器可以直接实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。这两种方法殊途同归,其实HandlerInterceptorAdapter也就是声明了HandlerInterceptor接口中所有方法的默认实现,而我们在继承他之后只需要重写必要的方法。
下面就是HandlerInterceptorAdapter的代码,可以看到一个方法只是默认返回true,另外两个是空方法:
public abstract class HandlerInterceptorAdapter implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
这三个方法都是干什么的,有什么作用,什么时候调用,不同的拦截器之间是怎样的调用顺序呢?
先补一张图:
这还得参考一下DispatcherServlet的doDispatch方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
try {
ModelAndView mv;
boolean errorView = false;
try {
processedRequest = checkMultipart(request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// Apply preHandle methods of registered interceptors.
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// Apply postHandle methods of registered interceptors.
if (interceptors != null) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
// Trigger after-completion for successful outcome.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing failed", err);
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
finally {
// Clean up any resources used by a multipart request.
if (processedRequest != request) {
cleanupMultipart(processedRequest);
}
}
}
代码有点长,但是它封装了springMVC处理请求的整个过程。首先根据请求找到对应的HandlerExecutionChain,它包含了处理请求的handler和所有的HandlerInterceptor拦截器;然后在调用hander之前分别调用每个HandlerInterceptor拦截器的preHandle方法,若有一个拦截器返回false,则会调用triggerAfterCompletion方法,并且立即返回不再往下执行;若所有的拦截器全部返回true并且没有出现异常,则调用handler返回ModelAndView对象;再然后分别调用每个拦截器的postHandle方法;最后,即使是之前的步骤抛出了异常,也会执行triggerAfterCompletion方法。
MethodInterceptor是AOP项目中的拦截器,它拦截的目标是方法,即使不是controller中的方法。具体使用方式可以参考Spring boot中通过Aop和拦截器实现自定义注解_WX5991的博客-程序员宅基地
上面的两种拦截器都能起到拦截的效果,但是他们拦截的目标不一样,实现的机制不同,所以有的时候适用不同的场景。
HandlerInterceptoer拦截的是请求地址,所以针对请求地址做一些验证、预处理等操作比较合适。当你需要统计请求的响应时间时MethodInterceptor将不太容易做到,因为它可能跨越很多方法或者只涉及到已经定义好的方法中一部分代码。MethodInterceptor利用的是AOP的实现机制,在本文中只说明了使用方式,关于原理和机制方面介绍的比较少,因为要说清楚这些需要讲出AOP的相当一部分内容。在对一些普通的方法上的拦截HandlerInterceptoer就无能为力了,这时候只能利用AOP的MethodInterceptor。
另外,还有一个跟拦截器类似的东西----Filter。Filter是Servlet规范规定的,不属于spring框架,也是用于请求的拦截。但是它适合更粗粒度的拦截,在请求前后做一些编解码处理、日志记录等。而拦截器则可以提供更细粒度的,更加灵活的,针对某些请求、某些方法的组合的解决方案。
另外的另外,用过人人网的ROSE框架的人都会非常喜欢它的拦截器功能。因为它实现了全注解的方式,只要在类的名字上加上拦截器的注解即表示这是一个拦截器。而使用这个拦截器的方法或者controller也只需在方法或controller的上面加上这个拦截器的注解。其实这是一个关注点的转变,spring的切面控制在配置文件中,配置文件关注哪些地方需要拦截。而在ROSE中,则是在需要拦截的地方关注我要被谁拦截。
参考文章
(转)spring中的拦截器(HandlerInterceptor+MethodInterceptor) - niceyoo - 博客园
Spring Boot中实现配置自定义拦截器_huangjhai的博客-程序员宅基地
下一篇文章讲一下过滤器和拦截器的区别
文章浏览阅读896次。导入from PIL import Image加载图片和保存图片在保存图片的时候会根据输入文件名的后缀名自动转换文件格式image = Image.open(r'./lena.tiff')image.save(r'./lena.jpg')image.save(r'./lena.bmp')图像的对象的主要属性参数含义image.format图片的格式,如jpg等image.size图片的尺寸,如1920*1080image.mode图片的色彩模式,如r_img_gray = image.open('/h/%d.tiff' % (i+1)).convert('la')
文章浏览阅读4.5k次,点赞33次,收藏46次。文章目录写在前面Fibonacci数列用递归来实现用迭代来实现写在最后写在前面今天在作业题中看见了斐波那契数列(Fibonacci数列),相信大家或多或少都听过,具体如上图,实际上用C语言打印出来后也蛮有意思的,故而想在这里和大家一起分享!作者:Shining-point作者的博客主页:Shining-point的博客如果觉得博主的博客写的不错或者有所收获的话,希望大家多多点赞 评论收藏,你们的支持是我的最大动力,不驰于空想,不骛于虚声,我们一起加油!!!Fibon_应用递归算法,写出斐波那契函数的实现。
文章浏览阅读590次。本题要求实现一个删除字符串中的指定字符的简单函数。函数接口定义:void delchar( char *str, char c );其中char *str是传入的字符串,c是待删除的字符。函数delchar的功能是将字符串str中出现的所有c字符删除。裁判测试程序样例:#include <stdio.h>#define MAXN 20void delchar( char str, char c );void ReadString( char s[] ); / 由裁判实现,略去不表_实验8-2-3 删除字符分数 20全屏浏览题目切换布局作者 c课程组单位 浙江大学本
文章浏览阅读490次。import java.util.Random;/** * 随机生成位数工具类 * @author xc */public class RandomUtils {// public static void main(String[] args) {// Random random = new Random();// System.out.println(random.nextInt(4));// // } /** * 随机数50-150 */ public st_randomutils
文章浏览阅读2.7k次。第一种:jrebel使用jetty服务器serviceimpl方法中的代码的修改。不需要手动部署。不需要手动打包。只需要保存。controller方法中的代码的修改。不需要手动部署。不需要手动打包。只需要保存。可以进行xml的热部署。不需要手动部署。不需要手动打包。只需要保存。修改service方法中的结构:不需要手动部署。不需要手动打包。只需要保存。 第二种:jett_idea jetty热部署
文章浏览阅读4.6k次,点赞4次,收藏8次。%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%持续更新。 当前: 20100108 %%仅作笔记。 作者: keyflying %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%legend有时候挺烦人的,尽管大多时候挺好用。基本数据:data = rand(25)+repmat(1:25,25..._matlab legend遮住
文章浏览阅读201次。这是我第一次写博客,有些地方可能表示不清楚,还请见谅。区块链是比特币的重要底层技术,在 2008 年中本聪创造比特币时,也带来了互联网的一场新的革命。区块链,简而言之就是存放在网络云端的一条通过哈希指针将所有区块连接在一起的长链,每一个区块都存放着一笔区块链上的交易数据。下面将以比特币为模型介绍区块链。1 哈希指针是什么?首先介绍一下哈希函数,哈希函数是一种将不定长的数据块映射到定..._csdn qq_39693601
文章浏览阅读738次。一、什么是装饰器python装饰器(fuctional decorators)就是用于拓展原来函数功能的一种函数,目的是在不改变原函数名(或类名)的情况下,给函数增加新的功能。这个函数的特殊之处在于它的返回值也是一个函数,这个函数是内嵌“原“”函数的函数。实例:结果:二、语法糖语法糖说明语法糖(Syntactic sugar): 计算机语言中特殊的某种语法 这种语法对..._python装饰器 语法糖
文章浏览阅读540次。Build 2015 大会上,微软除了发布了 Microsoft Edge 浏览器和新的 Windows 10 系统外,最大的惊喜莫过于宣布推出免费跨平台的 Visual Studio Code 。Visual Studio Code (简称 VS Code / VSC) 是一款免费开源的现代化轻量级代码编辑器,支持几乎所有主流的开发语言的语法高亮、智能代码补全、自定义快捷键、括号匹配和颜色区分..._vs 标签另起一行插件
文章浏览阅读2.8k次。golang中string操作是一个比较频繁的工作。其中去除空格、换行、空白符是经常需要的。源码:package mainimport ( "fmt" "regexp" "strings")func compressStr(str string) string { if str == "" { return "" } //匹配一个或多个空白符的正则表达式 reg := regexp.MustCompile("\\s+") return reg.ReplaceAllStr._golang 去掉字符串前后的空格tab和换行
文章浏览阅读367次。坐标排序+平衡树_「poj2352」stars
文章浏览阅读5.4k次。CorelDRAW 2022(CDR22)软件最新版本正式发布 新增功能详解_cdr22