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都很重要

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