SpringMVC源碼從入門到放棄-DispatcherServlet

本系列文章主要對SpringMVC源碼做詳細介紹,面向對Spring有所瞭解的Java程序員程序員

本文擬解惑幾個問題
1.http請求如何映射到Controller的
2.Controller是如何被執行的web

DispatcherServlet

在有SpringMVC以前,咱們要對外提供http接口,那麼咱們須要編寫HttpServlet,並在web.xml中配置http請求到HttpServlet的映射關係
在有SpringMVC以後,開發人員開始寫Controller並用註解@RequestMapping("\test")定義http請求到Controller.method()的映射關係spring

從HttpServlet到Controller實際上是由於SpringMVC對HttpServlet進行了一層封裝,對外提供一個接收全部請求的HttpServlet(DispatcherServlet),Spring把Controller加了@RequestMapping的方法定義爲HandlerMethod,由DispatcherServlet統一分發http請求到對應的HandlerMethod去處理mvc

因此SpringMVC項目咱們都要在web.xml中配上全部請求到DispatcherServlet的映射app

<servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

DispatcherServlet的本質是一個HttpServlet

核心的方法有框架

@Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
            doDispatch(request, response);
        }

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

父類FrameworkServlet重寫了HttpServlet的doGet()、doPost()等方法,最後都會調用DispatcherServlet的doService()方法,而後核心的執行流程在doDispatch()方法中ide

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                //由HandlerMapping找處理該請求的HandlerMethod和攔截該請求的HandlerInterceptor,包裝成HandlerExecutionChain
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 找到該HandlerMethod的HandlerAdapter(具體的方法調用流程)
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());      
                                
                                //執行HandlerInterceptor
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler. 實際調用HandlerMethod
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        

                //執行HandlerInterceptor
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            
                        //返回渲染視圖
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        
        }

              protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
        }

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        for (HandlerAdapter ha : this.handlerAdapters) {
            if (ha.supports(handler)) {
                return ha;
            }
        }
    }

這裏涉及到兩個核心的類
1.HandlerMappingthis

Interface to be implemented by objects that define a mapping between
requests and handler objects.
2.HandlerAdapterurl

  • MVC framework SPI, allowing parameterization of the core MVC workflow.
  • Interface that must be implemented for each handler type to handle a request.

  • This interface is used to allow the {@link DispatcherServlet} to be indefinitely
  • extensible. The {@code DispatcherServlet} accesses all installed handlers through
  • this interface, meaning that it does not contain code specific to any handler type.

HandlerMapping

如官方文檔裏提到的,HandlerMapping定義了request和handler的映射關係,通俗點來講就是url對應的是哪一個HandlerMethod

public interface HandlerMapping {
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

核心方法返回HandlerExecutionChain,包裝了當前請求須要執行的Handler和interceptors

public class HandlerExecutionChain {

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

    private final Object handler;

    private HandlerInterceptor[] interceptors;

在DispatcherServlet.getHandler中
遍歷容器中全部的HandlerMapping,爲當前請求找到最匹配的HandlerMethod,包裝成HandlerExecutionChain,SpringMVC默認有不少HandlerMapping實現策略,每一個HandlerMapping中註冊着不一樣的HandlerMethod映射關係

咱們項目中默認對於Controller+@RequestMapping形式的HandlerMapping實現類爲RequestMappingHandlerMapping
他的父類的內部中維護了請求和HandlerMethod的映射關係

class MappingRegistry {
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();

        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();

下一期咱們將詳細闡述HandlerMapping


HandlerAdapter

DispatcherServlet.getHandlerAdapter(Object handler)方法爲當前handler找到合適的HandlerAdapter
從文檔定義能夠看出,HandlerAdapter定義了一個handler執行的流程,大體包括如下流程
1.請求參數解析成調用handler的參數
2.經過反射調用handler
3.處理handler的返回值

Spring上下文中有3個默認的HandlerAdapter實現,@RequestMapping定義的handler默認由RequestMappingHandlerAdapter處理

handler被交由RequestMappingHandlerAdapter的handle(HttpServletRequest request, HttpServletResponse response, Object handler)方法執行
通過層層調用,在invokeHandlerMethod()中定義了執行流程

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
                //包裝請求,提供額外方法
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
                        
//包裝執行器
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);//參數解析
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);//返回值處理
            invocableMethod.setDataBinderFactory(binderFactory);//參數解析時特殊類型處理
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);//

            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            
//執行調用
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
        
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }

咱們先來看看invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
                //執行調用
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);

    
        try {//處理返回值
            this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }

    }

    public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
                //獲取參數
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        //
        Object returnValue = doInvoke(args);
        
        return returnValue;
    }

最後實際調用Controller的方法的就是doInvoke中,傳入參數,利用反射進行調用

protected Object doInvoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            return getBridgedMethod().invoke(getBean(), args);
        }

以上就是一次htpp請求的調用過程,下一篇文章咱們將詳細闡述handleMethod的註冊過程

繼承結構

文章的末尾咱們來看看DispatcherServlet的繼承結構

DispatcherServlet

用於HTTP請求handler/controllers的中央調度程序,例如用於Web UI controllers或基於HTTP的遠程服務導出程序。調度註冊的handlers以處理Web請求,提供方便的url映射和異常處理功能。
這個servlet很是靈活:它能夠在任何工做流程中使用,只要實現了合適的adapter類。它提供瞭如下功能,將其與其餘請求驅動的Web MVC框架區分開來:
1.基於JavaBeans配置機制。
2.它可使用任何HandlerMapping實現 - 預先構建或做爲應用程序的一部分提供 - 控制分發 requests 到 handler objects。默認是 BeanNameUrlHandlerMapping和 RequestMappingHandlerMapping。HandlerMapping對象能夠定義爲servlet應用程序上下文中的bean,實現HandlerMapping接口,覆蓋默認的HandlerMapping(若是存在)。
3.它可使用任何HandlerAdapter; 這容許使用任何處理程序接口。默認適配器HttpRequestHandlerAdapter, 默認 RequestMappingHandlerAdapter 也會被註冊。HandlerAdapter對象能夠在應用程序上下文中做爲bean添加,並覆蓋默認的HandlerAdapter。
4。調度程序的exception解決策略能夠經過一個HandlerExceptionResolver例如映射到錯誤頁面的特定例外來指定 。默認的是 ExceptionHandlerExceptionResolver, ResponseStatusExceptionResolver和 DefaultHandlerExceptionResolver。這些HandlerExceptionResolvers能夠經過應用程序上下文重寫。
5.其視圖解析策略能夠經過ViewResolver 實現來指定,將符號視圖名稱解析爲視圖對象。默認是 InternalResourceViewResolver。能夠在應用程序上下文中將ViewResolver對象添加爲bean,覆蓋默認的ViewResolver。

注意:@RequestMapping只有在調度程序中存在相應HandlerMapping和HandlerAdapter時纔會處理。 這是默認狀況。可是,若是您正在定義自定義HandlerMappings 或者HandlerAdapters,那麼您須要確保相應的自定義 RequestMappingHandlerMapping和/或RequestMappingHandlerAdapter 定義 - 只要您打算使用@RequestMapping。

Web應用程序能夠定義任意數量的DispatcherServlet。 每一個servlet將在其本身的名稱空間中運行,使用映射,處理程序等加載其本身的應用程序上下文。只有ContextLoaderListener共享根據應用程序上下文。

核心方法

/**
     * 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 {

FrameworkServlet

相關文章
相關標籤/搜索