SpirngMVC中的拦截器
目录1、概述
SpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
过滤器:依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。
拦截器:依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。
2、原理
看下对应的接口:
public interface handlerinterceptor {
// 执行handler之前执行
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
// 执行完拦截器之后进行处理
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// 视图渲染之后执行
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】
2、先来**顺序执行 **所有拦截器的 preHandle方法
- 1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
- 2、如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;
3、如果任何一个拦截器返回false。直接跳出不执行目标方法
**6、前面的步骤有任何异常都会直接倒序触发 **afterCompletion
7、页面成功渲染完成以后,也会倒序触发 afterCompletion
那么来看下对应的源码操作:
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
.........
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
在执行handler方法之前执行拦截器的前置处理。可以看到如果拦截器的applyPreHandle如果返回的是false,那么就直接返回了,不向下继续执行了,也就是说,不会再来执行handler方法。那么看下applyPreHandle的执行过程:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
handlerinterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
handlerinterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("handlerinterceptor.afterCompletion threw exception", ex2);
}
}
}
看下对应的执行流程:
- 1、for循环正序遍历,循环遍历每个拦截器来执行preHandle方法;
- 2、如果拦截器返回的是true,那么记录一下每个拦截器的索引;如果拦截器返回的是FALSE,那么这里将会直接来执行当前拦截器的triggerAfterCompletion方法,然后就直接返回了。也就是说,如果拦截器链(ABC)中的某一个拦截器B执行preHandle方法时返回的是false,那么会直接直接上一下拦截器A中的afterCompletion方法,而B中的post和after以及C中的pre、post和after都不会执行了
- 如果所有的拦截器都执行成功,那么会继续向下来进行执行
那么这里来记录一下正常的执行流程:
protected void dodispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
....
try {
....
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
....
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new nestedservletexception("Handler dispatch Failed", err);
}
processdispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new nestedservletexception("Handler processing Failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
上面就是对应的全过程了。那么下面来进行测试一下。
3、例子
这里按照上面的方式来实现三个拦截器来进行响应处理:
创建三个拦截器:
1、Onehandlerinterceptor
public class Onehandlerinterceptor implements handlerinterceptor {
private static final Logger logger = LoggerFactory.getLogger(Onehandlerinterceptor.class);
/**
* 返回为true,则会进入到下一个过滤器链中
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("one preHandle---------------------");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("one postHandle---------------------");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("one afterCompletion---------------------");
}
}
2、Twohandlerinterceptor
public class Twohandlerinterceptor implements handlerinterceptor {
private static final Logger logger = LoggerFactory.getLogger(Twohandlerinterceptor.class);
/**
* 返回为true,则会进入到下一个过滤器链中
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("two preHandle---------------------");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("two postHandle---------------------");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("two afterCompletion---------------------");
}
}
3、Threehandlerinterceptor
public class Threehandlerinterceptor implements handlerinterceptor {
private static final Logger logger = LoggerFactory.getLogger(Threehandlerinterceptor.class);
/**
* 返回为true,则会进入到下一个过滤器链中
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("three preHandle---------------------");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("three postHandle---------------------");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("three afterCompletion---------------------");
}
}
4、配置拦截器
// 如果想要自定义springmvc,那么只需要在容器中添加一个WebMvcConfigurer类型的组件
@Configuration(proxyBeanMethods = false)
public class CustomWebMVCConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Onehandlerinterceptor()).addpathPatterns("/self/**").excludePathPatterns("/api/**").order(2);
registry.addInterceptor(new Twohandlerinterceptor()).addpathPatterns("/self/**").excludePathPatterns("/api/**").order(3);
registry.addInterceptor(new Threehandlerinterceptor()).addpathPatterns("/self/**").excludePathPatterns("/api/**").order(4);
}
}
每个拦截器有对应的拦截器的路径和对应的排除路径,以及对应的优先级顺序,也就是拦截器的执行顺序,哪个先执行,哪个后执行,都是可以来进行设置的。
5、编写对应的controller
@Controller
@RequestMapping(path = "self")
public class InterceperController {
@RequestMapping(path = "hello")
@ResponseBody
public String hello(){
return "sucdess";
}
}
6、访问
http://localhost:8080/web/self/hello
7、输出信息
2022-05-15 17:55:59.220 c.g.s.interceptor.Onehandlerinterceptor : one preHandle---------------------
2022-05-15 17:55:59.220 c.g.s.interceptor.Twohandlerinterceptor : two preHandle---------------------
2022-05-15 17:55:59.220 c.g.s.interceptor.Onehandlerinterceptor : one afterCompletion---------------
可以看到这里的因为
4、项目应用
1、问题描述
想要记录一下一次从开始到结束阶段的请求处理时间。
在拦截器的preHandle 方法中记录开始时间,在拦截器的afterCompletion 来记录结束时间。
2、应用技术
因为每个web请求都需要有一个对应的hander来进行处理,在每个handler执行之前,都需要经过intecepter来进行过滤处理,从请求之前到请求之后。
但是因为对于每个拦截器来说都是单例的,即线程不安全,所以针对于每个用户来说,每个请求都是一个新的线程,所以应该考虑到线程安全问题。所以需要在拦截器中使用ThreadLocal来记录一下对应的时间。
3、代码实现
public class LogRequestTimehandlerinterceptor extends handlerinterceptorAdapter {
private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("StopWatch-StartTime");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {
long beginTime = System.currentTimeMillis();//1、开始时间
startTimeThreadLocal.set(beginTime);//线程绑定变量(该数据只有当前请求的线程可见)
return true;//继续流程
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {
long endTime = System.currentTimeMillis();//2、结束时间
long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间)
long consumeTime = endTime - beginTime;//3、消耗的时间
if(consumeTime > 500) {//此处认为处理时间超过500毫秒的请求为慢请求
//Todo 记录到日志文件
System.out.println(String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
}
}
}
添加到MVC中去
// 如果想要自定义springmvc,那么只需要在容器中添加一个WebMvcConfigurer类型的组件
@Configuration(proxyBeanMethods = false)
public class CustomWebMVCConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogRequestTimehandlerinterceptor()).addpathPatterns("/**").excludePathPatterns("/api/**").order(2);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。