Spring mvc源码解析

作者: 蓝梅 | 来源:发表于2021-07-04 14:05 被阅读0次

    一、Spring MVC中重要角色

    1.DispatcherServlet:前端控制器,接受所有web.xml中配置的请求,处理整个请求流程
    2.HandlerMapping:处理映射器,根据请求的URL,找到对应的Handler,包括定义的拦截器
    3.HandlerAdapter:Handler处理器适配器,处理不同类型的请求,主要包括有HttpReqeustHandlerAdapter、SimpleServletHandlerAdapter和SimpleControllerHandlerAdapter,这三种都比较简单,都是通过方法直接调用;RequestMappingHandlerAdapter这个处理器就是我们常用的注解形式,使用RequestMapping注解时的处理器,这种类型就是使用反射的方式调用;
    4.Controller:后端处理器,用来接受请求,处理后端业务逻辑
    5.ViewResolver:视图解析器,把逻辑视图解析成真正的物理视图
    6.View:视图,将数据展现给用户

    二、大致流程

    我们先来看图 Spring mvc执行过程

    1.用户发起请求到前端控制器(DispatcherServlet)
    2.前端控制器会找到处理映射器(HandlerMapping)根据请求的URL,找到相应的处理器执行链
    3.前端控制器找到处理器执行链后,再找到处理器适配器,找到处理器
    4.执行处理器
    5.处理器会返回一个ModelAndView
    6.前端控制器会请求视图解析器解析视图
    7.视图渲染,返回到用户页面

    三、Spring MVC初始化流程源码解析

    之前的所有源码分析,把整个过程的代码都粘贴上来了,感觉效率不高,很多一些不重要的代码占了大量的篇幅,导致整篇看下来抓不住重点;这次源码只粘贴重要的代码,这样过程就会很清晰。
    首先Spring MVC初始化,是从我们再web.xml中配置DispatcherServlet,开始;DispatcherServlet继承了FrameworkServlet,FrameworkServlet继承了HttpServletBean,所以最开始的初始化是从HttpServletBean中的init()方法开始的

    public final void init() throws ServletException {
        ...
        //我们主要看这个初始化的方法
        initServletBean();
        ...
    }
    protected final void initServletBean() throws ServletException {
        ...
        try {
            //初始WebApplicationContext容器
            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;
        }
        ...
    }
    
    protected WebApplicationContext initWebApplicationContext() {
        //获取父容器,如果我们配置了Spring的配置文件,会在这一步获取到Spring的容器
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
        ...
        if (wac == null) {
            //创建容器,把父容器传进去
            wac = createWebApplicationContext(rootContext);
        }
        if (!this.refreshEventReceived) {
            onRefresh(wac);
        }
        if (this.publishContext) {
            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;
    }
    
    protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
        //获取到容器类型,我们是配置在web.xml中的,使用的是XmlWebApplicationContext容器类型
        Class<?> contextClass = getContextClass();
        ...
        //实例化容器
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
        wac.setEnvironment(getEnvironment());
        //设置父容器
        wac.setParent(parent);
        //获取到我们的mvc配置文件
        String configLocation = getContextConfigLocation();
        if (configLocation != null) {
            wac.setConfigLocation(configLocation);
        }
        //根据配置初始化容器
        configureAndRefreshWebApplicationContext(wac);
        return wac;
    }
    
    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        ...
        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        wac.setNamespace(getNamespace());
        //主要是看这一步,这里注册了一个容器初始化后的监听器,这一步是mvc初始化的关键
        wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
        }
        postProcessWebApplicationContext(wac);
        applyInitializers(wac)
        //初始化Spring容器,这个我们再spring源码分析的时候,分析过spring的加载过程,这里就不进去看了
        wac.refresh();
    }
    

    这里就是容器初始化,我们主要是看到ContextRefreshListener这个监听器的注册,接下来,我们看看ContextRefreshListener这个监听器做了什么

    /**
     *我们看到ContextRefreshListener这个监听器,监听了ContextRefreshedEvent这个事件
     *也就是spring容器初始化完成的事件
     **/
    private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            //执行了FrameworkServlet的onApplicationEvent方法
            FrameworkServlet.this.onApplicationEvent(event);
        }
    }
    public void onApplicationEvent(ContextRefreshedEvent event) {
        this.refreshEventReceived = true;
        //执行初始化mvc容器的方法
        onRefresh(event.getApplicationContext());
    }
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }
    protected void initStrategies(ApplicationContext context) {
        //初始化上传文件解析器
        initMultipartResolver(context);
        //初始化本地化解析器
        initLocaleResolver(context);
        //初始化主题解析器
        initThemeResolver(context);
        //初始化Handler映射器处理器组件
        initHandlerMappings(context);
        //初始化处理器适配器
        initHandlerAdapters(context);
        //初始化处理器异常解析器
        initHandlerExceptionResolvers(context);
        //初始化视图解析器
        initRequestToViewNameTranslator(context);
        //初始化视图组件
        initViewResolvers(context);
        //初始化分布管理者
        initFlashMapManager(context);
    }
    

    到这一步整个spring mvc初始化就完成了

    四、spring mvc请求流程

    因为DispatcherServlet继承了FrameworkServlet,FrameworkServlet继承了HttpServletBean,HttpServletBean继承了HttpServlet,也就是请求进来时,会调用到doPost或者doGet方法去,FrameworkServlet覆盖了这几个方法,我们看下doGet方法

    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //无论是doPost还是doGet都是调用processRequest方法
        processRequest(request, response);
    }
    
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
        ...
        try {
            //执行doService方法
            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");
                    }
                }
            }
            //发布ServletRequestHandledEvent事件
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }
    
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ...
        //设置request属性,所以我们可以在controller中获取到下面四个属性
        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());
        
        ...
        try {
            //开始进入doDispatch,这个就是前段控制器最重要的方法了,所有流程都在这个方法中
            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);
                }
            }
        }
    }
    //这个方法我们就不省略代码了,这样流程会清晰一点
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        //handler执行链,这个里会封装所有拦截器的集合,还有执行的handler
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
    
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
    
            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
                //从映射器中获取处理器执行链
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                //从处理器适配器中获取处理器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                //获取请求方式
                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;
                    }
                }
                //执行拦截器的preHandle方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                //使用处理器执行相应的handle,也就是我们的业务代码
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                //视图处理(页面渲染)
                applyDefaultViewName(processedRequest, mv);
                //执行拦截器postHandle方法
                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 {
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }
    

    Spring MVC的处理流程就结束了,太细节的东西就不讲了,大家自己有时间可以阅读下源码,例如怎么获取处理器执行链的,怎么获取处理器的,还有各种处理器是怎么去处理不同方式的请求的,带着这些问题去阅读源码。

    相关文章

      网友评论

        本文标题:Spring mvc源码解析

        本文链接:https://www.haomeiwen.com/subject/fqymultx.html