SpringMVC与Web解读(二):Dispatcher

本文详细解析了Spring MVC中DispatcherServlet的工作原理,包括初始化过程、请求处理流程、HandlerMapping配置及设计原理等内容。

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

在上一章中,主要讲了ContextLoaderListener对IoC容器在Web环境中的构建。本章讲另一个重点,DispatcherServlet

1.DispatcherServlet设计概述

DispatcherServlet作为一个前端控制器,所有的请求都要经过它来处理,进行转发、匹配、数据处理后,由页面进行展示。
在对完成ContextLoaderListener的初始化以后,Web容器开始初始化DispatcherServlet。DispatcherServlet会建立自己的上下文(Context)来持有SpringMVC的Bean对象,在建立这个自己持有的IoC容器的时候,会从ServletContext中得到根上下文(Root Context)来作为DispatcherServlet持有的上下文的双亲上下文。这个根上下文在上一章中有提到,是ContextLoader所设置的。
有了这个根上下文,在对自己持有的上下文进行初始化,并保存到ServletContext中,供以后使用。

1.1 DispatcherServlet类继承

先看类继承图:

这里写图片描述
DispatcherSerlvet通过继承FrameworkServlet和HttpServletBean而进程了HttpServlet,通过使用Serevlet AP来响应HTTP请求。

1.2 DispatcherServlet处理过程

从改图中,我们可以看出,其处理过程分为两个部分,一个是初始化,另一个则是doGet/doPost,处理请求的过程。doGet/doPost方法在进过Framework的processRequest()方法简单处理后,调用DispatcherServlet的doService方法,这个方法中封装了doDispatcher,这个方法是实现MVC模式的主要部分,也是接下来重点关注的部分。
这里写图片描述

2.DispatcherServlet的启动和初始化

作为一个Servlet的,开始时,Servlet的init方法被调用,加载Servlet中的设置。接下来,会执行DispatcherServlet持有的IoC容器的初始化过程,在这个初始化过程中,属于DispatcherServlet的上下文被建立起来,并设置为根上下文的子上下文。IoC容器getBean的时候,是会向双亲上下文getBaen。即根上下文的Bean可以被共享。上下文建立之后,和其它IoC容器一样通过refresh完成初始化。最后,DispatcherServlet给这个自己持有的上下文命名,并把它设置到Web容器的上下文中,这个名称和在web.xml中设置的DispatcherServlet的Servlet名称相关,从而保证了这个上下文在Web环境上下文体系的唯一性。

实现代码逻辑如上所述,这里不贴出来了。

Bean管理
这个时候IoC容器已经建立起来了,这个IoC容器是根上下文的子容器。对于一个Bean,系统首先到根容器查找,找不到之后,才回到DispatcherServlet所管理的IoC容器中去查找,这个是有IoC容器的getBean决定的。

2.1 Dispatcher Server的初始化

DispatcherServlet持有一个以自己Servlet命名的IoC容器。这个IoC容器是一个WebApplicationContext对象,这个IoC容器建立之后,意味着DispatcherServlet拥有自己的Bean定义空间,这位后来的XML文件来配置MVC中各个Bean创造了条件。自此之后,Web容器相关的加载已经完成。
接下来,是Sping MVC Dispatcher Server的初始化。
其调用关系如下:
这里写图片描述

其中初始化在initStrategies方法中完成。
其中,initHandlerMappings的作用是,为HTTP请求找到相应的Conteroller。其代码如下:

private void initHandlerMappings(ApplicationContext context) {
   this.handlerMappings = null;

   if (this.detectAllHandlerMappings) {
      // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
      //找到所有在ApplicationContext中包含双亲上下文中的HandlerMappings。
      Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {
         this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
         // We keep HandlerMappings in sorted order.
       //排序   
         AnnotationAwareOrderComparator.sort(this.handlerMappings);
      }
   }
   else { //根据名称从当前的IoC容器中通过getBean获取handlerMapping
      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.
   //要有至少一个HandlerMapping,如果没有找到,则设置一个默认的。
   if (this.handlerMappings == null) {
      this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
      if (logger.isDebugEnabled()) {
         logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
      }
   }
}

3. MVC处理HTTP分发请求

3.1 HandlerMapping的配置和设计原理

HandlerMapping也就是控制URL和相对应的处理方法的地方。在初始化的时候,在上下文环境中已定义的所有HandlerMapping都已经被加载在一个List中,并被排序(为什么排序)。这个List的每一个元素对应着一个具体的HandlerMapping的配置。一般来说,每一个HandlerMapping可以持有一系列从URL请求道Contorller的映射(对应/*等URL匹配规则)。
HandlerMapping的设计图:

这里写图片描述
其中,HandlerMapping的getHandler方法:

public interface HandlerMapping{
    HandlerExecutionChain getHander(HttpServletRequest request);
}

public class HandlerExecutionChain {

   private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

   private final Object handler; //HTTP请求对应的Handler

   private HandlerInterceptor[] interceptors;

   private List<HandlerInterceptor> interceptorList;

   private int interceptorIndex = -1;

//下面是一些增强方法


   /**
    * Create a new HandlerExecutionChain.
    * @param handler the handler object to execute
    */
   public HandlerExecutionChain(Object handler) {
      this(handler, (HandlerInterceptor[]) null);
   }

   /**
    * Create a new HandlerExecutionChain.
    * @param handler the handler object to execute
    * @param interceptors the array of interceptors to apply
    * (in the given order) before the handler itself executes
    */
   public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {
      if (handler instanceof HandlerExecutionChain) {
         HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
         this.handler = originalChain.getHandler();
         this.interceptorList = new ArrayList<HandlerInterceptor>();
         CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
         CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
      }
      else {
         this.handler = handler;
         this.interceptors = interceptors;
      }
   }


   /**
    * Return the handler object to execute.
    * @return the handler object (may be {@code null})
    */
   public Object getHandler() {
      return this.handler;
   }

   public void addInterceptor(HandlerInterceptor interceptor) {
      initInterceptorList().add(interceptor);
   }

   public void addInterceptors(HandlerInterceptor... interceptors) {
      if (!ObjectUtils.isEmpty(interceptors)) {
         CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
      }
   }

   private List<HandlerInterceptor> initInterceptorList() {
      if (this.interceptorList == null) {
         this.interceptorList = new ArrayList<HandlerInterceptor>();
         if (this.interceptors != null) {
            // An interceptor array specified through the constructor
            CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
         }
      }
      this.interceptors = null;
      return this.interceptorList;
   }

   /**
    * Return the array of interceptors to apply (in the given order).
    * @return the array of HandlerInterceptors instances (may be {@code null})
    */
   public HandlerInterceptor[] getInterceptors() {
      if (this.interceptors == null && this.interceptorList != null) {
         this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
      }
      return this.interceptors;
   }


   /**
    * Apply preHandle methods of registered interceptors.
    * @return {@code true} if the execution chain should proceed with the
    * next interceptor or the handler itself. Else, DispatcherServlet assumes
    * that this interceptor has already dealt with the response itself.
    */
   boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            if (!interceptor.preHandle(request, response, this.handler)) {
               triggerAfterCompletion(request, response, null);
               return false;
            }
            this.interceptorIndex = i;
         }
      }
      return true;
   }

   /**
    * Apply postHandle methods of registered interceptors.
    */
   void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         for (int i = interceptors.length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(request, response, this.handler, mv);
         }
      }
   }

   /**
    * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
    * Will just invoke afterCompletion for all interceptors whose preHandle invocation
    * has successfully completed and returned true.
    */
   void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
         throws Exception {

      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         for (int i = this.interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            try {
               interceptor.afterCompletion(request, response, this.handler, ex);
            }
            catch (Throwable ex2) {
               logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
            }
         }
      }
   }

   /**
    * Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.
    */
   void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         for (int i = interceptors.length - 1; i >= 0; i--) {
            if (interceptors[i] instanceof AsyncHandlerInterceptor) {
               try {
                  AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
                  asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
               }
               catch (Throwable ex) {
                  logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
               }
            }
         }
      }
   }


   /**
    * Delegates to the handler's {@code toString()}.
    */
   @Override
   public String toString() {
      Object handler = getHandler();
      if (handler == null) {
         return "HandlerExecutionChain with no handler";
      }
      StringBuilder sb = new StringBuilder();
      sb.append("HandlerExecutionChain with handler [").append(handler).append("]");
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
         sb.append(" and ").append(interceptors.length).append(" interceptor");
         if (interceptors.length > 1) {
            sb.append("s");
         }
      }
      return sb.toString();
   }

}
}

在HandlerExecutionChain中设置了一个拦截器链,通过这个拦截器链中的拦截器,为handler对象提供功能的增强。

以SimpleURLHandlerMapping ,要做的就是根据URL映射的方式,注册Handle和Interceptor(拦截器),从而维护这样的一个映射的Map。
那么,SimplerURLHandlerMapping的注册Handler的过程就是我们接下来要关注的。

3.2 SimpleURLHandlerMapping注册Handler

···
public void initApplicationContext(){
super.initApplicationContext();
registerHandlers(this.urlMap);
}

//表层注册函数
protected void registerHandlers(Map

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
        Assert.notNull(urlPath, "URL path must not be null");
        Assert.notNull(handler, "Handler object must not be null");
        Object resolvedHandler = handler;

        // 直接用Bean的名称进行映射
        if (!this.lazyInitHandlers && handler instanceof String) {
            String handlerName = (String) handler;
            if (getApplicationContext().isSingleton(handlerName)) {
                resolvedHandler = getApplicationContext().getBean(handlerName);
            }
        }

        Object mappedHandler = this.handlerMap.get(urlPath);
        if (mappedHandler != null) {
            if (mappedHandler != resolvedHandler) {
                throw new IllegalStateException(
                        "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
                        "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
            }
        }
        else {
            //"/"设置到rootHandler中
            if (urlPath.equals("/")) {
                if (logger.isInfoEnabled()) {
                    logger.info("Root mapping to " + getHandlerDescription(handler));
                }
                setRootHandler(resolvedHandler);
            }
            //"/*"设置到defaultHandler当中
            else if (urlPath.equals("/*")) {
                if (logger.isInfoEnabled()) {
                    logger.info("Default mapping to " + getHandlerDescription(handler));
                }
                setDefaultHandler(resolvedHandler);
            }
            else {
                                //放入到一个Map当中
                this.handlerMap.put(urlPath, resolvedHandler);
                if (logger.isInfoEnabled()) {
                    logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
                }
            }
        }
    }

上面这个就是handlerMap了,其中保存了URL和COnteroller之间的映射关系。

3.3 使用HandlerMapping完成请求的映射处理

这个主要是HandleMapping中定义的getHandler方法了,只是通过URL匹配规则来获取持有Handler的HandlerExecutionChain。

3.4 SpingMCV对HTTP的请求的分发处理

这个无疑是我们所关注的重点啦。这里重新回到DispatcherSerlvelt。请求的分发在doService中的doDispatch完成。
以下是这个方法的过程,看似很长,其实只是,获取HandleExecutinChain,请求前置处理,处理,后置处理,视图呈现,结束。
以下是处理过程图:

这里写图片描述

以下是Spring技术内幕的代码注释图,不关注View的部分。

这里写图片描述
接下来,看下DispatcherServlet是如何获得一个Handler的,一个个找.

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            if (logger.isTraceEnabled()) {
                logger.trace(
                        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }

视图呈现,在这里不关注。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值