Spring MVC源码解析之HandlerMethod、ServletInvocableHandlerMethod

举报
JavaEdge 发表于 2021/06/03 23:18:11 2021/06/03
【摘要】 InvocableHandlerMethod 增加了调用能力:在调用的时候,把方法入参都封装进,主要还是依靠 HandlerMethodArgumentResolver,只是把解析好的放到对应位置里去 public class InvocableHandlerMethod extends HandlerMethod { private static final O...

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();

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在给定请求的上下文中解析方法的参数值后调用该方法。 也就是说:方法入参里就能够自动使用请求域(包括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;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

小结

该类主要提供了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;
	}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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);
		}
		...
	}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
	// 设置返回的状态码到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;
		}
		...
	}
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

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";
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

}
借助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;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

运行,打印结果如下:

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

文章来源: javaedge.blog.csdn.net,作者:JavaEdge.,版权归原作者所有,如需转载,请联系作者。

原文链接:javaedge.blog.csdn.net/article/details/106523765

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。