Spring MVC源码详解

本文深入探讨Spring MVC的内部机制,从DispatcherServlet的初始化到请求处理流程,详述HandlerMapping和HandlerAdapter的角色。了解Spring MVC如何映射请求、调用控制器并返回响应。
摘要由CSDN通过智能技术生成

本文将讲解Spring MVC的工作流程,Spring MVC的入口是DispatcherServlet,下面我们来看下DispatcherServlet的继承结构。
在这里插入图片描述
从上图我们可以看出DispatcherServlet实际上就是一个Servlet。

DispatcherServlet初始化

既然我们知道了DispatcherServlet是一个Servlet,那么在Spring MVC初始化的时候就会调用Servlet的init方法。因此我们顺着这个继承体系结构找init方法。在子类GenericServlet中实现了Servlet接口的init方法,并且还实现了一个空的init方法提供给子类覆写。

public void init(ServletConfig config) throws ServletException {
    this.config = config;
    // 是一个钩子方法用来给子类覆盖的。
    this.init();
}

因此我们看到这个空的init方法,我们发现在HttpServletBean中覆写了这个方法。

@Override
public final void init() throws ServletException {
    if (logger.isDebugEnabled()) {
        logger.debug("Initializing servlet '" + getServletName() + "'");
    }

    // Set bean properties from init parameters.
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }

    // Let subclasses do whatever initialization they like.
    // 注意重点来了,这又是一个模板方法提供给子类覆写
    initServletBean();

    if (logger.isDebugEnabled()) {
        logger.debug("Servlet '" + getServletName() + "' configured successfully");
    }
}

我们看到initServletBean这个方法,在FrameworkServlet中被覆写。在这个方法里面调用了initWebApplicationContext方法进行初始化。

@Override
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        // 这里是初始化的重点,初始化ApplicationContext
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
    }
    catch (ServletException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }
    catch (RuntimeException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                         elapsedTime + " ms");
    }
}

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    if (this.webApplicationContext != null) {
        // A context instance was injected at construction time -> use it
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent -> set
                    // the root application context (if any; may be null) as the parent
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        // No context instance was injected at construction time -> see if one
        // has been registered in the servlet context. If one exists, it is assumed
        // that the parent context (if any) has already been set and that the
        // user has performed any initialization such as setting the context id
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        // No context instance is defined for this servlet -> create a local one
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
        // 这里同样是个模板方法,这个就是我们的核心了,被DispatcherServlet覆写,从而进入了我们的入口,去执行真正的初始化
        onRefresh(wac);
    }

    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                              "' as ServletContext attribute with name [" + attrName + "]");
        }
    }

    return wac;
}

从上面代码我们可以发先在一定条件下会执行onRefresh方法,而DispatcherServlet通过重写这个方法就开始真正执行初始化SpringMVC操作了。

@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
    // 文件上传
    initMultipartResolver(context);
    // 当前环境处理器,可以对不同区域的用户显示不同的结果
    initLocaleResolver(context);
    // 主题解析
    initThemeResolver(context);
    // 将请求映射到处理器
    initHandlerMappings(context);
    // 处理器适配器,进行多类型参数的动态匹配
    initHandlerAdapters(context);
    // 根据处理请求过程中的异常设置view
    initHandlerExceptionResolvers(context);
    // 根据viewname查找view
    initRequestToViewNameTranslator(context);
    // 页面渲染解析器
    initViewResolvers(context);
    // 用来管理FlashMap的,FlashMap主要用在redirect中传递参数
    initFlashMapManager(context);
}

上面initStrategies方法初始化了SpringMVC的九大组件,至此初始化工作全部完成,详细的内容读者可以自行深入了解。

DispatcherServlet处理流程

Servlet处理请求主要是调用service方法去处理。我们发现在HttpServlet中实现了该方法,并且根据不同类型将请求路由到doGet、doHead、doPost、doPut、doDelete、doOption、doTrace,并做了doHead、doOption、doTrace的默认实现。

在 FrameworkServlet重写了service、doGet、doPost、doPut、doDelete、doOption、doTrace,在service增加了对PATCH类型的处理。并且我们发现,在这些重写的方法里面都调用了processRequest方法,毫无疑问这个就是我们要关注的重点。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;

    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = buildLocaleContext(request);

    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    initContextHolders(request, localeContext, requestAttributes);

    try {
        // 注意注意注意,模板方法提供给子类覆写
        doService(request, response);
    }
    catch (ServletException | IOException ex) {
        failureCause = ex;
        throw ex;
    }
    catch (Throwable ex) {
        failureCause = ex;
        throw new NestedServletException("Request processing failed", ex);
    }

    finally {
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }

        if (logger.isDebugEnabled()) {
            if (failureCause != null) {
                this.logger.debug("Could not complete request", failureCause);
            }
            else {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    logger.debug("Leaving response open for concurrent processing");
                }
                else {
                    this.logger.debug("Successfully completed request");
                }
            }
        }

        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

我们发现在processRequest中有个模板方法doService,这个方法被DispatcherServlet覆写了,毫无疑问这个就是方法就是DispatcherServlet处理Htpp请求的方法。

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (logger.isDebugEnabled()) {
        String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
        logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
                     " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
    }

    // Keep a snapshot of the request attributes in case of an include,
    // to be able to restore the original attributes after the include.
    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        attributesSnapshot = new HashMap<>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    // Make framework objects available to handlers and view objects.
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

    if (this.flashMapManager != null) {
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    }

    try {
        // 前面对htpp请求进行了一大堆的属性赋值,然后调用了这个方法,那么这个方法就是真正执行请求处理的地方
        doDispatch(request, response);
    }
    finally {
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Restore the original attribute snapshot, in case of an include.
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
    }
}

我们发现doService方法中前面对htpp请求进行了一大堆的属性赋值,然后调用了doDispatch这个方法,那么这个方法就是真正执行请求处理的地方,至此DispatcherServlet的处理流程到此结束。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            // 1.检查是否是文件上传的请求
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // Determine handler for the current request.
            // 2.取得处理当前请求的controller,这里也称为hanlder,处理器,
            // 	 第一个步骤的意义就在这里体现了.这里并不是直接返回controller,
            //	 而是返回的HandlerExecutionChain请求处理器链对象,
            //	 该对象封装了handler和interceptors.
            mappedHandler = getHandler(processedRequest);
            // 如果handler为空,则返回404
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request.
            //3. 获取处理request的处理器适配器handler adapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            // 处理 last-modified 请求头
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (logger.isDebugEnabled()) {
                    logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                }
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Actually invoke the handler.
            // 4.实际的处理器处理请求,返回结果视图对象
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            // 结果视图对象的处理
            applyDefaultViewName(processedRequest, mv);
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            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()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                // 请求成功响应之后的方法
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

Spring MVC处理请求过程

在这里插入图片描述
具体步骤:

1、发送请求,被DispatcherServlet接收到。

2、DispatcherServlet将请求交给HandlerMapping处理器映射器,HandlerMapping根据请求的url获取到对应的Handler(Controller处理器)。

3、然后HandlerAdapter处理器适配器,通过调用supports方法得到适合处理Handler的适配器,然后调用适配器的handle方法通过反射调用对应处理器的方法,并返回ModelAndView。

4、最后通过ViewResolvers视图解析器将ModelAndView解析成试图(View)放回给前端。

5、前端对View进行视图渲染。

Spring MVC中的核心角色

HandlerMapping

是一个接口,主要记录了Url和Controller的对应关系。每次处理请求就是从HandlerMapping中获取对应的Controller。该接口主要有以下几个实现类。

BeanNameUrlHandlerMapping :通过对比url和bean的name找到对应的对象

SimpleUrlHandlerMapping :也是直接配置url和对应bean,比BeanNameUrlHandlerMapping功能更多

DefaultAnnotationHandlerMapping : 主要是针对注解配置@RequestMapping的,已过时

RequestMappingHandlerMapping :取代了上面一个

HandlerAdapter

同样是一个接口,主要是根据HandlerMapping获取的Controller,找到一个可以处理Controller的HandlerAdapter,然后通过反射调用Controller的具体方法获得ModelAndView。主要的实现类有。

AnnotationMethodHandlerAdapter主要是适配注解类处理器,注解类处理器就是我们经常使用的@Controller的这类处理器

HttpRequestHandlerAdapter主要是适配静态资源处理器,静态资源处理器就是实现了HttpRequestHandler接口的处理器,这类处理器的作用是处理通过SpringMVC来访问的静态资源的请求。

SimpleControllerHandlerAdapter是Controller处理适配器,适配实现了Controller接口或Controller接口子类的处理器,比如我们经常自己写的Controller来继承MultiActionController.

SimpleServletHandlerAdapter是Servlet处理适配器,适配实现了Servlet接口或Servlet的子类的处理器,我们不仅可以在web.xml里面配置Servlet,其实也可以用SpringMVC来配置Servlet,不过这个适配器很少用到,而且SpringMVC默认的适配器没有他,默认的是前面的三种。

HttpRequestHandlerAdapter主要是适配静态资源处理器,静态资源处理器就是实现了HttpRequestHandler接口的处理器,这类处理器的作用是处理通过SpringMVC来访问的静态资源的请求。

SimpleControllerHandlerAdapter是Controller处理适配器,适配实现了Controller接口或Controller接口子类的处理器,比如我们经常自己写的Controller来继承MultiActionController.

SimpleServletHandlerAdapter是Servlet处理适配器,适配实现了Servlet接口或Servlet的子类的处理器,我们不仅可以在web.xml里面配置Servlet,其实也可以用SpringMVC来配置Servlet,不过这个适配器很少用到,而且SpringMVC默认的适配器没有他,默认的是前面的三种。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值