SpringMvc之HandlerMapping

一、Handler與HandlerMapping關係

Handler可以理解爲具體幹活的,也就是我們的業務處理邏輯。

Handler最終是要通過url 來訪問到,這樣url 與Handler之間就有一個映射關係了。

HandlerMapping的作用就是維護這種映射,對Handler登記在冊,對外提供根據url 查詢Handler的服務。

二、Handler分類

SpringMVC爲我們提供了多種定義Handler的方式。

1.實現Controller接口

org.springframework.web.servlet.mvc.Controller 接口是SpringMvc提供的控制器接口,實現此接口的類,可以看做是一個Handler。

此接口只有一個方法

public interface Controller {
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

實現了此接口的Handler特點就是: 只有一個handleRequest方法接受請求,也就是說大部分情況下,一個類是一個handler,一個類只能映射一個URL。

針對一些場景,SpringMVC提供了幾個Controller的默認實現:

與Servlet相關

  • ServletForwardingController
    與Servlet有關的控制器,作用:將到達ServletForwardingController的請求轉發到當前應用中的一個Servlet。

  • ServletWrappingController
    與Servlet有關的控制器,作用:將當前應用中的某個 Servlet直接包裝爲一個Controller,所有到達ServletWrappingController的請求最終交給其包裝的那個servlet進行處理

直接跳轉頁面

  • ParameterizableViewController
    用於直接界面跳轉,省去自己實現Controller。控制器根據配置的參數來跳轉界面。

  • UrlFilenameViewController
    用於直接界面跳轉,省去自己實現Controller。控制器根據請求的URL直接解析出視圖名。

MultiActionController

一個 Controller 可以寫多個方法,分別對應不同的請求,使同一業務的方法可以放在一起了。在使用時讓自己的 Controller 類繼承 MultiActionController 類。(注意,雖然處理了多個請求但還是隻有一個handleRequest接受的請求。此類類似分發功能)

2.實現HttpRequestHandler接口

此接口與Controller類似,也是隻有一個handlerRequest方法.

public interface HttpRequestHandler {
    void handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException;
}

但是handleRequest 並沒有返回一個視圖。從這點來看

HttpRequestHandler是專門處理Http請求(非視圖請求),並生成對應的響應的處理器。

針對一些場景,SpringMVC提供了幾個HttpRequestHandler的默認實現:

靜態資源相關

靜態資源的請求也web開發中不可或缺的請求.HttpRequestHandler的兩個實現類,針對靜態資源進行處理。

  • DefaultServletHttpRequestHandler
    與靜態資源有關的控制器。 。通常web容器都有處理靜態資源請求的能力。我們以Tomcat爲例,Tomcat中所有的資源都是Servlet實現的,靜態資源的請求交類DefaultServlet去處理。

DefaultServletHttpRequestHandler的作用其實就是根據當前容器,將靜態資源請求轉給容器自己去處理靜態資源。例如:Tomcat容器下,DefaultServletHttpRequestHandler將靜態資源請求交給DefaultServlet去處理

推薦閱讀Tomcat 對靜態資源的處理

  • ResourceHttpRequestHandler
    與靜態資源有關的控制器。DefaultServletHttpRequestHandler把靜態資源請求交給容器處理。ResourceHttpRequestHandler是把靜態資源請求交給SpringMVC自己處理。

我們通常用註解的形式配置靜態資源的處理

@Configuration
public class WebMvcStaticResourcesConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        if (!registry.hasMappingForPattern("/static/**")) {
            registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
        }
        super.addResourceHandlers(registry);
    }
}

此時我們心裏應該清楚:這是交給SpringMVC自己處理靜態資源請求

遠程訪問相關。

針對遠程訪問的場景,Spring不但自己做了實現,而且還提供了對其他技術的集成支持。

  • Spring HTTP Invoker: Spring自己實現,使用HTTP協議,允許穿透防火牆,使用JAVA系列化方式,但僅限於Spring應用之間使用,即調用者與被調用者都必須是使用Spring框架的應用。

  • RMI:使用JRMP協議(基於TCP/IP),不允許穿透防火牆,使用JAVA系列化方式,使用於任何JAVA應用之間相互調用。

  • Hessian:使用HTTP協議,允許穿透防火牆,使用自己的系列化方式,支持JAVA、C++、.Net等跨語言使用。

  • Burlap: 與Hessian相同,只是Hessian使用二進制傳輸,而Burlap使用XML格式傳輸(兩個產品均屬於caucho公司的開源產品)。

SpringMVC提供的幾個HttpRequestHandler實現,正是處理這類請求。

  • HttpInvokerServiceExporter
    支持 Spring HTTP Invoker 調用器
  • HessianServiceExporter
    hessian服務處理程序
  • BurlapServiceExporter
    Burlap服務處理程序

3.實現Servlet方式

Servlet是我們使用最早的定義業務邏輯的方式。SpringMVC也提供了對這種實現方式的支持

@Controller("/servletController")
public class ServletController extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("this servlet controller");
    }
}

4.@RequestMapping方式

這種方式是我們現在最熟悉的方式。一個方法就是一個handler, 一類中可以寫多個handler。

@RestController
@RequestMapping(value = "/test")
public class TestController {
        @RequestMapping(value = "/test")
        public void getCode()throws  Exception{
            System.out.println("this RequestMapping");
        }
}

對於springMVC提供的定義Handler的這些方式,我們可以靈活的定義我們的業務處理。

三、HandlerMapping

定義了完了Handler,我們最終的目的還是需要通過url訪問到他們進行業務的處理。此時HandlerMapping上場。HandlerMapping 會把他們,按照url 與Handler的映射關係登記在冊。我們就可以通過url找到對應的Handler了。

HandlerMapping 接口,只定義了一個方法getHandler。此方法是各種HandlerMapping實現類對外提供獲取Handler的核心方法

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

上文可以看出:一個handler可能是一個方法,也可能是一個 Controller 對象、 HttpRequestHandler 對象或 Servlet 對象

針對這種情況, HandlerMapping分爲兩個分支來處理

  • AbstractHandlerMethodMapping:一種是處理url直接與類級別handler的對應。
  • AbstractUrlHandlerMapping一種是處理url與Method級別handler的對應。

它們又統一繼承於 AbstractHandlerMapping

AbstractHandlerMapping

AbstractHandlerMapping 是HandlerMapping的抽象實現,使用模板模式定義了獲取Handler的算法骨架

 public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
   Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }

        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
 }

可以看到在這個方法中又調用了 getHandlerInternal() 方法獲取到了 Handler 對象,而 Handler 對象具體內容是由它的子類去定義的。

算法骨架

  • 獲取到一個handler
  • 將handler與攔截器包裝成一個HandlerExecutionChain 實例返回

AbstractUrlHandlerMapping

AbstractUrlHandlerMapping 這個分支獲取的 Handler 的類型實際就是一個 Controller 類,處理url直接與類handler的映射。(也就是一個類中只有一個接受請求的方法)

常見handlerMapping實現:

  • BeanNameUrlHandlerMapping
    利用 BeanName 來作爲 URL 使用。並繼承AbstractDetectingUrlHandlerMapping類,說明其具有檢查url的能力。我們使用@Controller註解標識(Controller接口方式,Servlet方式,HttpRequestHandler接口方式)handler的beaname,通過beanname訪問到handler

  • SimpleUrlHandlerMapping
    可以將 URL 與處理器的定義分離,還可以對 URL 進行統一的映射管理。怎麼理解呢? 說白了就是我們把我們定義的url與handler的關係配置到此handlerMapping.urlMap屬性上,統一管理。

AbstractHandlerMethodMapping

AbstractHandlerMethodMapping 這個分支獲取的 Handler 的類型是 HandlerMethod,即這個 Handler 是一個方法,它保存了方法的信息(如Method),這樣一個 Controller 就可以處理多個請求了

AbstractHandlerMethodMapping只有一個實現類 RequestMappingHandlerMapping

handlerMapping實現:

  • RequestMappingHandlerMapping:處理@RequestMapping 這種方式的handler 。

四、總結

在我們使用springmvc時:

  • 我們在寫接口時,其實就是在定義handler,並配置一個Url與之映射。
  • springmvc 爲我們提供了4種定義handler的形式
  • 我們定義handler 被HandlerMapping登記在冊,這樣我們就可以使用url從HandlerMapping找到對應handler。

這就是Handler與HandlerMapping的關係了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章