(二)万字详解SpringMVC Servlet —SpringMVC十大常用组件是什么?过滤器和拦截器的区别是什么?如何将不同Request路由到不同控制器?

引言

作为一名Java开发实习生,今天分享一下自己的知识学习。Spring框架已经成为我们开发工作中不可或缺的一部分。虽然Spring框架带来了极大的便利,但它有时也会让我们忽视一些底层的技术细节,不了解对编写代码貌似影响不大,但解决bug时,如果百度到了还好,百度不到只能一头雾水,两眼干瞪。在Spring Web应用中,一个很简单的问题:1个http请求通过网络到达我们的服务器,是怎么执行java代码的?

在学习SpringMVC之前,先认识一下Servlet。

一、Servlet

什么是servlet?servlet运行在哪?

可以看见servlet就是一个接口;接口就是规定了一些规范,使得一些具有某些共性的类都能实现这个接口,从而都遵循某些规范。有的人往往以为就是servlet直接处理客户端的http请求,其实并不是这样,servlet并不会去监听8080端口;直接与客户端打交道是“容器”,比如常用的tomcat。

在这里插入图片描述
在这里插入图片描述

二、SpringMVC整体流程与十大常用组件

任何Spring Web的entry point(入口点,即应用程序开始处理HTTP请求的地方),都是servlet。

先了解一下整体流程和SpringMVC常用的10个组件

在这里插入图片描述

1、DispatcherServlet:前端控制器

springmvc 处理请求的入口,作用:统一处理请求和响应,整个流程控制的中心,由它来调用其他组件处理用户的请求。

2、HandlerMapping:处理器映射器

getHandler:根据请求查找请求处理器,会返回一个 HandlerExecutionChain 对象。

public interface HandlerMapping {
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
3、HandlerExecutionChain:处理器执行链

请求处理器,通常就是我们自定义的 controller 对象及方法

public class HandlerExecutionChain {
    // 请求处理器,通常就是我们自定义的 controller 对象及方法
    private final Object handler;
    // 拦截器,当前请求匹配到的拦截器列表
    private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
    // 拦截器索引,用来记录执行到第几个拦截器了
    private int interceptorIndex = -1;
}
4、handler:处理器

一般指我们自定义的 controller,在 DispatcherServlet 的控制下 handler 对具体的请求进行处理。

5、HandlerAdapter:处理器适配器

它负责对 handler 的方法进行调用,由于 handler 的类型可能有很多种,每种 handler 的调用过程可能不一样,此时就需要用到适配器 HandlerAdapte

public interface HandlerAdapter {
    // 当前 HandlerAdapter 是否支持 handler
    boolean supports(Object handler);
    // 其内部负责调用 handler 的来处理用户的请求,返回返回一个 ModelAndView 对象
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
6、ModelAndView:模型和视图
public class ModelAndView {
    // 视图
    @Nullable
    private Object view;
    // 模型,用来存放共享给客户端的数据
    @Nullable
    private ModelMap model;
}
7、ViewResolver:视图解析器

它负责视图解析,根据视图的名称得到对应的视图对象(View)

public interface ViewResolver {
    // 根据视图的名称得到对应的视图对象
    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;
}
8、View:视图

render 方法根据指定的模型数据(model)渲染视图,即 render 方法负责将结果输出给客户端。

public interface View {
    void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
9、HandlerExceptionResolver:处理器异常解析器

负责处理异常的,HandlerExceptionResolver 接口有个resolveException方法,用来解析异常,返回异常情况下对应的 ModelAndView 对象

public interface HandlerExceptionResolver {
    @Nullable
    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
10、HttpMessageConverter:http 报文转换器

将请求报文转换为 Java 对象,或将 Java 对象转换为响应报文,在处理@RequestBody、RequestEntity、@ResponseBody、ResponseEntity 的时候会用到

public interface HttpMessageConverter<T> {
    // 是否可以将请求报文读取给方法参数指定的类型
    boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
    // 是否可以将响应的报文转换为方法参数指定的类型输出
    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
    // 当前转换器支持的类型
    List<MediaType> getSupportedMediaTypes();
    // 当前转换器支持的类型
    default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
        return (canRead(clazz, null) || canWrite(clazz, null) ?
            getSupportedMediaTypes() : Collections.emptyList());
    }
    // 将http报文转换为给定的类型,然后返回
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;
    // 将给定的对象t,转换为http报文输出到客户端
    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}

三、SpringMVC处理流程详解

在这里插入图片描述
springmvc 的所有请求,最终都会到org.springframework.web.servlet.DispatcherServlet#doDispatch这个方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 请求对象
    HttpServletRequest processedRequest = request;
    // 处理器执行链对象
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    // 获取异步处理管理器,servlet3.0后支持异步处理,可以在子线程中响应用户请求
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    try {
        //模型和视图
        ModelAndView mv = null;
        //异常对象
        Exception dispatchException = null;
        try {
            // 1.解析multipart类型的请求,上传文件用的就是multipart类型的请求方式
            processedRequest = checkMultipart(request);
            // 用来标记是否是multipart类型的请求
            multipartRequestParsed = (processedRequest != request);
            // 2.根据请求获取HandlerExecutionChain对象
            mappedHandler = getHandler(processedRequest);
            //如果没有找到处理器,就404了
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
            // 3.根据处理器获取HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            // 4.调用拦截器的preHandle方法,若返回false,处理结束
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
            // 5.调用handler实际处理请求,获取ModelAndView对象,这里会调用HandlerAdapter#handle方法处理请求,其内部会调用handler来处理具体的请求
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            // 判断异步请求不是已经开始了,开始了就返回了
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }
            // 如果mv对象中没有视图 & DispatcherServlet配置了默认的视图,则给mv安排一个默认的视图
            applyDefaultViewName(processedRequest, mv);
            // 6.调用拦截器的postHandle方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
            dispatchException = ex;
        } catch (Throwable err) {
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        // 7.处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    } catch (Exception ex) {
        // 8.调用拦截器的afterCompletion方法
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    } catch (Throwable err) {
        // 8.调用拦截器的afterCompletion方法
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new NestedServletException("Handler processing failed", err));
    } finally {
        // 对于异步处理的情况,调用异步处理的拦截器AsyncHandlerInterceptor的afterConcurrentHandlingStarted方法
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else {
            // 对于multipart的请求,清理资源,比如文件上传的请求,在上传的过程中文件会被保存到临时文件中,这里就会对这些文件继续清理
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}
1、解析 multipart 类型的请求

上传文件用的就是multipart类型的请求方式

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
    // 判断multipartResolver解析器是否存在 && 请求是否是multipart类型
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
        // 将请求转换为multipart类型的请求对象,通常为MultipartHttpServletRequest类型
        return this.multipartResolver.resolveMultipart(request);
    }
    return request;
}
2、根据请求获取 HandlerExecutionChain 对象

HandlerExecutionChain包含handler、interceptorList、interceptorIndex3个信息

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

handler 类型为HandlerMethod

public class HandlerMethod {
    protected final Log logger = LogFactory.getLog(this.getClass());
    // 通常是我们自定义的controller对象
    private final Object bean;
    @Nullable
    private final BeanFactory beanFactory;
    private final Class<?> beanType;
    // 能够处理当前请求的方法
    private final Method method;
    private final Method bridgedMethod;
    private final MethodParameter[] parameters;
    @Nullable
    private HttpStatus responseStatus;
    @Nullable
    private String responseStatusReason;
    @Nullable
    private HandlerMethod resolvedFromHandlerMethod;
    @Nullable
    private volatile List<Annotation[][]> interfaceParameterAnnotations;
    private final String description;
3、根据处理器获取 HandlerAdapter

遍历HandlerAdapter列表,找到能够处理当前 handler 的HandlerAdapter,如果没找到会报错

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    } 
    throw new ServletException("No adapter for handler [" + handler +
                               "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
4、调用拦截器的 preHandle 方法
// 调用拦截器的preHandle方法,若返回false,处理结束
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}

主要干了 3 个事情

  • 循环调用拦截器的preHandle方法
  • 如果某个拦截器的preHandle方法返回 false,则反向依次调用那些 preHandle 方法返回 ture 的拦截器的 afterCompletion 方法;这句话有点绕,比如有 3 个拦截器,1、2 的 preHandler 返回了 true,而 3 返回的是 false,那么这里将按照 2、1 的顺序调用他们的 afterCompletion 方法
  • 记录拦截器的执行位置
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for (int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        // 调用拦截器的preHandle方法
        if (!interceptor.preHandle(request, response, this.handler)) {
            // 如果拦截器返回false,则反向依次调用那些preHandle方法返回ture的拦截器的afterCompletion方法
            triggerAfterCompletion(request, response, null);
            return false;
        }
        // 记录当前拦截器执行的位置
        this.interceptorIndex = i;
    }
    return true;
}

triggerAfterCompletion方法

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);
        }
    }
}

通过拦截器当前执行的位置interceptorIndex逆向调用拦截器的afterCompletion方法

5、调用 handler 实际处理请求,获取 ModelAndView 对象
// 调用handler实际处理请求,获取ModelAndView对象,
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

ha.handle 方法内部过程会走到RequestMappingHandlerAdapter#invokeHandlerMethod方法,这个方法内部会通过反射调用@RequestMapping 标注的方法,这个方法主要做了 3 个重要的事情:

  • step1:组装目标方法需要的参数
public interface HandlerMethodArgumentResolver {
    // 判断当前解析器是否能处理这个parameter这个参数
    boolean supportsParameter(MethodParameter parameter);
    // 解析参数:从http请求中解析出控制器需要的参数的值
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

这个接口有很多实现类,列几个常见的

实现类对应的控制器参数说明
PathVariableMapMethodArgumentResolver@PathVariable 标注参数从 url 中提取参数的值
RequestParamMethodArgumentResolver@RequestParam 标注参数从http 请求参数中获取值
RequestResponseBodyMethodProcessor@RequestBody 标注参数提取 body 数据,转换为参数类型
  • step2:通过反射调用处理请求的目标方法,获取方法的返回值

也就是调用 controller 中的@RequestMapping 标注的方法org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

调用地方:调用处理请求的实际方法的时候+方法有异常的时候,异常解析器里面

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    // 通过反射调用目标方法,内部会组装目标方法需要的参数
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    // 如果返回值为空,表示目标方法中已经完成了请求的所有处理,表示请求处理结束了,将执行mavContainer.setRequestHandled(true)标记请求处理完毕
    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            mavContainer.setRequestHandled(true);
            return;
        }
    } // 若getResponseStatusReason()不为空,表示请求已经处理过了
    else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }
    // 走到这里,说明有返回值,标记请求未处理完毕
    mavContainer.setRequestHandled(false);
    // 对返回值进行处理
    this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
  • step3:对方法的返回值进行处理
  • 会对上述反射调用的结果 returnValue 进行处理,org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                              ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    // 根据返回值找到HandlerMethodReturnValueHandler
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
    }
    // 调用HandlerMethodReturnValueHandler#handleReturnValue处理返回值
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
    // 根据返回值判断是否是异步请求
    boolean isAsyncValue = isAsyncReturnValue(value, returnType);
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
            continue;
        }
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}

HandlerMethodReturnValueHandler这个接口用来处理返回值

public interface HandlerMethodReturnValueHandler {
    // 是否能够处理 returnType 参数指定的返回值
    boolean supportsReturnType(MethodParameter returnType);
    // 处理返回值
    void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

此接口有很多实现类,列举几个

实现类说明
ViewNameMethodReturnValueHandler返回值为视图名称时的解析器
RequestResponseBodyMethodProcessor方法上标注有@ResponseBody 注解时返回值的解析器
MapMethodProcessor返回值为 Map 的解析器
StreamingResponseBodyReturnValueHandler返回值为 ResponseEntity 类型时的解析器
6、调用拦截器的 postHandle 方法
// 调用拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);

逆序调用拦截器的postHandle方法

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
    for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        interceptor.postHandle(request, response, this.handler, mv);
    }
}
7、渲染视图

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

 // 7处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                   @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                   @Nullable Exception exception) throws Exception {
    boolean errorView = false;
    if (exception != null) {
        Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
        // 如果有异常,进行全局异常处理
        mv = processHandlerException(request, response, handler, exception);
        errorView = (mv != null);
    }
    if (mv != null && !mv.wasCleared()) {
        // 渲染视图
        render(mv, request, response);
        if (errorView) {
            //调用request.removeAttribute方法清理request中错误信息
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    if (mappedHandler != null) {
        // 调用拦截器的afterCompletion方法
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

这个方法主要干了 3 个事情

  • step1:如果有异常,进行全局异常处理
if (exception != null) {
    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
    // 7-1如果有异常,进行全局异常处理
    mv = processHandlerException(request, response, handler, exception);
    errorView = (mv != null);
}

org.springframework.web.servlet.DispatcherServlet # processHandlerException

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
   @Nullable Object handler, Exception ex) throws Exception {
    // 调用处理器异常解析器解析异常,得到ModelAndView
    ModelAndView exMv = null;
    if (this.handlerExceptionResolvers != null) {
        for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
            exMv = resolver.resolveException(request, response, handler, ex);
            if (exMv != null) {
                break;
            }
        }
    }
    if (exMv != null) { // 暴露异常信息到request对象中(request.setAttribute)
        WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
        return exMv;
    }
    throw ex;
}

HandlerExceptionResolver 接口:处理器异常解析器,内部就只有一个方法,用来解析异常的,得到一个 ModelAndView 对象。

public interface HandlerExceptionResolver {
    @Nullable
    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
  • step2:渲染视图
    render(mv, request, response);
// 7-2 渲染视图
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
        // 调用视图解析器解析视图名称得到视图View对象
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
    } else {
        view = mv.getView();
    }
    // 调用视图的render方法渲染视图,将结果输出到客户端
    view.render(mv.getModelInternal(), request, response);
}

此方法干了 2 件事

  • 调用视图解析器解析视图名称得到视图 View 对象
  • 调用视图的 render 方法渲染视图,将结果输出到客户端
// 调用视图解析器解析视图名称得到视图 View 对象
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
   Locale locale, HttpServletRequest request) throws Exception {
    if (this.viewResolvers != null) {
        for (ViewResolver viewResolver : this.viewResolvers) {
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
    }
    return null;
}
// 调用视图的 render 方法渲染视图,将结果输出到客户端
view.render(mv.getModelInternal(), request, response);
  • step3:调用拦截器的 afterCompletion 方法
    mappedHandler.triggerAfterCompletion(request, response, null);
// 反向调用拦截器的afterCompletion方法
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);
        }
    }
}

四、扩展:过滤器和拦截器的区别是什么

在这里插入图片描述

在Tomcat中,一次请求会先进入到Tomcat容器,然后经过Filter的处理,处理通过之后才会进入到Servlet容器,进入到Servlet容器之后,才会在Servlet执行的前后执行Intercepter。

维度不同:过滤器是基于Java Servlet规范实现的,可以通过配置web.xml文件进行实现。拦截器是基于Spring框架实现的,可以通过定义拦截器类实现。拦截器可以对请求进行更加精细的控制,例如进行AOP、权限控制、事务管理等操作。

定义一个过滤器

需要实现javax.servlet.Filter接口

public class LoginFilter implements Filter {
    public void init(FilterConfig config) throws ServletException { // 初始化 }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        // 登录验证和处理
        chain.doFilter(req, resp);
    }
    public void destroy() { // 销毁 }
}

并且需要在web.xml文件中把他配置上:

<filter>
    <filter-name>LoginFilter</filter-name>
    <filter-class>com.jd.LoginFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>LoginFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
定义一个拦截器

需要实现org.springframework.web.servlet.HandlerInterceptor接口:

@Component
public class LoginInterceptor 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 {
        // 完成处理
    }
}

并且在SpringMVC中进行配置:

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/api/*"/>
        <bean class="com.jd.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

或者

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/api/*");
    }
}

五、扩展:SpringMVC是如何将不同的Request路由到不同Controller中的?

根据上面的学习,可以将问题转化为:不同的request如何拿到对应的HandlerMethod

SpringMvc启动会把带有@RequestMapping注解的方法和类封装成一个RequestMappingInfoHandlerMethod,然后注册到MappingRegistry。当HttpServletRequest访问时,会通过AbstractHandlerMethodMapping#lookupHandlerMethod方法获取对应的HandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    // 1.先通过url获取到对应的RequestMappingInfo集合
    List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    if (directPathMatches != null) {
        // 2.把RequestMappingInfo和HandlerMethod放到match里面
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    }
    if (!matches.isEmpty()) {
        Match bestMatch = matches.get(0);
        // 3.如果匹配到多个Match(譬如url相同但是方法不同),则通过RequestMappingInfo中的各种condition匹配出对应的bestMatch
        if (matches.size() > 1) {
        }
        // 4.获取match中的HandlerMethod
        return bestMatch.getHandlerMethod();
    } else {
        return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
    }
}

假设我们有两个 HandlerMethod:

  1. 第一个HandlerMethod使用了@RequestMapping("/users/{id}")
  2. 第二个HandlerMethod使用了@RequestMapping("/users/123")

对于请求 /users/123,Spring MVC 会比较这两个 HandlerMethod 的 RequestMappingInfo 中的 PathPatternsRequestCondition 条件,以确定哪一个是最匹配的。

  • 第一个HandlerMethod的PathPatternsRequestCondition与请求/users/123的匹配度较低,因为它需要从路径中解析{id}
  • 第二个HandlerMethod的PathPatternsRequestCondition与请求/users/123的匹配度较高,因为它是直接匹配的。

一个http请求中,携带有不同的信息,如url,method,header等等,SpringMVC通过Match类统一封装所有的RequestMappingInfo中的各种condition,同时利用compare方法,直接比较出最优的那个handlerMethod。

六、总结

深入阅读代码有助于我们全面了解代码的执行流程,从而在开发过程中做到心中有数。这样的习惯不仅能够帮助我们在编写代码时更加自信,还能让我们在遇到bug时迅速定位问题,从容应对。因此,深入研究代码不仅是提升个人技能的有效途径,也是确保项目顺利进行的重要保障。

如有错误,还请指正,谢谢大家!


在这里插入图片描述
感谢大家的观看!!!创作不易,如果觉得我写的好的话麻烦点点赞👍支持一下,谢谢!!!
相关文章已在掘金发布,体验更佳!!

  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值