zoukankan      html  css  js  c++  java
  • @Spring MVC请求处理流程

      大部分java应用都是web应用,展现层是web应用的重要的部分。Spring为展现层提供了一个优秀的Web MVC框架——Spring MVC。它基于MVC的设计理念,此外,它采用了松散耦合、可插拔的组件结构,比其他MVC框架更具扩展性和灵活性。

      SpringMVC通过一套MVC注解,让POJO成为处理请求的处理器,无需实现任何接口,同时,Spring MVC还支持REST风格的URL请求。注解驱动以及REST风格的Spring MVC是Spring的出色功能之一,此外,SpringMVC在数据绑定、视图解析、本地化处理及静态资源处理上都有许多不俗的表现。

    在Spring MVC框架中,DispatcherServlet处于核心的位置,它接收所有的请求,负责协调和组织不同组件以完成请求处理返回响应的工作

    用过python Django框架的都知道Django对于访问方式的配置就是,一个url路径和一个函数配对,你访问这个url,就会直接调用对应的函数,简单明了。对于java的面向对象来说,就要分两步走。第一步首先要找到是哪个对象,即handler,即我们写的action或是controller。第二步要找到访问的函数,即action中的方法。所以就出现了两个源码接口HandlerMappingHandlerAdapter,前者负责第一步,后者负责第二步。

    Spring MVC处理请求的整体过程如下:
    1.客户端发出一个HTTP请求,如果这个请求与web.xml中指定的DispatcherServlet的请求映射路径相匹配,web容器就将该请求转交给DispatcherServlet继续处理。
    2.DispatcherServlet接收到这个请求后,根据这个请求的信息(比如请求的URL、请求的方式、请求报文头、请求参数等)及HandlerMapping的配置找到处理请求的处理器(Handler)。值得注意的是:Spring MVC中并没有定义一个Handler接口,实际上任何一个Object都可以成为请求处理器。
    3.当DispatcherServlet根据HandlerMapping得到对应当前请求的Handler后,通过HandlerAdapter对Handler进行封装,再以统一的适配器接口调用Handler。 HandlerAdapter是Spring MVC的框架级接口,顾名思义,HandlerAdapter是一个适配器,它用统一的接口对各种Handler方法进行调用
    4.处理器完成业务逻辑的处理后将返回一个ModelAndView给DispatcherServlet,ModelAndView包含了逻辑视图名和模型数据信息。
    5.ModelAndView对象中包含的是"逻辑视图名"而非真正的视图对象,DispatcherServlet通过ViewResolver完成逻辑视图真实视图对象的解析工作。
    6.当得到真实的视图对象View后,DispatcherServlet就使用这个View对象对ModelAndView中的模型数据进行视图渲染。
    7.最终将响应消息返回给客户端,可能是一个普通的HTML页而,也可能是一个XML或JSON串,甚至是一张图片或一个PDF文档等不同的媒体形式。

    Spring究竟如何将上下文中的Spring MVC组件Bean装配到DispatcherServlet实例中呢?通过查看DispatcherServlet的initStrategies()方法体中的代码,一切的真相就大白于天下了:

    /** MultipartResolver used by this servlet */
    private MultipartResolver multipartResolver;
    /** LocaleResolver used by this servlet */
    private LocaleResolver localeResolver;
    /** ThemeResolver used by this servlet */
    private ThemeResolver themeResolver;
    /** List of HandlerMappings used by this servlet */
    private List<HandlerMapping> handlerMappings;
    /** List of HandlerAdapters used by this servlet */
    private List<HandlerAdapter> handlerAdapters;
    /** List of HandlerExceptionResolvers used by this servlet */
    private List<HandlerExceptionResolver> handlerExceptionResolvers;
    /** RequestToViewNameTranslator used by this servlet */
    private RequestToViewNameTranslator viewNameTranslator;
    /** FlashMapManager used by this servlet */
    private FlashMapManager flashMapManager;
    /** List of ViewResolvers used by this servlet */
    private List<ViewResolver> viewResolvers;
    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
        // 初始化上传文件解析器
        initMultipartResolver(context);
        // 初始化本地化解析器
        initLocaleResolver(context);
        // 初始化主题解析器
        initThemeResolver(context);
        // 初始化处理器映射器
        initHandlerMappings(context);
        // 初始化处理器适配器 
        initHandlerAdapters(context);
        // 初始化处理器异常解析器 
        initHandlerExceptionResolvers(context);
        // 初始化请求到视图名翻译器
        initRequestToViewNameTranslator(context);
        // 初始化视图解析器 
        initViewResolvers(context);
        initFlashMapManager(context);
    }

    initStrategies()方法会在DispatcherServlet对应的WebApplicationContext初始化后自动执行,此时Spring上下文中的Bean已经初始化完毕。该方法的工作原理是通过反射机制查找并装配Spring容器中用户显式定义的组件bean,如果找不到,则装配默认的组件实例。

    Spring MVC定义了一套默认的组件实现类,也就是说即使不在Spring容器中显式定义组件Bean,也会有一套可用的默认组件出现在DispatcherServlet中。Spring在spring-webmiv.jar包的org/springframework/web/servlet类路径定义了一个DispatcherServlet.properties配置文件,在其中指定了组件的默认组件实现类:

    # Default implementation classes for DispatcherServlet's strategy interfaces.
    # Used as fallback when no matching beans are found in the DispatcherServlet context.
    # Not meant to be customized by application developers.
    
    org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
    
    org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
    
    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
        org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
    
    org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,
        org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,
        org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
    
    org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,
        org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,
        org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
    
    org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
    
    org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
    
    org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

    如果用户希望采用非默认类型的组件,则只需在Spring配置文件中配置自定义的组件Bean就可以,Spring MVC一旦发现上下文中有用户自定义的组件,就不会使用默认的组件了。

    看一下Dispatcher中的doDispatcher源码

    //前端控制器分派方法
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            HandlerExecutionChain mappedHandler = null;
            int interceptorIndex = -1;
    
            try {
                ModelAndView mv;
                boolean errorView = false;
    
                try {
                       //检查是否是请求是否是multipart(如文件上传),如果是将通过MultipartResolver解析
                    processedRequest = checkMultipart(request);
                       //步骤2、请求到处理器(页面控制器)的映射,通过HandlerMapping进行映射
                    mappedHandler = getHandler(processedRequest, false);
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        noHandlerFound(processedRequest, response);
                        return;
                    }
                       //步骤3、处理器适配,即将我们的处理器包装成相应的适配器(从而支持多种类型的处理器)
                    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
                      // 304 Not Modified缓存支持
                    //此处省略具体代码
    
                    // 执行处理器相关的拦截器的预处理(HandlerInterceptor.preHandle)
                    //此处省略具体代码
    
                    // 步骤4、由适配器执行处理器(调用处理器相应功能处理方法)
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
                    // Do we need view name translation?
                    if (mv != null && !mv.hasView()) {
                        mv.setViewName(getDefaultViewName(request));
                    }
    
                    // 执行处理器相关的拦截器的后处理(HandlerInterceptor.postHandle)
                    //此处省略具体代码
                }
                catch (ModelAndViewDefiningException ex) {
                    logger.debug("ModelAndViewDefiningException encountered", ex);
                    mv = ex.getModelAndView();
                }
                catch (Exception ex) {
                    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                    mv = processHandlerException(processedRequest, response, handler, ex);
                    errorView = (mv != null);
                }
    
                //步骤5 步骤6、解析视图并进行视图的渲染
    //步骤5 由ViewResolver解析View(viewResolver.resolveViewName(viewName, locale))
    //步骤6 视图在渲染时会把Model传入(view.render(mv.getModelInternal(), request, response);)
                if (mv != null && !mv.wasCleared()) {
                    render(mv, processedRequest, response);
                    if (errorView) {
                        WebUtils.clearErrorRequestAttributes(request);
                    }
                }
                else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                                "': assuming HandlerAdapter completed request handling");
                    }
                }
    
                // 执行处理器相关的拦截器的完成后处理(HandlerInterceptor.afterCompletion)
                //此处省略具体代码
    
    
            catch (Exception ex) {
                // Trigger after-completion for thrown exception.
                triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
                throw ex;
            }
            catch (Error err) {
                ServletException ex = new NestedServletException("Handler processing failed", err);
                // Trigger after-completion for thrown exception.
                triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
                throw ex;
            }
    
            finally {
                // Clean up any resources used by a multipart request.
                if (processedRequest != request) {
                    cleanupMultipart(processedRequest);
                }
            }
        }

     

  • 相关阅读:
    Linq 和 Lambda 查询中按照多个值进行分组GroupBy
    enter键触发事件的清除
    3、Python 基础类型 -- List 列表类型
    2、Python 基础类型 -- String 字符串类型
    1、Python 基础类型 -- Number 数字类型
    JMeter 常用网站
    性能测试之基础理论
    JMeter 性能测试实例
    JMeter 服务器监控插件环境配置
    【C++】利用指针实现通过函数改变多个参数的值
  • 原文地址:https://www.cnblogs.com/winner-0715/p/7198419.html
Copyright © 2011-2022 走看看