从源码角度分析springMVC执行流程

今天从源码的角度,靠断电跟踪技术分析一下springMVC内部的执行流程,(包括拦截器、过滤器、servlet的dispatch、异常处理等)

1.我们先自定义一个拦截器(springmvc的全局拦截器)

//定义一个拦截器
public class TimeAccessInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        LocalTime now=LocalTime.now();//JDK8中的时间对象
        int hour=now.getHour();//获取当前时间对应小时
        log.info("hour {}",hour);
        if(hour<=7||hour>=22)
            throw new RuntimeException("请在6~10点进行访问");
        return true;
    }
}
//在springmvc全局引入这个拦截器
@Configuration
public class SpringWebConfig implements WebMvcConfigurer {
    //配置spring mvc 拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TimeAccessInterceptor())
                .addPathPatterns("/user/*");
    }
}

然后我们断点跟踪,发现生效了。注意这里的调用栈,拦截器是在servlet分发请求之后调用的

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        .......

        try {
            this.doDispatch(request, response);  //分发请求
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

        }

    }

 2.由于我们这里使用抛异常的方式对请求进行拦截的,所以客户端的请求结果会直接显示500异常,但我们可以将异常返回结果进行处理,处理成客户端看得懂的格式

我们用一个springmvc的全局异常处理GlobalControllerAdvice

@RestControllerAdvice
public class GlobalControllerAdvice {
    @ExceptionHandler({RuntimeException.class})
    public SysResult fail(Exception e){
        return SysResult.fail(null,e.getMessage());
    }
}

现在我们再来看返回结果:就变成了这样:就好看多了

这个时候DispatchServlet类的doDispatch方法必定会抛出异常,但是源码在抛出异常之后,仍然对分发的结果做出了处理:processDispatchResult

2.我们再给自定义的拦截器重写它的afterCompletion,看看他的执行时机

这时候我们发现,如果在perHandle里抛出了异常或者返回值为false的时候,afterCompletion里的内容是不会被执行的。为什么? 

for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }

这段源码告诉我们,当preHandle返回值为false的时候,会去执行下面的triggerAfterCompletion方法,而这个方法内部执行的时候,根据mappedHandler的interceptorIndex来决定是否执行循环体,而这个interceptorIndex值在初始化的时候为-1,如果抛出异常或者preHandle返回false的时候,interceptorIndex值不会有变化,所以永远不会执行到aftetCompletion方法。而只有当返回值未true的时候,interceptorIndex才会累加到数组长度-1,之后就会执行afterCompletion方法了。(这里的mappedHandler,可以把他理解为一个处理对象,同时他里面包含了handler和一些拦截器)

那有人会问:这个mappedHandler是从哪里来的呢?再往前看,在doDispatch方法内

mappedHandler = this.getHandler(processedRequest);

有这样一行代码,往里看在AbstractHandlerMapping类的方法中

while(var5.hasNext()) {
            HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            } else {
                chain.addInterceptor(interceptor);
            }
        }

这里将拦截器添加到了mappedHandler中,当然还包括那个handler(也就是controller里的那个method),而这个adaptedInterceptors哪里来的呢?容器初始化的。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_34116044

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值