SpringMVC之源碼分析--Controller(一)

概述

Controller是Spring MVC爲咱們提供的基礎的控制器接口,和HttpServlet同樣,接收request和response參數處理用戶請求,並返回ModelAndView,從概念上能夠類比Struts的Action。git

Controller主要實現的以下功能:github

  • 接收並處理用戶請求
  • 調用業務方法
  • 返回ModelAndView

基於Controller開發請求處理Handler的特色:web

  • 須要實現Controller接口
  • 請求參數需從請求request中獲取
  • 請求處理Handler與Spring深度耦合

就目前項目開發,幾乎不會使用此方式進行開發,除非維護一些歷史項目。目前是基於註解進行開發,從Spring2.5及以後開始支持。編寫本章內容主要是基於知識點的全面性來考慮,你們能夠了解了解。spring

本系列文章是基於Spring5.0.5RELEASE。瀏覽器

接口Controller

Spring提供的Controller接口,定義了一個方法,做用就是處理用戶請求,源碼以下:緩存

package org.springframework.web.servlet.mvc;

public interface Controller {
    
    /**
     *接收request和response參數,處理用戶請求
     *參數從request中獲取
     *方法返回ModelAndView,Model爲模型數據,View爲視圖
     */
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;

}

Spring提供的實現類

Spring提供了以下實現:session

AbstractController 控制瀏覽器緩存、支持的請求方法等等mvc

ServletForwardingController 將Spring Handler接收的請求轉發給一個Servlet去執行app

ParameterizableViewController 參數化視圖控制器,根據參數的邏輯視圖名直接選擇須要展現的視圖ide

AbstractUrlViewController 根據請求URL路徑直接轉化爲邏輯視圖名的支持基類

UrlFilenameViewController 將請求URL路徑轉換爲邏輯視圖名並返回的實現類

ServletWrappingController Servlet包裝控制器

以上是Spring MVC爲咱們提供Controller接口的默認實現,下面咱們接着分析這些實現類。

AbstractController

該抽象類繼承WebContentGenerator並實現Controller接口,其中WebContentGenerator類用於瀏覽器緩存控制、自定義Controller支持的請求方法類型(默認支持:GET/HEAD/POST)等。

WebContentGenerator類的主要屬性以下:

/** 支持的請求方法類型,默認支持:GET、HEAD、POST */
@Nullable
private Set<String> supportedMethods;

@Nullable
private String allowHeader;
/** 當前請求是否必須有session */
private boolean requireSession = false;

@Nullable
private CacheControl cacheControl;
/** 緩存過時時間,正數表示須要緩存,負數表示不作任何事情 */
private int cacheSeconds = -1; 

@Nullable
private String[] varyByRequestHeaders;

/** 是否使用HTTP1.0協議過時響應頭:若是true則會在響應頭添加「Expires:」;須要配合cacheSeconds使用 */
private boolean useExpiresHeader = false;

/** 是否使用HTTP1.1協議的緩存控制響應頭,若是true則會在響應頭添加;須要配合cacheSeconds使用 */
private boolean useCacheControlHeader = true;

/** 是否使用HTTP 1.1協議的緩存控制響應頭,若是true則會在響應頭添加;須要配合cacheSeconds使用 */
private boolean useCacheControlNoStore = true;

AbstractController類源碼以下:

/** 表示該控制器是否在執行時同步session,從而保證該會話的用戶串行訪問該控制器 */
private boolean synchronizeOnSession = false;

/**
 *實現Controller接口的handleRequest方法
 */
@Override
@Nullable
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
        throws Exception {

    if (HttpMethod.OPTIONS.matches(request.getMethod())) {
        response.setHeader("Allow", getAllowHeader());
        return null;
    }

    // 檢查是否支持請求方法以及必須的session
    checkRequest(request);
    // 根據設置準備response
    prepareResponse(response);

    // 若是必要順序執行handleRequestInternal方法
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                return handleRequestInternal(request, response);
            }
        }
    }
    
    return handleRequestInternal(request, response);
}

/**
 * 須要子類實現的模板方法
 */
@Nullable
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
        throws Exception;

實戰

本章實戰是經過繼承AbstractController類編寫用戶請求handler,測試以下幾點:

  • 繼承AbstractController抽象類,實現handlerRequestInternal方法處理用戶請求

經過前幾篇及上面的分析,SimpleControllerHandlerAdapter類能夠適配實現了Controller接口的類,AbstractController實現了Controller接口的handleRequest方法,並預留了handleRequestInternal模板方法供子類實現,用戶處理用戶請求,根據這個思路,咱們要使用SimpleControllerHandlerAdapter適配Controller時,只需繼承AbstractController類並實現handleRequestInternal方法便可,具體源碼以下:

自定義Controller源碼:

/**
 *繼承AbstractController並實現handleRequestInternal方法
 */
public class HelloWorldController extends AbstractController {

    /**
     *經過response直接回寫數據,也可經過ModelAndView指定邏輯視圖並回寫數據
     */
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Writer writer = response.getWriter();
        writer.write("hello AbstractController");
        writer.flush();
        return null;
    }

}

配置文件配置以下:

<!-- 使用BeanNameUrlHandlerMapping映射器
     能夠不用配置,Spring MVC默認支持:BeanNameUrlHanderMapping、RequestMappingHandlerMapping
 -->
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

<!-- 指定Spring使用SimpleControllerHandlerAdapter適配器
     能夠不用配置,Spring MVC默認支持:HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter
 -->
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

<!-- 配置Controller bean -->
<bean name="/helloWorldController" class="com.github.dalianghe.controller.HelloWorldController"/>

驗證結果

代碼編寫完後,啓動應用進行測試,AbstractController默認支持GET、HEAD、POST方法類型,咱們使用Postman進行測試,GET請求結果以下:

HEAD、POST也能處理,但若是發起PUT請求則返回不支持方法類型,以下圖:

  • 指定請求方法類型

AbstractController默認支持GET、HEAD、POST三種請求類型,本例經過設置supportedMethods屬性來設置支持的請求方法類型,代碼以下:

配置文件配置以下:

<!-- 配置Controller bean -->
<bean name="/helloWorldController" class="com.github.dalianghe.controller.HelloWorldController">
    <!-- 設置支持的請求方法類型,以下支持PUT、POST -->
    <property name="supportedMethods">
        <set>
            <value>PUT</value>
            <value>POST</value>
        </set>
    </property>
</bean>

驗證結果

只需在Controller Bean修改supportMethods屬性接口,測試PUT請求,結果以下:

  • requireSession前置檢查

當把requireSession屬性設置爲true時,訪問Controller需檢查有無session,若是沒有將跑出HttpSessionRequiredException異常,代碼以下:

配置文件配置以下:

<!-- 配置Controller bean -->
<bean name="/helloWorldController" class="com.github.dalianghe.controller.HelloWorldController">
    <!-- 設置支持的請求方法類型,以下支持PUT、POST -->
    <property name="supportedMethods">
        <set>
            <value>GET</value>
            <value>POST</value>
        </set>
    </property>
    <!-- 訪問此處理器前檢查session -->
    <property name="requireSession" value="true"/>
</bean>
<!-- 增長一個處理器,用於獲取session -->
<bean id="/helloWorld2Controller" class="com.github.dalianghe.controller.HelloWorld2Controller"/>

新增的HelloWorld2Controller與HelloWorldController代碼基礎上增長獲取session的代碼,以下:

request.getSession(true);

測試驗證

首先驗證沒有session狀況下,結果以下:

說明在訪問到自定義Controller前檢查session沒有,拋出HttpSessionRequiredException異常

咱們增長了一個新的controller用於設置建立session,此時咱們訪問一次後,再訪問須要驗證的session的請求,結果以下:

經過結果可見,測試能正常訪問了。

總結

本章主要分析了Controller接口、AbstractController抽象類以及對自定義Controller的幾個重要屬性進行了測試,但願對你們有幫助,謝謝。

最後建立了qq羣方便你們交流,可掃描加入,同時也可加我qq:276420284,共同窗習、共同進步,謝謝!

相關文章
相關標籤/搜索