Spring MVC源碼解析之ServletInvocableHandlerMethod

HandlerMethod

定製化實現Spring MVC的關鍵類。HandlerMethod封裝了很多屬性,在訪問請求方法的時候可以方便的訪問到方法、方法參數、方法上的註解、所屬類等並且對方法參數封裝處理,也可以方便的訪問到方法參數的註解等信息。

public class HandlerMethod {

	// Object類型,既可以是個Bean,也可以是個BeanName
	private final Object bean;
	// 如果是BeanName,拿就靠它拿出Bean實例了~
	@Nullable
	private final BeanFactory beanFactory;
	private final Class<?> beanType; // 該方法所屬的類
	private final Method method; // 該方法本身
	private final Method bridgedMethod; // 被橋接的方法,如果method是原生的,它的值同method
	// 封裝方法參數的類實例,**一個MethodParameter就是一個入參**
	// MethodParameter也是Spring抽象出來的一個非常重要的概念
	private final MethodParameter[] parameters;
	@Nullable
	private HttpStatus responseStatus; // http狀態碼(畢竟它要負責處理和返回)
	@Nullable
	private String responseStatusReason; // 如果狀態碼裏還要複數原因,就是這個字段  可以爲null


	// 通過createWithResolvedBean()解析此handlerMethod實例的handlerMethod。
	@Nullable
	private HandlerMethod resolvedFromHandlerMethod;
	// 標註在**接口入參**上的註解們(此處數據結構複雜,List+二維數組)
	@Nullable
	private volatile List<Annotation[][]> interfaceParameterAnnotations;

	// 它的構造方法衆多  此處我只寫出關鍵的步驟
	public HandlerMethod(Object bean, Method method) {
		...
		this.beanType = ClassUtils.getUserClass(bean);
		this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
		this.parameters = initMethodParameters();
		...
		evaluateResponseStatus();
	}
	// 這個構造方法拋出了一個異常NoSuchMethodException 
	public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
		...
		this.method = bean.getClass().getMethod(methodName, parameterTypes);
		this.parameters = initMethodParameters();
		...
		evaluateResponseStatus();
	}
	// 此處傳的是BeanName
	public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
		...
		// 這部判斷:這個BeanName是必須存在的
		Class<?> beanType = beanFactory.getType(beanName);
		if (beanType == null) {
			throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
		}
		this.parameters = initMethodParameters();
		...
		evaluateResponseStatus();
	}

	// 供給子類copy使用的
	protected HandlerMethod(HandlerMethod handlerMethod) { ... }
	
	// 所有構造都執行了兩個方法:initMethodParameters和evaluateResponseStatus

	// 初始化該方法所有的入參,此處使用的是內部類HandlerMethodParameter
	// 注意:處理了泛型的~~~
	private MethodParameter[] initMethodParameters() {
		int count = this.bridgedMethod.getParameterCount();
		MethodParameter[] result = new MethodParameter[count];
		for (int i = 0; i < count; i++) {
			HandlerMethodParameter parameter = new HandlerMethodParameter(i);
			GenericTypeResolver.resolveParameterType(parameter, this.beanType);
			result[i] = parameter;
		}
		return result;
	}

	// 看看方法上是否有標註了@ResponseStatus註解(接口上或者父類 組合註解上都行)
	// 若方法上沒有,還會去所在的類上去看看有沒有標註此註解
	// 主要只解析這個註解,把它的兩個屬性code和reason拿過來,最後就是返回它倆了~~~
	// code狀態碼默認是HttpStatus.INTERNAL_SERVER_ERROR-->(500, "Internal Server Error")
	private void evaluateResponseStatus() {
		ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
		if (annotation == null) {
			annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
		}
		if (annotation != null) {
			this.responseStatus = annotation.code();
			this.responseStatusReason = annotation.reason();
		}
	}
	... // 省略所有屬性的get方法(無set方法)

	// 返回方法返回值的類型  此處也使用的MethodParameter 
	public MethodParameter getReturnType() {
		return new HandlerMethodParameter(-1);
	}
	// 注意和上面的區別。舉個列子:比如方法返回的是Object,但實際return “fsx”字符串
	// 那麼上面返回永遠是Object.class,下面你實際的值是什麼類型就是什麼類型
	public MethodParameter getReturnValueType(@Nullable Object returnValue) {
		return new ReturnValueMethodParameter(returnValue);
	}

	// 該方法的返回值是否是void
	public boolean isVoid() {
		return Void.TYPE.equals(getReturnType().getParameterType());
	}
	// 返回標註在方法上的指定類型的註解   父方法也成
	// 子類ServletInvocableHandlerMethod對下面兩個方法都有複寫~~~
	@Nullable
	public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) {
		return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType);
	}
	public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) {
		return AnnotatedElementUtils.hasAnnotation(this.method, annotationType);
	}


	// resolvedFromHandlerMethod雖然它只能被構造進來,但是它實際是銅鼓調用下面方法賦值
	@Nullable
	public HandlerMethod getResolvedFromHandlerMethod() {
		return this.resolvedFromHandlerMethod;
	}
	// 根據string類型的BeanName把Bean拿出來,再new一個HandlerMethod出來~~~這才靠譜嘛
	public HandlerMethod createWithResolvedBean() {
		Object handler = this.bean;
		if (this.bean instanceof String) {
			Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
			String beanName = (String) this.bean;
			handler = this.beanFactory.getBean(beanName);
		}
		return new HandlerMethod(this, handler);
	}

	public String getShortLogMessage() {
		return getBeanType().getName() + "#" + this.method.getName() + "[" + this.method.getParameterCount() + " args]";
	}


	// 這個方法是提供給內部類HandlerMethodParameter來使用的~~ 它使用的數據結構還是蠻複雜的
	private List<Annotation[][]> getInterfaceParameterAnnotations() {
		List<Annotation[][]> parameterAnnotations = this.interfaceParameterAnnotations;
		if (parameterAnnotations == null) {
			parameterAnnotations = new ArrayList<>();

			// 遍歷該方法所在的類所有的實現的接口們(可以實現N個接口嘛)
			for (Class<?> ifc : this.method.getDeclaringClass().getInterfaces()) {
			
				// getMethods:拿到所有的public的方法,包括父接口的  接口裏的私有方法可不會獲取來
				for (Method candidate : ifc.getMethods()) {
					// 判斷這個接口方法是否正好是當前method複寫的這個~~~
					// 剛好是複寫的方法,那就添加進來,標記爲接口上的註解們~~~
					if (isOverrideFor(candidate)) {
						// getParameterAnnotations返回的是個二維數組~~~~
						// 因爲參數有多個,且每個參數前可以有多個註解
						parameterAnnotations.add(candidate.getParameterAnnotations());
					}
				}
			}
			this.interfaceParameterAnnotations = parameterAnnotations;
		}
		return parameterAnnotations;
	}

	
	// 看看內部類的關鍵步驟
	protected class HandlerMethodParameter extends SynthesizingMethodParameter {
		@Nullable
		private volatile Annotation[] combinedAnnotations;
		...

		// 父類只會在本方法拿,這裏支持到了接口級別~~~
		@Override
		public Annotation[] getParameterAnnotations() {
			Annotation[] anns = this.combinedAnnotations;
			if (anns == null) { // 都只需要解析一次
				anns = super.getParameterAnnotations();
				int index = getParameterIndex();
				if (index >= 0) { // 有入參才需要去分析嘛
					for (Annotation[][] ifcAnns : getInterfaceParameterAnnotations()) {
						if (index < ifcAnns.length) {
							Annotation[] paramAnns = ifcAnns[index];
							if (paramAnns.length > 0) {
								List<Annotation> merged = new ArrayList<>(anns.length + paramAnns.length);
								merged.addAll(Arrays.asList(anns));
								for (Annotation paramAnn : paramAnns) {
									boolean existingType = false;
									for (Annotation ann : anns) {
										if (ann.annotationType() == paramAnn.annotationType()) {
											existingType = true;
											break;
										}
									}
									if (!existingType) {
										merged.add(adaptAnnotation(paramAnn));
									}
								}
								anns = merged.toArray(new Annotation[0]);
							}
						}
					}
				}
				this.combinedAnnotations = anns;
			}
			return anns;
		}
	}

	// 返回值的真正類型~~~
	private class ReturnValueMethodParameter extends HandlerMethodParameter {
		@Nullable
		private final Object returnValue;
		public ReturnValueMethodParameter(@Nullable Object returnValue) {
			super(-1); // 此處傳的-1哦~~~~ 比0小是很有意義的
			this.returnValue = returnValue;
		}
		...
		// 返回值類型使用returnValue就行了~~~
		@Override
		public Class<?> getParameterType() {
			return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
		}
	}
}

雖然它持有了目標的Method,但是它並沒有提供invoke執行它的能力,如果你要執行它還得自己把Method拿去自己執行。

單一職責:準備數據,封裝數據,而不提供具體使用的方法

繼承樹

子類都有invoke能力

InvocableHandlerMethod

增加了調用能力:在調用的時候,把方法入參都封裝進,主要還是依靠 HandlerMethodArgumentResolver,只是把解析好的放到對應位置裏去

public class InvocableHandlerMethod extends HandlerMethod {
	private static final Object[] EMPTY_ARGS = new Object[0];

	// 新增屬性:數據綁定、數據校驗

	// 用於產生數據綁定器、校驗器
	@Nullable
	private WebDataBinderFactory dataBinderFactory;
	// HandlerMethodArgumentResolver用於入參的解析
	private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
	// 獲取到形參名,所以註解裏我們不寫value,通過形參名字來匹配也可以
	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

在給定請求的上下文中解析方法的參數值後調用該方法。 也就是說:方法入參裏就能夠自動使用請求域(包括path裏的,requestParam裏的、以及常規對象如HttpSession這種)

  • providedArgs
    調用者可以傳進來,然後直接doInvoke()的時候原封不動的使用它,彌補了請求域沒有所有對象的不足,畢竟有些對象是用戶自定義的。
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	}
	// 普通的方法調用
	Object returnValue = doInvoke(args);
	return returnValue;
}

小結

該類主要提供了invoke調用目標Bean的目標方法的能力,核心的邏輯可是各種各樣的HandlerMethodArgumentResolver完成

InvocableHandlerMethod這個子類雖然它提供了調用了能力,但是它卻依舊還沒有和Servlet的API綁定起來,畢竟使用的是Spring自己通用的的NativeWebRequest,它還有一個子類

ServletInvocableHandlerMethod

繼承InvocableHandlerMethod,增加了返回值和響應狀態碼的處理
內部類ConcurrentResultHandlerMethod繼承於它,支持異常調用結果處理,Servlet容器下Controller在查找適配器時發起調用的最終就是ServletInvocableHandlerMethod。

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
	private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call");

	// 處理方法返回值
	@Nullable
	private HandlerMethodReturnValueHandlerComposite returnValueHandlers;

	// 構造函數略
	
	// 設置處理返回值的HandlerMethodReturnValueHandler
	public void setHandlerMethodReturnValueHandlers(HandlerMethodReturnValueHandlerComposite returnValueHandlers) {
		this.returnValueHandlers = returnValueHandlers;
	}

invokeAndHandle

對invokeForRequest方法的進一步增強 因爲調用目標方法還是靠invokeForRequest,本處是把方法的返回值拿來進一步處理,比如狀態碼
調用該方法,並通過所配置的HandlerMethodReturnValueHandler處理返回值

	public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		// 設置返回狀態碼 這裏面還是有點意思的  因爲@ResponseStatus#code()在父類已經解析,但子類才用
		setResponseStatus(webRequest);

		if (returnValue == null) {
			// Request的NotModified爲true 有@ResponseStatus註解標註 RequestHandled=true 三個條件有一個成立,則設置請求處理完成並返回
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				// 該請求已被處理
				mavContainer.setRequestHandled(true);
				return;
			}
		// 返回值不爲null,@ResponseStatus存在reason 同樣設置請求處理完成並返回
		} else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		// 前邊都不成立,則設置RequestHandled=false即請求未完成
		// 繼續交給HandlerMethodReturnValueHandlerComposite處理
		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
			this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		...
	}
	// 設置返回的狀態碼到HttpServletResponse 裏面去
	private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
		HttpStatus status = getResponseStatus();
		if (status == null) { // 如果調用者沒有標註ResponseStatus.code()此註解  此處就忽略它
			return;
		}

		HttpServletResponse response = webRequest.getResponse();
		if (response != null) {
			String reason = getResponseStatusReason();

			// 此處務必注意:若有reason,那就是sendError  哪怕你是200哦~
			if (StringUtils.hasText(reason)) {
				response.sendError(status.value(), reason);
			} else {
				response.setStatus(status.value());
			}
		}

		// 設置到request的屬性,把響應碼給過去。爲了在redirect中使用
		// To be picked up by RedirectView
		webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
	}

	private boolean isRequestNotModified(ServletWebRequest webRequest) {
		return webRequest.isNotModified();
	}


	// 這個方法RequestMappingHandlerAdapter裏有調用
	ServletInvocableHandlerMethod wrapConcurrentResult(Object result) {
		return new ConcurrentResultHandlerMethod(result, new ConcurrentResultMethodParameter(result));
	}

	// 內部類們
	private class ConcurrentResultMethodParameter extends HandlerMethodParameter {
		@Nullable
		private final Object returnValue;
		private final ResolvableType returnType;
		public ConcurrentResultMethodParameter(Object returnValue) {
			super(-1);
			this.returnValue = returnValue;
			// 主要是這個解析 兼容到了泛型類型 比如你的返回值是List<Person> 它也能把你的類型拿出來
			this.returnType = (returnValue instanceof ReactiveTypeHandler.CollectedValuesList ?
					((ReactiveTypeHandler.CollectedValuesList) returnValue).getReturnType() :
					ResolvableType.forType(super.getGenericParameterType()).getGeneric());
		}

		// 若返回的是List  這裏就是List的類型哦  下面纔是返回泛型類型
		@Override
		public Class<?> getParameterType() {
			if (this.returnValue != null) {
				return this.returnValue.getClass();
			}
			if (!ResolvableType.NONE.equals(this.returnType)) {
				return this.returnType.toClass();
			}
			return super.getParameterType();
		}

		// 返回泛型類型
		@Override
		public Type getGenericParameterType() {
			return this.returnType.getType();
		}


		// 即使實際返回類型爲ResponseEntity<Flux<T>>,也要確保對@ResponseBody-style處理從reactive 類型中收集值
		// 是對reactive 的一種兼容
		@Override
		public <T extends Annotation> boolean hasMethodAnnotation(Class<T> annotationType) {
			// Ensure @ResponseBody-style handling for values collected from a reactive type
			// even if actual return type is ResponseEntity<Flux<T>>
			return (super.hasMethodAnnotation(annotationType) ||
					(annotationType == ResponseBody.class && this.returnValue instanceof ReactiveTypeHandler.CollectedValuesList));
		}
	}


	// 這個非常有意思   內部類繼承了自己(外部類) 進行增強
	private class ConcurrentResultHandlerMethod extends ServletInvocableHandlerMethod {
		// 返回值
		private final MethodParameter returnType;

		// 此構造最終傳入的handler是個Callable
		// result方法返回值 它支持支持異常調用結果處理
		public ConcurrentResultHandlerMethod(final Object result, ConcurrentResultMethodParameter returnType) {
			super((Callable<Object>) () -> {
				if (result instanceof Exception) {
					throw (Exception) result;
				} else if (result instanceof Throwable) {
					throw new NestedServletException("Async processing failed", (Throwable) result);
				}
				return result;
			}, CALLABLE_METHOD);


			// 給外部類把值設置上  因爲wrapConcurrentResult一般都先調用,是對本類的一個增強
			if (ServletInvocableHandlerMethod.this.returnValueHandlers != null) {
				setHandlerMethodReturnValueHandlers(ServletInvocableHandlerMethod.this.returnValueHandlers);
			}
			this.returnType = returnType;
		}
		...
	}
}

HandlerMethod封裝Handler和處理請求的Method
InvocableHandlerMethod增加了方法參數解析和調用方法的能力;ServletInvocableHandlerMethod在此基礎上在增加了如下三個能力:
對@ResponseStatus註解的支持
1.當一個方法註釋了@ResponseStatus後,響應碼就是註解上的響應碼。 並且,並且如果returnValue=null或者reason不爲空(不爲null且不爲“”),將中斷處理直接返回(不再渲染頁面)
對返回值returnValue的處理

  1. 對返回值的處理是使用HandlerMethodReturnValueHandlerComposite完成的
    對異步處理結果的處理

@Getter
@Setter
@ToString
public class Person {

@NotNull
private String name;
@NotNull
@Positive
private Integer age;

public Object demoMethod(Person person, Object object,
                         List<Integer> intList, List<Person> personList,
                         Set<Integer> intSet, Set<Person> personSet,
                         Map<String, Object> myMap,
                         String name, Integer age,
                         int number, double money) {
    return "hello parameter";
}

}
藉助HandlerMethod完成此測試用例

public static void main(String[] args) {
    // 準備一個HandlerMethod
    HandlerMethod handlerMethod = new HandlerMethod(new Person(), getPersonSpecfyMethod());
    // 拿到該方法所有的參數
    MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
    for (MethodParameter parameter : methodParameters) {
        Class<?> parameterType = parameter.getParameterType();
        String nameForParameter = ModelFactory.getNameForParameter(parameter);
        System.out.println("類型" + parameterType.getName() + "--->缺省的modelKey是:" + nameForParameter);
    }
}

private static Method getPersonSpecfyMethod() {
    for (Method method : Person.class.getMethods())
        if (method.getName().equals("demoMethod"))
            return method;
    return null;
}

運行,打印結果如下:

類型com.fsx.bean.Person—>缺省的modelKey是:person
類型java.lang.Object—>缺省的modelKey是:object
類型java.util.List—>缺省的modelKey是:integerList
類型java.util.List—>缺省的modelKey是:personList
類型java.util.Set—>缺省的modelKey是:integerList // 可以看到即使是set 名稱也是同List的
類型java.util.Set—>缺省的modelKey是:personList
類型java.util.Map—>缺省的modelKey是:map
類型java.lang.String—>缺省的modelKey是:string
類型java.lang.Integer—>缺省的modelKey是:integer
類型int—>缺省的modelKey是:int
類型double—>缺省的modelKey是:double
這個結果是不同類型對應的缺省的ModelKey,對理解和正確使用@SessionAttributes、@ModelAttribute都很重要

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