1、前面一篇文章我们讲解了HandlerMapping的主流实现RequestMappingHandlerMapping处理器映射器,今天来剖析HandlerAdapter。RequestMappingHandlerMapping是主流使用的,那么对应肯定也有一个主流使用的RequestMappingHandlerAdapter,这个只是猜测,那么我们来spring-webmvc的源码中找一下看看,果不其然我们找到了如下图:
没毛病,这就是目前主要使用的HandlerAdapter的实现。
2、我们知道HandlerMapping就是用来寻找一个HandlerExecutionChain即处理器执行链,里面封装了请求的拦截器,真正的处理器,而HandlerAdapter就是用来执行这个执行链的,也就是执行真正的处理器的,大白话一点就是Controller中的某个方法。
3、在DispatcherServlet的整个执行链路中,先获取到HandlerExecutionChain,然后在找到合适的HandlerAdapter来执行HandlerExecutionChain中的真正的处理器,套路就是这样搞。DispatcherServlet中的源码体现如下(片段):
// Determine handler adapter for the current request.
使用执行链里的处理去找到适合自己的处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified)&&isGet) {
return;
}
}
执行拦截器的前置方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
使用找到的合适的HandlerAdapter来执行真正的处理器,并返回一个ModelAndView实例
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
执行拦截器的后置方法。
mappedHandler.applyPostHandle(processedRequest, response, mv);
4、整体流程我们知道了,那么我们来讨论两个问题,第一个:如何查找适合的处理器适配器HnadlerAdapter呢?
第二个:处理器适配器HandlerAdapter是如何执行真正的处理器的呢?
.5、我们来解答第一个问题,如何根据真正的处理器来查找合适的处理器适配器。
在DispatcherServlet中源码如下:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
循环DispatcherServlet实例的handlerAdapters列表
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
传入真正的处理器,看看当前的处理器适配器HandlerAdapter是否支持当前的处理器。
if (ha.supports(handler)) {
如果支持就直接返回当前的处理器实例。
return ha;
}
}
如果都不支持,那就抛出异常。
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
我们知道我们得到的真正的处理器类型是HandlerMethod类型的,我们来看看那些HandlerAdapter支持HandlerMethod类型的处理器,HandlerAdapter类图如下:
在RequestMappingHandlerAdapter的父类AbstractHandlerMethodAdapter中的 supports(Object handler)方法如下:
@Override
public final boolean supports(Object handler) {
如果handler是HandlerMethod类型的,并且内部支持HandlerMethod类型处理器,那就返回true
这个supportsInternal方法是抽象方法,由RequestMappingHandlerAdapter实现。
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
在RequestMappingHandlerAdapter中的supportsInternal方法如下:
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
直接返回true,表示支持类型是HandlerMethod类型的处理器
return true;
}
找处理器适配器HandlerAdapter的方式就是,循环调用拥有的处理器适配器的supports方法,如果返回true,那么就是用这个处理器映射器。
针对我们当前主要使用的实现就是RequestMappingHandlerAdapter实现类,其他的可以不用去深究。
6、接下来我们来剖析第二个问题,如何使用处理器适配器HandlerAdapter来执行真正的处理器。
6.1、执行处理器在DispatcherServlet中的执行链路的源码体现:
使用HandlerAdapter去执行真正的处理器
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
6.2、我们使用的是RequestMappingHandlerAdapter来执行的,源码如下:
在父类AbstractHandlerMethodAdapter中:
@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
执行内部的处理方法,在RequestMappingHandlerAdapter中有具体的实现
return handleInternal(request, response, (HandlerMethod) handler);
}
在RequestMappingHandlerAdapter中的内部执行处理器的方法源码:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
第1步:将请求使用ServletWebRequest进行装饰。
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
第2步:使用handlerMethod去获取一个WebDataBinderFactory即web 数据绑定工厂。
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
第3步:使用参数绑定工厂+ 处理器 获取一个模型工厂ModelFactory。
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
第4步:使用处理器创建一个Servlet可调用的处理器实例
ServletInvocableHandlerMethod并设置其重要的属性
argumentResolvers、parameterNameDiscoverer、
returnValueHandlers、dataBinderFactory
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandl
ers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
第5步:创建一个ModelAndViewContainer即ModelAndView的容器并初始化。
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
第6步:对异步请求的支持条件构建。
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
第7步:执行处理器的方法。
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
第8步:返回ModelAndView。
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
上面的RequestMappingHandlerAdapter的源码主要分为8个大步骤,接下来我们逐一进行详细分析:
第1步->将请求使用ServletWebRequest进行装饰, 为什么需要把HttpServletRequest装饰成为一个ServletWebRequest类型呢?原因是ServletWebRequest提供了请求头里的Last-Modified、If-Modified-Since、ETag的判定,可以做到如果请求资源没有任何改变,重复请求会告诉浏览器304 表示使用浏览器缓存的资源,关于这些请移步https://blog.csdn.net/Dongguabai/article/details/84323511。
第2步->使用handlerMethod去获取一个WebDataBinderFactory即web 数据绑定工厂,这个地方很核心,表面意思就是获取一个WebDataBinderFactory即web数据绑定者工厂,我们来分析源码然后再谈谈工作机制:
类图:WebDataBinderFactory的作用就是用来创建并初始化一个WebDataBinder实例
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
入参是HandlerMethod 实例
1、获取HandlerMethod 实例的beanType 其实就是Controller的class。
Class<?> handlerType = handlerMethod.getBeanType();
2、从缓存中查找初始化的绑定者,什么是绑定者,待会详细说明。
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
如果缓存中没有找到当前Controller中的绑定者,那就查找当前Controller中
的绑定者,此处的INIT_BINDER_METHODS入参是一个MethodFilter,就是控制
只查找Controller中被@InitBinder注解标注的方法。
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
找到后将其加入到缓存中,以提高效率,从这里可以看出来其实绑定者就是
Controller类中被@InitBinder注解标注的方法,一个方法对应一个绑定者。
this.initBinderCache.put(handlerType, methods);
}
3、创建一个空集合用于存放InvocableHandlerMethod实例
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
// Global methods first
4、先找到全局的被@InitBinder的方法,从哪里找呢?就是从被@ControllerAdvice标注的类中去找。在RequsetMappingHandlerAdapter
初始化的时候会去解析全局的被@InitBinder的方法,然后添加到initBinderAdviceCache缓存中。
for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
Object bean = entry.getKey().resolveBean();
for (Method method : entry.getValue()) {
构建一个类型是InvocableHandlerMethod的可执行方法。
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
}
5、然后再将当前的Controller类中的@InitBinder标注的方法构建为一个InvocableHandlerMethod实例添加到initBinderMethods集合的尾部。
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
6、构建一个类型是ServletRequestDataBinderFactory的数据绑定者工厂
return createDataBinderFactory(initBinderMethods);
}
构建一个类型是InvocableHandlerMethod的可执行方法
private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {
直接new 一个InvocableHandlerMethod实例,使用initBinderMethod + Controller类||ControllerAdvice类。
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
设置可执行方法的参数解析器
binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
设置可执行方法的数据绑定工厂为一个DefaultDataBinderFactory,
入参webBindingInitializer实在注解驱动阶段设置的类型是ConfigurableWebBindingInitializer的实例。
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
设置可执行方法的参数名称查找器
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
return binderMethod;
}
WebDataBinder在初始化的过程中会调用被@InitBinder标注的方法。
第3步->使用第2步中获取到的参数绑定工厂+ 处理器 获取一个模型工厂ModelFactory,ModelFactory的作用就是用来执行被@ModelAttribute标注的方法以及处理@SessionAttribute注解的逻辑的,源码分析如下:
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
入参是HandlerMethod 实例 + 数据绑定工厂binderFactory
1、获取SessionAttributesHandler即找到当前Controller类上标注的@SessionAttributes注解,
根据注解的属性构建一个SessionAttributesHandler。
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
2、获取当前的Controller类的类型。
Class<?> handlerType = handlerMethod.getBeanType();
3、查找当前Controller类中被注解@ModelAttribute注解标注的方法并添加到modelAttributeCache缓存中
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
4、创建一个空集合用于存放可执行的方法实例
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
// Global methods first
5、先查找全局的被@ModelAttribute注解标注的方法,从哪里找呢?就是从被@ControllerAdvice标注的类中去找。在RequsetMappingHandlerAdapter
初始化的时候会去解析全局的被@ModelAttribute的方法,然后添加到modelAttributeAdviceCache缓存中。
for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
Object bean = entry.getKey().resolveBean();
for (Method method : entry.getValue()) {
构建一个类型是InvocableHandlerMethod的可执行方法。
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
}
6、然后再将当前的Controller类中的@ModelAttribute标注的方法构建为一个InvocableHandlerMethod实例添加到attrMethods集合的尾部。
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
7、使用attrMethods + 参数绑定工厂+SessionAttributesHandler实例来构建一个ModelFactory返回。
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
构建一个类型是InvocableHandlerMethod的可执行方法。
private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) {
直接new 一个InvocableHandlerMethod实例,使用ModelAttributMethod + Controller类||ControllerAdvice类。
InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);
设置可执行方法的参数解析器
attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
设置可执行方法的参数名称查找器
attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
设置可执行方法的数据绑定工厂。
attrMethod.setDataBinderFactory(factory);
return attrMethod;
}
第4步->使用HandlerMethod实例创建一个ServletInvocableHandlerMethod并设置重要属性。
创建一个ServletInvocableHandlerMethod实例,就是直接new
ServletInvocableHandlerMethod invocableMethod =
createInvocableHandlerMethod(handlerMethod);
设置其参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
设置其返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
设置其数据绑定工厂为第2步中构建的数据绑定工厂
invocableMethod.setDataBinderFactory(binderFactory);
设置其参数名称查找器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
第5步:创建一个ModelAndViewContainer即ModelAndView的容器并初始化,我们主要查看初始化ModelAndViewContainer的流程。ModelAndViewContainer里面主要会存放view对象+model数据。
直接new 一个ModelAndViewContainer实例
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
添加数据到ModelAndViewContainer中的model中
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
使用第3步构建的modelFactory初始化model,会执行@ModelAttribute方法以及SessionAttributeHandler方法。
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
设置在重定向的时候忽列model
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
使用modelFactory来初始化model:
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
1、先检查当前请求中的HttpSession中是否有@SessionAttributez注解定义的数据,如果有获取出来构建成一个Map,key=attributeName value=设置的值。
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
2、合并session中的sessionAttributes数据,合并的意思就是使用session中的值替换掉ModelAndViewContainer中的model中同名称的值。
container.mergeAttributes(sessionAttributes);
3、执行被注解@ModelAttribute标注的方法,在构建ModelFactory的时候传入的解析好的attrMethod,
在构建阶段会为使用其每一个InvocableHandlerMethod来构建一个ModelMethod实例,然后添加到ModelFactory的modelMethods集合中,
在这一步会进行逐一调用。调用完成后会将返回值添加到ModelAndViewContainer中的model中。
invokeModelAttributeMethods(request, container);
4、查找当前的真正的处理器handlerMethod的method的参数是否标记了@ModelAttribute注解,
for (String name : findSessionAttributeArguments(handlerMethod)) {
找到被注解@ModelAttribute标记的参数。
if (!container.containsAttribute(name)) {
如果ModelAndViewContainer的model中不存在此属性值,那就去检索HttpSession中是否存在此值,如果没有直接抛出异常。
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
如果session中有值
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
如果HttpSession中存在此值,就加添加到ModelAndViewContainer中的model中。
container.addAttribute(name, value);
}
}
}
这个HttpSessionRequiredException异常在什么时候会抛出呢??? 案例如下:
@Controller
@RequestMapping("user")
@SessionAttributes({"user", "userName"})
public class UserController {
@ModelAttribute("user")
public User modelAttribute() {
User user = new User();
user.setUserId("1");
return user;
}
@RequestMapping("test")
public String test(@ModelAttribute("user") User user, @ModelAttribute("userName") String userName) {
参数userName在ModelAndViewContainer 的model中不存在,并且在HttpSession
中也不存在,所有此处这样使用,在调用/user/test的时候会抛出
HttpSessionRequiredException。为什么user参数不会呢?因为执行到此处的时候
ModelAndViewContainer 的model中已经存在user属性了。
user.setUserId("2");
return "register";
}
}
第6步:对异步请求的支持条件构建,这一步没啥好说的,基本上不会使用。
第7步:执行处理器HandlerMethod。
invocableMethod.invokeAndHandle(webRequest, mavContainer);
ServletInvocableHandlerMethod(继承了InvocableHandlerMethod)类中:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
1、执行处理器方法HandlerMethod
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
2、设置响应状态
setResponseStatus(webRequest);
3、处理返回值为空的情况
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);
4、使用返回值处理器对返回值进行处理
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
重点在执行处理器方法HandlerMethod : InvocableHandlerMethod类中
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
1、获取方法参数值列表
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"' with arguments " + Arrays.toString(args));
}
2、执行真正的method方法,就是Controller中的方法。
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"] returned [" + returnValue + "]");
}
return returnValue;
}
获取方法参数值列表是个大步骤,SpringMVC/spring很多核心的组件都在这一步工作,比如ConversionService、Converter<S, T>、HttpMessageConverter<T>、PropertyEditor、Format等。
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
1、获取HandlerMethod中的方法入参列表,在HnadlerMapping初始化的时候构建HandlerMethod的时候就以及解析好的了。
MethodParameter[] parameters = getMethodParameters();
2、创建一个方法入参value值的空数组,长度为方法入参的个数。
Object[] args = new Object[parameters.length];
3、循环绑定每一个方法入参的value值
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
3.1、初始化当前参数的名称发现者,默认是一个DefaultParameterNameDiscoverer。
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
3.2、从提供的providedArgs参数列表获取参数,默认providedArgs是空数组。
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
3.3、判断当前的参数解析器列表中,是否有能支持当前参数的 参数解析器,如果没有就会抛出IllegalStateException异常。
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
3.4、如果有能够支持当前参数解析的 参数解析器,那就进行参数解析。
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
throw new IllegalStateException("Could not resolve method parameter at index " +
parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
}
}
3.5、所有参数解析完成后,就得到了方法的入参值列表返回。
return args;
}
上面源码中提到了InvocableMethod的参数解析器列表argumentResolvers,这个列表是什么时候初始化的呢?还记得在DispatcherServlet中构建了一个ServletInvocableHandlerMethod实例吗,ServletInvocableHandlerMethod继承了InvocableMethod类,在构建ServletInvocableHandlerMethod的时候设置了这个参数解析器列表argumentResolvers,设置的值就是RequestMappingHandlerAdapter中的argumentResolvers,也就是说 InvocableMethod.argumentResolvers=RequestMappingHandlerAdapter.argumentResolvers
InvocableMethod.returnValueHandlers=RequestMappingHandlerAdapter.returnValueHandlers
InvocableMethod.parameterNameDiscoverer=RequestMappingHandlerAdapter.parameterNameDiscoverer
InvocableMethod.dataBinderFactor=ServletRequestDataBinderFactory实例
那么RequestMappingHandlerAdapter.argumentResolvers实在什么时候设置的呢?RequestMappingHandlerAdapter实现了InitializingBean接口,在初始化的时候会设置一些默认的HandlerMethodArgumentResolver参数解析器,同时也会设置一下默认的返回结果处理器HandlerMethodReturnValueHandler,源码如下:
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
1、初始化Controller通知类
initControllerAdviceCache();
2、初始化参数解析器列表
if (this.argumentResolvers == null) {
获取默认的参数解析器列表,然后设置到当前RequestMappingHandlerAdapter的argumentResolvers属性。
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
3、初始化initBinder方法的参数解析器什么意思呢?就是说被注解@InitBinder标注的方法,
在调用的时候也需要进行参数解析,就是用initBinderArgumentResolvers这个里面的参数解析器进行入参绑定。
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
4、初始化返回结果处理器列表
if (this.returnValueHandlers == null) {
获取默认的返回结果处理器列表并设置给当前RequestMappingHandlerAdapter的returnValueHandlers属性。
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
获取默认的参数解析器列表:
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); 支持@RequestParam注解标注的入参绑定,如下示例中的file参数也是使用此参数解析器,但是fileName不是,因为这个参数解析器
的useDefaultResolution属性设置为了false,所以它不支持String fileName参数绑定。在下面还会添加一个同类型的RequestParamMethodArgumentResolver
参数解析器用于支持String fileName这种基本类型的入参绑定。
@RequestMapping(value = "upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
private String upload(MultipartFile file, String fileName){
resolvers.add(new RequestParamMapMethodArgumentResolver()); 支持@RequestParam注解标注的入参且类型是Map类型的入参绑定
resolvers.add(new PathVariableMethodArgumentResolver()); 支持@PathVariable注解标注的入参绑定
resolvers.add(new PathVariableMapMethodArgumentResolver()); 支持@PathVariable注解标注的入参且类型是Map类型的入参绑定
resolvers.add(new MatrixVariableMethodArgumentResolver()); 支持@MatrixVariable注解标注的入参绑定
resolvers.add(new MatrixVariableMapMethodArgumentResolver()); 支持@MatrixVariable注解标注的入参类型是Map类型的入参绑定
resolvers.add(new ServletModelAttributeMethodProcessor(false)); 支持常规的封装类型入参如Huma huma 也支持@ModelAttribute注解标注的入参绑定
案例:@RequestMapping("test")
public String test(Huma huma) {
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); 支持@RequestBody标注的入参绑定,
同时也是一个返回结果处理器,支持方法被@ResponseBody标注的方法,这个处理器需要使用HttpMessageConverters来进行body里面的数据与参数类型的转换。
同时这个参数解析器还需要使用requestResponseBodyAdvice 通知类列表在read 数据前后进行通知,我们可以在利用这个特性进行扩展。
!!!!HttpMessageConverters 只会在入参是@RequestBody、@ResponseBody的这种方式下会使用到,其他方式都是使用Converter<S, T>来进行入参绑定。
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); 支持@RequestPart标注的入参绑定,一般用于文件上传的请求
@RequestMapping(value = "upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
private String upload(@RequestPart("file") MultipartFile file, String fileName){
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); 支持@RequestHeader标注的入参绑定,用于从请求头中获取参数进行绑定
resolvers.add(new RequestHeaderMapMethodArgumentResolver()); 支持@RequestHeader标注的入参绑且类型是Map类型的入参绑定,用于从请求头中获取参数进行绑定
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); 支持@CookieValue注解标注的入参绑定
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); 支持@Value标注的入参绑定,绑定的时候进行表达式计算
resolvers.add(new SessionAttributeMethodArgumentResolver()); 支持@SessionAttribute标注的入参绑定,用于从HttpSession中获取数据进行绑定
resolvers.add(new RequestAttributeMethodArgumentResolver()); 支持@RequestAttribute标注的入参绑定,其实就是获取request的属性,与request.getParameter(“name”)不一样哈。
案例:
@RequestMapping("/httpServletRequestParam")
public String httpServletRequestParam(HttpServletRequest request,
@RequestAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT") Object o){
WebApplicationContext attribute = (WebApplicationContext) request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
System.out.println(o==attribute); //结果输出为true
return "register";
}
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver()); 支持入参类型是WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal
InputStream、Reader、HttpMethod、Locale、TimeZone 或者参数名称="ava.time.ZoneId" 类型的入参
其含义就是取请求request对象或者request对象的属性进行入参绑定。
resolvers.add(new ServletResponseMethodArgumentResolver()); 支持入参类型是ServletResponse、OutputStream、Writer其含义就是取请求response对象或者response对象的属性进行入参绑定。
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); 支持入参类型是HttpEntity、RequestEntity的入参绑定
resolvers.add(new RedirectAttributesMethodArgumentResolver()); 支持入参类型是RedirectAttributes的入参绑定
resolvers.add(new ModelMethodProcessor()); 支持入参类型是Model的入参绑定
resolvers.add(new MapMethodProcessor()); 支持入参是Map类型的入参绑定
resolvers.add(new ErrorsMethodArgumentResolver()); 支持入参是org.springframework.validation.Errors类型的入参绑定,SpringMVC的验证结果BindingResult就是继承了Errors。
resolvers.add(new SessionStatusMethodArgumentResolver()); 支持入参类型是SessionStatus的入参绑定,其含义是直接获取 ModelAndViewContainer实例的SessionStatus实例进行入参绑定
resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); 支持入参类型是UriComponentsBuilder、ServletUriComponentsBuilder的入参绑定,
其含义就是将使用构建请求uri的 uri组件构建者获取到绑定到入参上,我们可以使用UriComponentsBuilder取获取请求uri的信息
案例:
@RequestMapping(value = "uriComponentsBuilderTest")
private String UriComponentsBuilderTest(UriComponentsBuilder uriComponentsBuilder,
ServletUriComponentsBuilder servletUriComponentsBuilder){
System.out.println(uriComponentsBuilder.toUriString());
System.out.println(servletUriComponentsBuilder.toUriString());
return "register";
}
输出如下:
http://localhost:7070
http://localhost:7070
添加自定义的参数解析器
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
在添加一个RequestParamMethodArgumentResolver且为默认处理基本类型的参数解析器,在上面已经添加了一个同类型的,只是默认不处理基本类型
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
再添加一个注解不是必须的ServletModelAttributeMethodProcessor参数解析器,也就是说没有@ModelAttribute注解的封装类型支持 如User user类型入参绑定
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
上面这段源码太长,建议读者自己拷贝出来阅读,这就是RequestMappingHandlerAdapter初始化参数解析器列表的主要实现。
接着主流程走,找到相应支持当前参数的参数解析器后,那就是用其参数解析器就行参数value的解析,然后将参数的value值添加到方法的入参数组中。我们讲解两个重要且常用的参数解析器:
第一个:RequestParamMethodArgumentResolver(getBeanFactory(), false)
第二个:RequestResponseBodyMethodProcessor
RequestParamMethodArgumentResolver(getBeanFactory(), false) 实现原理分析:
类图:继承了抽象的AbstractNamedValueMethodArgumentResolver
解析参数的方法实现:RequestParamMethodArgumentResolver 来说,解析参数的方法在父类AbstractNamedValueMethodArgumentResolver中:
AbstractNamedValueMethodArgumentResolver中源码如下:
@Override
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
1、构建一个NamedValueInfo实例 实例包含参数名称name、是否是必须required、
defaultValue 三个信息,会进行缓存NamedValueInfo实例方便下一次调用的时候提高
性能。getNamedValueInfo方法中调用了createNamedValueInfo(MethodParameter
parameter)抽象方法,具体的实现由RequestParamMethodArgumentResolver实现
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);解决参数嵌套
就是检查参数是否是Java8 的java.util.Optional类型,如果是就解析嵌套。
MethodParameter nestedParameter = parameter.nestedIfOptional();
2、解析参数名称
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
3、解析参数值,是抽象方法,具体的实现由RequestParamMethodArgumentResolver实现,这里我们不再细看,针对RequestParamMethodArgumentResolver就如下事情:
如果是上传文件请求,解析文件,否则就直接 request.getParameterValues(name);获取入参的字符串,如果请求中存在多个名称相同的入参,则返回字符串数组。
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
如果从请求中获取到参数,且有默认值,那就解析默认值
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
如果从请求中获取到参数,且参数是必须的且不是Java8 的java.util.Optional,那就抛出异常MissingServletRequestPartException
异常的message="Required request part '" + partName + "' is not present" 这个错误应该是不叫出名的吧!!
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
处理默认值,比如是Boolean类型的话,那就赋值为false,目前只针对Boolean进行了默认值赋值。
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
如果参数值是empty 字符串 且参数的默认值不为空
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
那就使用默认值赋值给入参
arg = resolveStringValue(namedValueInfo.defaultValue);
}
4、如果参数值获取到了且binderFactory 参数绑定者工厂不为空,那就创建参数绑定器进行参数绑定。
if (binderFactory != null) {
使用binderFactory创建参数绑定器DataBinder,WebDataBinder继承自DataBinder,根据整体的调用链跟踪下来我们的binderFactory 类型是ServletRequestDataBinderFactory
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
使用binder进行参数转换,其实就是返回Controller的方法入参,也许有人会问,前面不是拿到请求的参数了吗?为什么还需要进行参数转换,
原因就是请求里面的参数都是字符类型的,如果我们接受的不是字符类型,比如数字,集合,包装类型的数据,那么就需要进行入参转换了,这
就是参数绑定者binder的作用。
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
处理器已经转换的参数,这里在PathVariableMethodArgumentResolver有实现,其他的参数解析器目前没有任何操作。
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
上面源码中我们看到了使用WebDataBinderFactory来创建一个数据绑定者binder
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
我们知道来看看实现是什么,在前面我们已经介绍了WebDataBinderFactory的整体类结构:
在DefaultDataBinderFactory中:
@Override
public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
throws Exception {
1、创建一个绑定者,就是直接new ExtendedServletRequestDataBinder,
是因为我们的WebDataBinderFactory类型是ServletRequestDataBinderFactory,
所以创建的过程在ServletRequestDataBinderFactory中。
objectName是参数名称,target是null,webRequest就是我们的请求。
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
if (this.initializer != null) {
如果当前的DataBinderFactory 绑定者工厂的属性 private final WebBindingInitializer initializer;不为空,那就初始化绑定初始者。
initializer我们是在解析注解驱动的时候给RequestMappingHandlerAdapter设置的时候设置了handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
默认使用的实现是ConfigurableWebBindingInitializer实例,当然我们也可以自定义。
this.initializer.initBinder(dataBinder, webRequest);
}
2、初始化创建的绑定者(区分三个角色WebDataBinderFactory:绑定者工厂,专门由于构建绑定者。
WebBindingInitializer : 绑定初始化者,专门用于初始化绑定者。
WebDataBinder:真正的绑定者,复杂数据转换工作。)
初始化绑定者的时候会调用之前解析好的@InitBinder标注的方法。
案例:
@InitBinder
private void configDataBinder(WebDataBinder webDataBinder) {
//给当前的绑定者注册自定义属性编辑器,注意方法一定要使用@InitBinder标注。此编辑器的作用域只是当前的Controller类中。
//注册到dataBinder的SimpleTypeConverter属性的customerEditors的集合中。
webDataBinder.registerCustomEditor(Person.class, new PersonEditor111());
}
initBinder(dataBinder, webRequest);
返回初始化好的绑定者
return dataBinder;
}
类图:
类图可以看出DataBinder是WebDataBinder的父类,我们来看看DataBinder的重要属性:
public class DataBinder implements PropertyEditorRegistry, TypeConverter {
public static final String DEFAULT_OBJECT_NAME = "target";
public static final int DEFAULT_AUTO_GROW_COLLECTION_LIMIT = 256;
protected static final Log logger = LogFactory.getLog(DataBinder.class);
private static Class<?> javaUtilOptionalClass = null;
private final Object target; 一般是null
private final String objectName; 当前处理的参数名称,也就是说一个入参会对应一个DataBinder实例
private AbstractPropertyBindingResult bindingResult;
private SimpleTypeConverter typeConverter; 常规的类型转换器,这里
private boolean ignoreUnknownFields;
private boolean ignoreInvalidFields;
private boolean autoGrowNestedPaths;
private int autoGrowCollectionLimit;
private String[] allowedFields;
private String[] disallowedFields;
private String[] requiredFields;
private ConversionService conversionService; 转换服务
private MessageCodesResolver messageCodesResolver; 消息code解析器
private BindingErrorProcessor bindingErrorProcessor; 绑定错误处理器,默认是DefaultBindingErrorProcessor实例
private final List<Validator> validators; 验证器列表
接下来我们分析DataBinder是如何进行入参转换的:
DataBinder中:
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
先获取到DataBinder的SimpleTypeConverter实例,然后进行类型转换
return this.getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
}
SimpleTypeConverter父类TypeConverterSupport中:
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
找到doConvert方法进行参数类型转换。
return this.doConvert(value, requiredType, methodParam, (Field)null);
}
private <T> T doConvert(Object value, Class<T> requiredType, MethodParameter methodParam, Field field) throws TypeMismatchException {
try {
使用TypeConverterSupport中的typeConverterDelegate进行参数类型转换
return field != null ? this.typeConverterDelegate.convertIfNecessary(value, requiredType, field) : this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
} catch (ConverterNotFoundException var6) {
throw new ConversionNotSupportedException(value, requiredType, var6);
} catch (ConversionException var7) {
throw new TypeMismatchException(value, requiredType, var7);
} catch (IllegalStateException var8) {
throw new ConversionNotSupportedException(value, requiredType, var8);
} catch (IllegalArgumentException var9) {
throw new TypeMismatchException(value, requiredType, var9);
}
}
TypeConverterDelegate是TypeConverterSupport中的一个属性作用就是代理类型转换任务,TypeConverterDelegate中的private final PropertyEditorRegistrySupport propertyEditorRegistry; 属性值就是SimpleTypeConverter实例,接着流程往下走:
TypeConverterDelegate中:
public <T> T convertIfNecessary(Object newValue, Class<T> requiredType, MethodParameter methodParam) throws IllegalArgumentException {
return this.convertIfNecessary((String)null, (Object)null, newValue, requiredType, methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType));
}
下面这个方法比较长,是真正处理类型转换的逻辑:
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
从SimpleTypeConverter 中通过参数类型获取到自定义的属性编辑器,
也就是我们可以手动的通过WebDataBinder去注册自己想要的属性编辑器。
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
从SimpleTypeConverter 中获取到类型转换服务conversionService,这个我们在注解驱动是时候可配
置 <mvc:annotation-driven conversion-service="conversionService" >如果不配置的话会配置
一个默认的FormattingConversionServiceFactoryBean,conversionService这个bean会初始化好
很多默认的Converter<S, T>,如StringToBooleanConverter、StringToLocaleConverter等等
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
如果没有找到参数类型相关的属性编辑器器,但是找到了conversionService,那就使用conversionService进行类型转换。
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
如果没有找到属性编辑器的话,但是找到了conversionService的话,那就循环去判断
conversionService中的所有的Converter转换器,谁支持入参的类型就是用谁进行类型转换。
还记得都写过的Date入参的类型转换吗?原因就是因为conversionService默认是没有Date类型的Converter的。
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
} catch (ConversionFailedException var14) {
conversionAttemptEx = var14;
}
}
}
Object convertedValue = newValue;
如果属性类型相关的编辑器存在
if (editor != null || requiredType != null && !ClassUtils.isAssignableValue(requiredType, newValue)) {
如果参数类型是集合,且请求里面的参数值是String类型,那就split(",")得到一个数组赋值到convertedValue。
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && newValue instanceof String) {
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String)newValue);
}
}
}
如果没有找到用户自定义的属性编辑器,那就去寻找默认的属性编辑器,此处也就是SpringMVC支持JavaBean规范的地方
if (editor == null) {
editor = this.findDefaultEditor(requiredType);
}
使用找到的属性编辑器进行入参类型转换。此处不在细看,很简单,就是调用编辑器的方法而已。
convertedValue = this.doConvertValue(oldValue, convertedValue, requiredType, editor);
}
如果通过以上匹配未发现相关的属性编辑器或者Converter那就兜底处理,此处大致过一下,感兴趣的可以深究一下。主要掌握 Editor 和 Converter 的作用就好。
boolean standardConversion = false;
if (requiredType != null) {
if (convertedValue != null) {
如果入参是Object类型,直接返回请求里的值
if (Object.class == requiredType) {
return convertedValue;
}
如果是数组,那就转换为数组
if (requiredType.isArray()) {
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String)convertedValue);
}
return this.convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
如果是集合那就转换为集合类型
if (convertedValue instanceof Collection) {
convertedValue = this.convertToTypedCollection((Collection)convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
如果是Map那就转换为Map类型
} else if (convertedValue instanceof Map) {
convertedValue = this.convertToTypedMap((Map)convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
return convertedValue.toString();
}
if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
try {
Constructor<T> strCtor = requiredType.getConstructor(String.class);
return BeanUtils.instantiateClass(strCtor, new Object[]{convertedValue});
} catch (NoSuchMethodException var12) {
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", var12);
}
} catch (Exception var13) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", var13);
}
}
}
String trimmedValue = ((String)convertedValue).trim();
if (requiredType.isEnum() && "".equals(trimmedValue)) {
return null;
}
convertedValue = this.attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
} else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
convertedValue = NumberUtils.convertNumberToTargetClass((Number)convertedValue, requiredType);
standardConversion = true;
}
} else if (javaUtilOptionalEmpty != null && requiredType == javaUtilOptionalEmpty.getClass()) {
convertedValue = javaUtilOptionalEmpty;
}
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (conversionAttemptEx != null) {
throw conversionAttemptEx;
}
if (conversionService != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
}
StringBuilder msg = new StringBuilder();
msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append("'");
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append("] returned inappropriate value of type '").append(ClassUtils.getDescriptiveType(convertedValue)).append("'");
throw new IllegalArgumentException(msg.toString());
}
msg.append(": no matching editors or conversion strategy found");
throw new IllegalStateException(msg.toString());
}
}
if (conversionAttemptEx != null) {
if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
throw conversionAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
}
return convertedValue;
}
讲完RequestParamMethodArgumentResolver(getBeanFactory(), false)接下来我们来分析RequestResponseBodyMethodProcessor:
老规矩先看类图:
从类图可以看出RequestResponseBodyMethodProcessor即是也是一个ArgumentResovler也是一个ReturnValueHandler, 在上面我们知道RequestResponseBodyMethodProcessor支持的入参是@RequestBody标注的入参,支持的返回值处理的方式是方法被注解@ResponseBody标注的方法。
我们的messageConverters列表是在什么时候初始化的呢?
这个问题其实之前已经讲过,那就是在RequestMappingHandlerAdapter在构建BeanDefinition实例的时候赋值的,在注解驱动解析阶段的源码体现如下:
此处会添加一下默认的HttpMessageConverteter,比如:
ByteArrayHttpMessageConverter
StringHttpMessageConverter
ResourceHttpMessageConverter
SourceHttpMessageConverter
AllEncompassingFormHttpMessageConverter
还会查看类路径下是否有jackson相关的依赖,如果有也会默认添加:
MappingJackson2HttpMessageConverter
MappingJackson2XmlHttpMessageConverter
等等。。。
ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
接下来详细分析RequestResponseBodyMethodProcessor的实现方式我们只需要从resolveArgument方法开始,之前的寻找参数解析器的流程是一致的:
RequestResponseBodyMethodProcessor中:
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
同样判断如果入参类型是java.util.Optional类型的话获取到真正的类型
parameter = parameter.nestedIfOptional();
使用已有的HttpMessageConverter进行请求体里的数据读取,读取的方式就是循环匹配,查看哪一个HttpMessageConverter支持当前入参类型的读取,
如果有能够读取的HttpMessageConverter那就调用其read 方法得到入参类的value值返回
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
获取入参参数的名称
String name = Conventions.getVariableNameForParameter(parameter);
再创建一个WebDataBinder实例,为什么会创建WebDataBinder实例呢?是不是很奇怪,那是因为SpringMVC吧参数检验的校验器封装到了WebDataBinder实例中,
因此还需要创建一个WebDataBinder实例来进行入参校验,所以才创建了一个WebDataBinder实例。
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
使用WebDataBinder实例进行入参检验
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
把校验结果添加到ModelAndViewContainer实例中
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
返回适配结果,这里如果入参是java.util.optional的话会给真正的入参赋值。
return adaptArgumentIfNecessary(arg, parameter);
}
readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()):
parameter.getNestedGenericParameterType()这个值就是入参的参数类型:
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
获取到被包装过的HttpServletRequest请求实例
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
使用上面获取到的HttpServletRequest请求实例来构建一个ServletServerHttpRequest实例,
创建一个ServletServerHttpRequest的作用是,ServletServerHttpRequest类提供了获取请求体body
的功能,返回的是一个InputStream 输入流。
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
正式使用MessageConverters列表进行请求体处理
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
if (arg == null) {
if (checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getMethod().toGenericString());
}
}
return arg;
}
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
MediaType contentType;
boolean noContentType = false;
try {
从请求头中获取到内容的类型,如果获取出错就抛出HttpMediaTypeNotSupportedException异常,也就是媒体类型不支持。
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
如果没有获取到contentType,那么默认就设置为application/octet-stream 类型
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
获取到上下文的Class类型,其实就是获取Controller类的类型
Class<?> contextClass = (parameter != null ? parameter.getContainingClass() : null);
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = (parameter != null ?
ResolvableType.forMethodParameter(parameter) : ResolvableType.forType(targetType));
targetClass = (Class<T>) resolvableType.resolve();
}
获取请求方式如GET、POST等请求方式
HttpMethod httpMethod = ((HttpRequest) inputMessage).getMethod();
先赋值一个body变量=new Object();
Object body = NO_VALUE;
try {
使用HttpInputMessage实例来构建一个EmptyBodyCheckingHttpInputMessage实例,其实就是检查是否是空数据。
inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);
循环HeepMessageConverter列表,找到能够处理当前入参类型的
messageConverter来进行数据的读取与类型转换。具体的api可以查看
HttpMessageConverter<T>接口
我们常用的MappingJackson2HttpMessageConverter、
MappingJackson2XmlHttpMessageConverter就是在这里工作的。
for (HttpMessageConverter<?> converter : this.messageConverters) {
获取到当前循环的HttpMessageConverter的类型
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
if (converter instanceof GenericHttpMessageConverter) {
如果当前循环的HttpMessageConverter的类型是GenericHttpMessageConverter类型那就强转为GenericHttpMessageConverter类型也就是通用的处理器
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
然后使用强转后的GenericHttpMessageConverter类型进行可读性判断。
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
如果当前循环GenericHttpMessageConverter类型可读当前的请求体,且请求体不为空,那就进行读取。
if (inputMessage.getBody() != null) {
读取之前先获取到通知,这里的通知类就是在RequestMappingHandlerAdapter初始化的时候会初始化这些通知类,规则就是在容器中
查找被注解@ControllerAdvice标注的且类型是RequestBodyAdvice 或者 ResponseBodyAdvice 类型的通知。
如果有RequestBodyAdvice 的话就按顺序调用其beforeBodyRead方法进行读前处理。
inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
使用genericConverter来读取body并转化为入参的目标类型 得到一个入参的value值。
body = genericConverter.read(targetType, contextClass, inputMessage);
如果有RequestBodyAdvice 的话就按顺序调用其afterBodyRead方法进行读后处理
body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
else {
如果body是空的,那就使用找到的RequestBodyAdvice进行handleEmptyBody 空body处理。
body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
}
break;
}
}
如果当前循环的HttpMessageConverter不是通用的处理器,比如ByteArrayHttpMessageConverter就只支持入参类型是字节数组byte[]
else if (targetClass != null) {
如果不是通用的处理器,那就判断当前处理器是否支持当前的入参类型
if (converter.canRead(targetClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
如果支持且body不为空那就使用当前处理器进行body的读取与类型转换。
if (inputMessage.getBody() != null) {
inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
}
break;
}
}
}
}
catch (IOException ex) {
如果在寻找处理器或者使用处理器进行body读取以及类型转换的过程中出错的话就直接抛出HttpMessageNotReadableException异常。
throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
}
如果经过以上的操作还是没有正确的进行body读取以及类型转换的话
if (body == NO_VALUE) {
那就判断请求方法类型是否为空 或者请求的方式是不支持的 或者 contentType+body是空的话 那就直接返回null 表示当前的入参值是null
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && inputMessage.getBody() == null)) {
return null;
}
否则抛出异常HttpMediaTypeNotSupportedException 媒体类型不支持
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}
如果读取以及参数类型转换正确那就返回转换好的入参value值。
return body;
}
这就是整个RequestResponseBodyMethodProcessor参数解析器的工作原理。
参数解析器分析完成,我们接着第7步分析,参数解析完成后我们得到了参数列表,接下来正式调用HandlerMethod 也就是Controller中的方法,也就是这一步Object returnValue = doInvoke(args):
InvocableHandlerMethod中:
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
获取到泛型操作安全的桥接方法,也就是真正的Controller中的方法,只是说进行了泛型安全桥接。
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(getInvocationErrorMessage(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
String text = getInvocationErrorMessage("Failed to invoke handler method", args);
throw new IllegalStateException(text, targetException);
}
}
}
真正的Controller中的方法执行完成后会进行返回值处理,此时之前配置的返回值处理器就排上了用场,至于这一块的细节实现,自己去看吧,比较简单。
第8步: 返回ModelAndView。
return getModelAndView(mavContainer, modelFactory, webRequest);
在RequestMappingHandlerMapping中:
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
这一步就是把之前执行的被注解@ModelAttribute注解标注的方法的返回值去绑定到sessionAttributesHandler去进行存储,
当然前提是你使用了@SesssionAttribute注解进行model存储在session中前提是你使用了@SesssionAttribute注解进行model存储在session中
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
从ModelAndViewContainer实例中获取到model
ModelMap model = mavContainer.getModel();
创建一个ModelAndView实例
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
如果model是重定向参数那就特殊操作。
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
这就是RequestMappingHandlerAdapter的整体工作原理,我们对核心部分进行了源码剖析,整体还是比较复杂的,我们掌握好核心的原理就好,至于很多小细节处理不需要过度关心。