05-HandlerMethod

HandlerMethod

  • HandlerMethod封裝了目標handler處理器方法和目標方法所屬的Bean對象,提供了更方便的訪問handler參數和返回值的接口。其子類在得到這些屬性之後,對外提供了調用目標處理器的方法。

一、屬性方法

1.1 主要屬性

  • 主要屬性是封裝裏了目標方法、目標Bean對象、參數以及Bean類型等細節信息。
    private final Object bean;

	@Nullable
	private final BeanFactory beanFactory;   //Bean工廠

	private final Class<?> beanType;    //Bean類型

	private final Method method;    //方法對象

	private final Method bridgedMethod;

	private final MethodParameter[] parameters; //方法參數

	@Nullable
	private HttpStatus responseStatus;

	@Nullable
	private String responseStatusReason;

	@Nullable
	private HandlerMethod resolvedFromHandlerMethod;

	@Nullable
	private volatile List<Annotation[][]> interfaceParameterAnnotations;

1.2 主要方法

1.2.1 構造方法

  • 構造方法有很多重載的,下面這個構造方法傳入的是一個BeanName,Bean工廠和方法對象,後續會根據BeanName從工廠獲取真正的Bean對象
	public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
		Assert.hasText(beanName, "Bean name is required");
		Assert.notNull(beanFactory, "BeanFactory is required");
		Assert.notNull(method, "Method is required");
		this.bean = beanName;
		this.beanFactory = beanFactory;
		Class<?> beanType = beanFactory.getType(beanName);
		if (beanType == null) {
			throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
		}
		this.beanType = ClassUtils.getUserClass(beanType);
		this.method = method;
		this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
		this.parameters = initMethodParameters();
		evaluateResponseStatus();
	}

1.2.2 創建對象

  • createWithResolvedBean方法用於創建HandlerMethod對象,從註釋能夠看出,如果提供的只是一個String類型的BeanName而不是Bean實例,在HandlerMethod創建之前會通過createWithResolvedBean方法獲取Bean實例,其實就是從Bean工廠裏面獲取Bean實例,這個Bean實例就行目標方法所屬的Bean,比如我們自己寫的控制器類。
	/**
	 * If the provided instance contains a bean name rather than an object instance,
	 * the bean name is resolved before a {@link HandlerMethod} is created and returned.
	 */
	public HandlerMethod createWithResolvedBean() {
		Object handler = this.bean;
		if (this.bean instanceof String) {
			String beanName = (String) this.bean;
			//根據名稱獲取Bean對象
			handler = this.beanFactory.getBean(beanName);
		}
		return new HandlerMethod(this, handler);
	}
  • 其他很多都是屬性獲取方法、註解相關獲取等方法、另外還有方法參數的輔助類MethodParameter等,細節就不關注了。

二、InvocableHandlerMethod

  • InvocableHandlerMethod是HandlerMethod的子類,我們通過Adapter調用目標執行鏈,最終通過反射的方式調用目標方法,而這個調用的過程就是通過InvocableHandlerMethod來完成的。
  • 下面是從InvocableHandlerMethod的註釋翻譯過來
InvocableHandlerMethod 提供了調用目標處理器的方法來處理對應的請求,不過在調用之前需要通
過 HandlerMethodArgumentResolver 來處理好請求的參數值。

參數處理通常需要WebDataBinder來做參數綁定或者類型轉換,使用setDataBinderFactory方法來提
供一個binder factory來獲取參數解析器。

2.1 主要屬性

  • 下面是主要屬性,屬性相關的setXX方法省略,構造方法省略

    //WebDataBinder工廠,WebDataBinder用於參數綁定和參數類型轉換
    private WebDataBinderFactory dataBinderFactory;

    //參數解析器,用於解析參數
	private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();

    //參數名稱解析器
    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

2.2 主要方法

2.2.1 invokeForRequest

  • invokeForRequest方法是處理請求的入口,
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        
        //1.獲取參數
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		
		//2.調用目標方法
		Object returnValue = doInvoke(args);
		
		//3.返回結果
		return returnValue;
	}

2.2.2 getMethodArgumentValues

  • getMethodArgumentValues用於獲取參數,核心代碼如下,不具體分析了
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

	    //省略...
		//循環提取參數
		for (int i = 0; i < parameters.length; i++) {
			//省略...
			if (this.argumentResolvers.supportsParameter(parameter)) {
				try {
				    //提取參數
					args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
					continue;
				}
				catch (Exception ex) {
                    //拋出異常
				}
			}
			if (args[i] == null) {
    			//拋出異常
			}
		}
		return args;
	}
  • 下面是調試的業務代碼和截圖
@RestController
public class TestController {

    @PostMapping(value = "/hello")
    public String hello(@RequestParam("a") String a, @RequestParam("b") String b) {
        System.out.println("--------1----------" + a + " --- " + b);
        return "hello!";
    }
}
  • 參數: 給a和b參數傳值分別是:aaaaaa和bbbbbb

在這裏插入圖片描述

  • 下面是解析後得到的參數

在這裏插入圖片描述

  • 可以看到,通過getMethodArgumentValues拿到了方法的入參實參,後面就通過doInvoke來調用目標方法了。
  • 這裏的提取參數的細節在argumentResolvers.resolveArgument方法,深入需要分析HandlerMethodArgumentResolver接口和其實現類,具體後續文章再分析。

2.2.3 doInvoke

  • doInvoke內部通過反射調用目標方法,
protected Object doInvoke(Object... args) throws Exception {
		//1.使目標方法可被訪問
		ReflectionUtils.makeAccessible(getBridgedMethod());
		
		try {
		    //2.調用目標方法,Method.invoke(Object,args)
			return getBridgedMethod().invoke(getBean(), args);
		}
		catch (IllegalArgumentException ex) {
            //各種異常處理,省略...
		}
	}
  • 注意這裏getBridgedMethod()會得到一個Method對象,然後Method.invoke(Object,args);
  • 裏面的getBean()調用的就是父類的HandlerMethod裏面封裝的Bean,其實就是我們的目標Bean,args就是方法參數
  • 最後invoke調用就到了Method的invoke方法,代碼如下,其實就是通過反射調用目標對象的方法
    @CallerSensitive
    public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor; // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        //反射調用目標對象的方法
        return ma.invoke(obj, args);
    }
  • 這裏大體把 InvocableHandlerMethod 的核心方法走讀了一遍,內部主要是先提取真正的參數,然後通過反射調用,因爲其內部持有目標Bean對象和目標方法Method對象,得到參數之後反射調用就很容易了。

三、ServletInvocableHandlerMethod

  • ServletInvocableHandlerMethod是InvocableHandlerMethod的一個子類,調試過程中真正的對象是 ServletInvocableHandlerMethod實例,invokeForRequest方法也是從ServletInvocableHandlerMethod往父類調用的
    那麼 它繼承 InvocableHandlerMethod之後又增加了什麼特性了?我們先還是看看源碼中類的註釋:
/**
 * 繼承InvocableHandlerMethod,具備通過HandlerMethodReturnValueHandler來處理返回值的能力,
 * 同時支持通過方法級別的註解@ResponseStatus來設置響應狀態
 *
 * Extends {@link InvocableHandlerMethod} with the ability to handle return
 * values through a registered {@link HandlerMethodReturnValueHandler} and
 * also supports setting the response status based on a method-level
 * {@code @ResponseStatus} annotation.
 *
 * 
 * 一個空返回值可以解釋爲請求處理的結束
 * <p>A {@code null} return value (including void) may be interpreted as the
 * end of request processing in combination with a {@code @ResponseStatus}
 * annotation, a not-modified check condition
 * (see {@link ServletWebRequest#checkNotModified(long)}), or
 * a method argument that provides access to the response stream.
 */

3.1 屬性方法

  • 屬性
//結果處理器,處理目標方法的返回結果
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
  • invokeAndHandle方法是ServletInvocableHandlerMethod在處理請求過程中最重要的方法,請求先被該方法處理,然後才走到父類 InvocableHandlerMethod 的invokeForRequest方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
        
        //1.調用父類的invokeForRequest方法得到返回結果,該方法前面已經分析過
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		
		//2.後面就是關於響應狀態相關的處理、返回結果的處理了
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		try {
			this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			throw ex;
		}
	}

3.2 內部類ConcurrentResultHandlerMethod

  • ConcurrentResultHandlerMethod是ServletInvocableHandlerMethod的一個內部類,也是ServletInvocableHandlerMethod的子類,按照註釋來看是關於異步處理相關的;

四、小結

  • 本文主要介紹HandlerMethod這個類的作用,它內部封裝了目標對象Bean,方法對象Method,方法參數、Bean類型和Bean工廠等參數,便於外部直接調用目標方法。
  • 不過在HandlerMethod中只定義了相關的屬性和一些屬性的讀取方法,處理請求的主體邏輯在子類InvocableHandlerMethod的invokeForRequest和doInvoke方法,內部通過反射來調用目標對象的指定方法。
  • 子類ServletInvocableHandlerMethod通過 InvocableHandlerMethod的invokeForRequest和doInvoke方法處理請求,自身會做關於響應狀態的操作以及通過結果處理器處理結果等細節。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章