浅谈对Spring mvc的理解和DispatherServlet源码深入分析

本文介绍了Spring MVC的基本概念和体系结构,强调理解其工作流程和组件交互的重要性。详细阐述了Spring MVC请求处理的7个步骤,并深入剖析了DispatcherServlet的源码,特别是doService和doDispatch方法,揭示了如何通过initStrategies方法自动组装Spring MVC组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

Spring mvc是Spring 为展示层提供的一个优秀的Web框架,它基于MVC的设计理念,并采用松耦合、可插拔的组件结构,让它比其他的MVC框架更具有灵活性和扩展性。

体系结构

学习Spring MVC的时候,最重要还是要懂得它里面内部的各个组件是如何运转工作的,我刚开始接触的时候,只想着一门心思去写代码,而忽略了其最精髓的原理理解和逻辑流程,这也导致我在进行Spring mvc配置和编写代码的时候经常出现错误,反而大大地降低了我的开发效率,因此了解Sping mvc的工作流程、体系结构还是对我们非常有帮助的。
下面是spring mvc的一个框架模型图:

这里写图片描述
Spring mvc请求处理过程详解:
(1)首先,客户端向服务器短发送一个HTTP请求,我们的应用服务器接收带这个请求后,先判断是否匹配DispatcherServlet的请求路径映射(这是在配置web.xml的时候指定),匹配成功则进入Spring mvc 的核心组件DispatcherServlet中进行处理
(2)DispatcherServlet接受到请求后,将根据请求信息以及HadlerMapping的路径配置找到处理该请求的处理器Handler(也就是我们写得Controller),在这个 过程中,HandlerMapping相当于一个路由控制器,帮助我们去找到“目标主机”Handler
( 3 )找到Handler后,会通过适配器HandlerAdapter对Handler经行封装,并调用统一的适配器接口调用这个Handler.
(4)处理器完成了业务逻辑后,会返回一个ModelAndView对象给DispatcherServlet,这个对象包含了视图逻辑名和后期用来渲染页面的数据信息。
(5)DispatcherServlet得到这个ModelAndView对象后并不能直接地得到 视图对象,注意上面说地是逻辑视图名,而要获取真正的视图名,DispatcherServlet需要借助视图解析器ViewResolver通过解析得到真正的视图名,从而获得视图对象(比如jsp)
(6)之后DispatcherServlet使用之前的Model数据对这个视图View进行页面渲染。
(7)最后返回客户端,可以是html、pdf、xml、json等。

对DispatcherServlet的源码分析

DispatcherServlet可以说是Spring mvc最核心的一个组件,而Spring是如何将业务层的spring mvc的上下文组件装配在这个统一的DispatcherServlet中的呢?我们可以通过对DispatherServlet的源码分析找到答案,下面是DispatcherServler的一些代码,首先DispatcherServlet是继承FramerworkServlet类,而这个FrameworkServlet类又是继承的HttpServletBean,我们现对这个FrameworkServlet的源码中重要方法进行分析,如下:

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

        String method = request.getMethod();
        if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
            //这一这里
            processRequest(request, response);
        }
        else {
            super.service(request, response);
        }
    }

@Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

//注意这里
        processRequest(request, response);
    }

@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }

通过上面每个servlet的几个重要方法(service、do××),它们都依赖这个processRequest方法,而这个processRequest方法中主要依赖的是doService方法,这个doService方法是由protecte abstrac修饰,因此其具体的逻辑由其子类DispatcherServlet实现

    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异常处理代码
    finally {
        //。。。。省略代码      }
}`

这是doService的源码:

protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;

对FrameworkServlet源码总结:FrameworkServlet 中覆盖了 HttpServlet 的 doGet(),doPost()等方法,而 doGet(),doPost()等又直接调用方法 processRequest 来处理请求,进入 processRequest 方法,实际的请求处理是调用其抽象方法 doService(由子类完成),进入DispatcherServlet源码后,我们可以看见在doService里面执行最后最主要的方法代码如下:

@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<String, Object>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                    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());

        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 {
        //注意这里
            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);
                }
            }
        }
    }

下面是对doDispatch(request,response)源码:

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 {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                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.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

                applyDefaultViewName(request, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Error err) {
            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);
                }
            }
        }
    }

DispatcherServlet 中doService具体实现请求的处理分发,先是把一些资源放到请求属性中,然后调用 doDispatch 实现请求分发到控制器的 handler。doDispatch 中首先会判断是否是文件传输流的请求(利用MultipartResolver),如果是的话就会转为 MultipartHttpServletRequest。接下来 getHandler(processedRequest) 根据请求获得对应的handler,最后调用 handle() 处理请求,会反射到在控制器中实现的方法

好了,我们接着分析DispatcherServlet的initStrategies方法的源码来接之前提出的问题

protected void initStrategies(ApplicationContext context) {
        //初始化上传文件解析器
        initMultipartResolver(context);
         //初始化本地化解析器
        initLocaleResolver(context);
        initThemeResolver(context);
        //初始化处理映射器
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        //初始化视图解析器
        initViewResolvers(context);
        initFlashMapManager(context);
    }

因此我们可以得出结论,Spring 之所以能够将Spring mvc的组件组装到DispatcherServlet中是因为在WebAppicationContext执行后,DispatcherServlet会自动执行它里面的innitStrategies方法,而在这个方法里面,会自动地处理化一系列的组件。

private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";


    /** Additional logger to use when no mapped handler is found for a request. */
    protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);

    private static final Properties defaultStrategies;

    static {
        // Load default strategy implementations from properties file.
        // This is currently strictly internal and not meant to be customized
        // by application developers.
        try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
        }
    }

这个静态代码块表明Spring mvc有一套默认的组件配置,如果我们不对这些组件进行显示地配置,那么这个DispatcherServlet就会初始化实现提供的默认组件配置,我们可以通过调用DispatcherServlet的getDefaultStrategies获取这个默认的组件配置信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值