zoukankan      html  css  js  c++  java
  • Spring MVC知识点整理

    网上Spring MVC相关知识点的介绍已经有很多了,但是大部分文章都是介绍其中的一部分知识点。
    本文希望能够向读者做一个基本整体的介绍,首先我们先来了解下Spring MVC的基础接口和组件。
     
    一、DispatcherServlet VS ContextLoaderListener
    首先我们需要区分DispatcherServlet和ContextLoaderListener,在web应用程序中有两种类型的容器。
    一个容器负责初始化应用程序上下文,另一个容器负责初始化web应用的上下文。
    其中,Application Context是通过ContextLoaderListener来进行初始化的。
    其配置实例如下,
    <listener>
         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <context-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath:*-context.xml</param-value>
    </context-param>
    Application Context主要包含service组件、DAO组件以及其他的有可能被多个应用重复利用的组件。
    而web application context则是通过DispatcherServlet来进行初始化的,它是Application Context的子上下文,
    所有在Application Context中加载的bean都可以被web application context引用到,
    因此,我们最好能够将应用服务组件、DAO组件与controller组件、View resolver组件区分对待。
    其具体配置样例如下,
    <servlet>
          <servlet-name>platform-services</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:platform-services-servlet.xml</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
    </servlet>
     
    二、Servlet Filter vs Spring Interceptor
    Filter和Interceptor都可以实现请求日志记录和身份验证的功能。
    但是两者的区别也是很明显的,简单来讲,Filter是由web容器来管理的,Interceptor是通过Spring来管理的。
    因此,Filter只能够用在web应用中,但是Interceptor可以用在任意的组件中;
    另外,因为Interceptor是通过Spring来进行管理的,因此Interceptor可以利用由Spring加载和生成的bean。
    利用Filter我们可以对用户请求进行预处理,以及对响应进行后处理。
    Filter的具体配置实例,可以参考:http://www.cnblogs.com/Fskjb/archive/2010/03/27/1698448.html
    而利用Interceptor我们可以在preHandle,postHandle以及afterCompletion三个点进行拦截。
    Interceptor接口定义如下:
    Interceptor的具体调用是在DispatcherServlet中进行的,具体代码实现如下:
    /**
          * Process the actual dispatching to the handler.
          * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
          * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
          * to find the first that supports the handler class.
          * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
          * themselves to decide which methods are acceptable.
          * @param request current HTTP request
          * @param response current HTTP response
          * @throws Exception in case of any kind of processing failure
          */
         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 {
                          processedRequest = checkMultipart(request);
     
                          // Determine handler for the current request.
                          mappedHandler = getHandler(processedRequest, false);
                          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()) {
                                    String requestUri = urlPathHelper.getRequestUri(request );
                                     logger.debug( "Last-Modified value for [" + requestUri + "] is: " + lastModified);
                               }
                                if ( new ServletWebRequest( request, response).checkNotModified( lastModified) && isGet) {
                                     return;
                               }
                         }
                          1. 在handler执行之前,获取注册到相关handler的interceptor列表,调用的顺序与配置的顺序一致
                          // Apply preHandle methods of registered interceptors.
                         HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
                          if ( interceptors != null) {
                                for ( int i = 0; i < interceptors. length; i++) {
                                    HandlerInterceptor interceptor = interceptors[ i];
                                     if (!interceptor.preHandle(processedRequest , response , mappedHandler.getHandler())) {
                                         2. 如果interceptor.preHandle执行结果为false,则触发afterCompletion方法的调用,并直接返回
                                         triggerAfterCompletion( mappedHandler, interceptorIndex , processedRequest, response , null);
                                          return;
                                    }
                                     interceptorIndex = i;
                               }
                         }
     
                          // Actually invoke the handler.
                          mv = ha.handle( processedRequest, response, mappedHandler.getHandler());
     
                          // Do we need view name translation?
                          if ( mv != null && ! mv.hasView()) {
                               mv.setViewName(getDefaultViewName( request));
                         }
                         3. handler执行完业务逻辑处理后,逆序调用interceptor列表来执行postHandle方法
                          // Apply postHandle methods of registered interceptors.
                          if ( interceptors != null) {
                                for ( int i = interceptors. length - 1; i >= 0; i--) {
                                    HandlerInterceptor interceptor = interceptors[ i];
                                     interceptor.postHandle( processedRequest, response, mappedHandler.getHandler(), mv );
                               }
                         }
                    }
                     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);
                    }
     
                     // Did the handler return a view to render?
                     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");
                         }
                    }
                    4. 完成视图的渲染之后,触发interceptor的afterCompletion方法,逆序执行
                     // Trigger after-completion for successful outcome.
                    triggerAfterCompletion( mappedHandler, interceptorIndex, processedRequest , response , null);
               }
     
                catch (Exception ex) {
                    5. 触发Exception后,执行interceptor的afterCompletion方法
                     // 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);
                    }
               }
         }
    interceptor的配置实例如下:
        <mvc:interceptors >
            <bean class= "com.interceptor.test.authenInterceptor" >
                <property name ="excludeUris">
                    <list >
                        <value >/static/ </value >
                    </list >
                </property >
            </bean >
        </mvc:interceptors >
    

      

    Filter和interceptor在web服务请求中的处理流程,可以参见后面的DipatcherServlet请求处理流程图
     
    三、HandlerMapping vs HandlerAdapter vs HandlerExceptionResolver  vs ViewResolver
    HandlerMapping接口的作用是将request uri映射到对应处理类和方法
    HandlerMapping接口的类继承结构如下所示:
     
    BeanNameUrlHandlerMapping是DispatcherServlet默认使用的HandlerMapping,其配置方式如下:
    <bean name="/hello.htm" class="com.sina.controller.HelloController"/>
    
    <bean name="/sayHello*" class="com.sina.controller.HelloController"/>
    

    SimpleUrlHandlerMapping相对于BeanNameUrlHandlerMapping的优势在与不需要为一个类重复定义bean,而且XML的可读性也更强,其配置实例如下:

    <bean id="helloController" class="com.sina.controller.HelloController"/>
    
      <bean id="urlHandler" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="urlMap">
                <map>
                    <entry key="/hello.htm" value-ref="helloController"/>
                    <entry key="/sayHello*" value-ref="helloController"/>
                    <entry key="/welcome.html" value-ref="helloController"/>
                    <entry key="/welcomeUser*" value-ref="helloController"/>
                </map>
            </property>
        </bean>
    
    然而,我们仍然会发现使用SimpleUrlHandlerMapping不利于相似功能请求的分类管理,因此DefaultAnnotationHandlerMapping被大家广泛应用起来。
    接下来我们看看HandlerMapping是如何被初始化的,其初始化流程是在DispatcherServlet的initHandlerMappings方法中完成。
     
         /**
          * Initialize the HandlerMappings used by this class.
          * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
          * we default to BeanNameUrlHandlerMapping.
          */
         private void initHandlerMappings (ApplicationContext context) {
                this. handlerMappings = null;
    
                if ( this. detectAllHandlerMappings) {
                     //1. 如果detectAllHandlerMappings,则找到所有注册的HandlerMapping bean
                    Map<String, HandlerMapping> matchingBeans =
                               BeanFactoryUtils.beansOfTypeIncludingAncestors( context, HandlerMapping.class, true , false);
                     if (! matchingBeans.isEmpty()) {
                          this. handlerMappings = new ArrayList<HandlerMapping>(matchingBeans .values());
                          //2. 对handlerMappings进行排序,排序依据是注册时的order值
                          OrderComparator.sort(this.handlerMappings);
                    }
               }
               //3. 如果detectAllHandlerMappings为false,则只查找名称为HandlerMapping的bean。
                else {
                     try {
                         HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME , HandlerMapping.class);
                          this. handlerMappings = Collections.singletonList(hm);
                    }
                     catch ( NoSuchBeanDefinitionException ex) {
                          // Ignore, we'll add a default HandlerMapping later.
                    }
               }
    
                // Ensure we have at least one HandlerMapping, by registering
                // a default HandlerMapping if no other mappings are found.
               4. 如果没有在上下文找到一个HandlerMapping bean,则采用默认策略加载,具体策略设置可以在 DispatcherServlet.properties中看到
                if ( this. handlerMappings == null) {
                     this. handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
                     if ( logger.isDebugEnabled()) {
                          logger.debug( "No HandlerMappings found in servlet '" + getServletName() + "': using default");
                    }
               }
         }
    

    HandlerAdapter的作用是调用HandlerMapping映射好的处理类和方法,具体配置方式和初始化流程与HandlerMapping相似。

    HandlerExceptionResolver接口让开发者可以对异常进行处理,返回给调用者更友好、清晰的信息。

    四、DispatcherServlet (请求分发控制器) 初始化处理流程
    接下来,我们来了解下DispatcherServlet是如何被实例化的。
    当一个Servlet容器(例如:tomcat)启动的时候或者该Servlet被使用的时候会调用相应servlet的init(ServletConfig servletConfig),具体加载时机由load-on-startup属性来设置。
     
    其中,ServletConfig接口的继承结构如下:
    四、DispatcherServlet (请求分发控制器) 初始化处理流程
    接下来,我们来了解下DispatcherServlet是如何被实例化的。
    当一个Servlet容器(例如:tomcat)启动的时候或者该Servlet被使用的时候会调用相应servlet的init(ServletConfig servletConfig),具体加载时机由load-on-startup属性来设置。
     
    其中,ServletConfig接口的继承结构如下:
     
     
    web容器会实例化实现了ServletContext的对象以及Servlet对象,通过init(ServletConfig servletConfig)函数传递给DispatcherServlet。
    接下来,DispatcherServlet类的初始化入口init()会被调用,该方法定义在HttpServletBean类中,其具体实现如下:
        public final void init()
            throws ServletException
        {
            if(logger.isDebugEnabled())
                logger.debug((new StringBuilder()).append("Initializing servlet '").append(getServletName()).append("'").toString());
            try
            {
                org.springframework.beans.PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), requiredProperties);
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                org.springframework.core.io.ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(org/springframework/core/io/Resource, new ResourceEditor(resourceLoader, getEnvironment()));
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }
            catch(BeansException ex)
            {
                logger.error((new StringBuilder()).append("Failed to set bean properties on servlet '").append(getServletName()).append("'").toString(), ex);
                throw ex;
            }
            initServletBean();
            if(logger.isDebugEnabled())
                logger.debug((new StringBuilder()).append("Servlet '").append(getServletName()).append("' configured successfully").toString());
        }
    
    在try,catch代码块中,会加载param属性值(具体调用流程后续再补充
    接下来,执行的是FrameworkServlet类的initServletBean方法,该方法的主要目的是初始化WebApplicationContext这个上下文。
    五、Spring MVC处理请求的工作流程
     
    经过上面的基础接口和类的介绍,我们接下来看下Spring MVC在处理web请求时的完整工作流程。
    任何一个MVC框架都必须解决两个关键的问题
    1、URL到处理方法的路由和映射
    2、request和response的生成与输出
    让我们在流程图里面来看Spring MVC是如何解决这个问题的。(后续补充)
     
  • 相关阅读:
    设计模式之抽象工厂模式
    MQ任意延时消息(三)基于服务端实现
    MQ任意延时消息(二)基于客户端实现
    MQ任意延时消息(一)实现原理概述
    sqlyog报错2058
    base标签的作用
    相对路径和绝对路径的解释
    自定义Tomcat部署目录
    常用正则表达式
    接口的结构定义
  • 原文地址:https://www.cnblogs.com/kaiblog/p/5291220.html
Copyright © 2011-2022 走看看