承接上文,Spring MVC通过HandlerMapping
定位到了具体的HandlerExecutionChain
,也就是具体要执行的方法.本篇详细阐述Spring MVC执行具体方法的流程.
HandlerAdapter HandlerAdapter
用来适配HandlerExecutionChain
的一个接口,从名称来看这里是适配器模式 ,适配器模式的本质在于包装转换 ,对于HandlerExecutionChain
中不同的Handler提供适配功能,并且提供统一的调用方法,该接口主要有以下方法:
1 2 3 4 5 6 7 8 9 10 11 public interface HandlerAdapter { boolean supports (Object handler) ; ModelAndView handle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; ..... }
在DispatcherServlet
中定位到具体的HandlerAdapter
是类似HandlerMappings
的处理,直接循环,判断是否支持处理,支持则返回,这类似一种标准责任链模式,查找出链中能够处理该事件的第一个节点类.
1 2 3 4 5 6 7 8 9 10 protected HandlerAdapter getHandlerAdapter (Object handler) throws ServletException { for (HandlerAdapter ha : this .handlerAdapters) { 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" ); }
HandlerAdapter
的继承体系如下所示:
SimpleControllerHandlerAdapter
处理实现了Controller
接口的handler.
SimpleServletHandlerAdapter
处理实现了Servlet
接口的handler.
HttpRequestHandlerAdapter
处理实现了HttpRequestHandler
接口的handler.静态资源映射就是使用该Adapter进行处理.
AbstractHandlerMethodAdapter
处理HandlerMethod
的子类,也就是我们的业务方法,也是本次分析的重点.
模板类AbstractHandlerMethodAdapter 该模板类比较简单,主要声明了所支持处理的类型,然后把请求转到handleInternal
方法中.
1 2 3 4 5 6 public final boolean supports (Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); } protected abstract ModelAndView handleInternal (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
实现类RequestMappingHandlerAdapter RequestMappingHandlerAdapter
在作为实现类,主要是负责调用逻辑,如下所示,主要的调用逻辑在invokeHandlerMethod
方法中.
1 2 3 4 5 6 7 8 protected ModelAndView handleInternal (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ... mav = invokeHandlerMethod(request, response, handlerMethod); ... return mav; }
invokeHandlerMethod
方法,顾名思义是负责调用的实现,其会把HandlerMethod
方法封装到ServletInvocableHandlerMethod
中,然后准备上下文环境,比如参数解析器HandlerMethodArgumentResolver
,参数转换器DataBinder
等信息,一切准备好了之后完成调用,在对结果进行解析包装.
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 protected ModelAndView invokeHandlerMethod (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest (request, response); try { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this .argumentResolvers); invocableMethod.setHandlerMethodReturnValueHandlers(this .returnValueHandlers); invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this .parameterNameDiscoverer); ..... invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null ; } return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
上述流程可以确定以下几点:
参数解析依赖HandlerMethodArgumentResolver
返回值解析依赖HandlerMethodReturnValueHandler
参数转换与绑定(针对Bean)依赖WebDataBinderFactory
参数如何解析? 在org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
中定义了参数的解析流程.
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 private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object...providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object [parameters.length]; for (int i = 0 ; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this .parameterNameDiscoverer); args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null ) { continue ; } if (this .argumentResolvers.supportsParameter(parameter)) { try { args[i] = this .argumentResolvers.resolveArgument( parameter, mavContainer, request, this .dataBinderFactory); continue ; } catch (Exception ex) { throw ex; } } ... } return args; }
其中HandlerMethodArgumentResolver
是负责参数解析的入口,其本身是组合设计模式中的Component
接口类,HandlerMethodArgumentResolverComposite
是组合模式中的Composite
树枝节点类.而众多解析方法则是Leaf
叶子类,组合模式的本质是为了组合多个过多的节点,统一叶子节点和组合节点 ,给客户端提供统一的访问形式在使用时不需要做区分,通过Composite
类把众多解析操作组合一起.
HandlerMethodArgumentResolver
接口定义如下,其中MethodParameter
是对用户业务方法参数的封装,比如参数类型,所属Bean,修饰的Annotation
等.
1 2 3 4 5 6 7 8 9 10 11 public interface HandlerMethodArgumentResolver { boolean supportsParameter (MethodParameter parameter) ; Object resolveArgument (MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;}
其中AbstractNamedValueMethodArgumentResolver
作为其中之一的模板类定义了那些需要根据名称解析参数方式的一些模板,比如@RequestParam
,@RequestHeader
等注解,大概流程如下,把具体的获取过程利用抽象方法resolveName()
延迟到了子类实现,让子类专注于从相应区域获取到对应的参数,拿到参数后使用WebDataBinder
对参数进行类型转换,下面是该模板类的解析流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public final Object resolveArgument (MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { ... Object resolvedName = resolveStringValue(namedValueInfo.name); Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest); ... if (binderFactory != null ) { WebDataBinder binder = binderFactory.createBinder(webRequest, null , namedValueInfo.name); try { arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter); } ... } handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg; }
这个是参数的大概解析流程,同理从playload中解析的大概也是如此设计,最底层的子类只负责从相应区域获取参数,上层的模板类负责参数的统一处理转换操作.
参数如何类型转换以及绑定? 在一般的写法中,经常会使用一个对象来接收参数,Spring MVC会自动把参数设置到该参数对象对应的属性上,这个是怎么实现的呢?答案是ModelAttributeMethodProcessor
参数解析器,解析流程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public final Object resolveArgument (MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { String name = ModelFactory.getNameForParameter(parameter); Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest)); ... WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); if (binder.getTarget() != null ) { if (!mavContainer.isBindingDisabled(name)) { bindRequestParameters(binder, webRequest); } validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new BindException (binder.getBindingResult()); } } return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter); }
其中参数的绑定和转换都依赖WebDataBinder
这个类,相比DataBinder
该类额外提供了从request中取出参数的能力,其绑定功能依赖BeanWrapper
,转换功能依赖ConversionService
.
ConversionService
是Spring3引进的类型转换系统,在Spring中想添加一个转换器有如下几种做法
实现interface Converter<S, T>
接口,负责把S转换为T
实现interface ConverterFactory<S, R>
接口,其负责把一类S转换为另一类R对象,与上面不同的是该方法是一个工厂类,其负责生产一类转换,比如String到Number,String到Integer等.
实现GenericConverter
接口,是类型转换中最复杂最强大的存在,可以实现根据上下文信息转换,支持一个源或者多个源到目标的转换.
自定义转换器 举个例子,实现字符到枚举类的转换:
1 2 3 4 5 6 7 public class StringToEnumConvert implements ConverterFactory <String,Enum> { @Override @SuppressWarnings("unchecked") public <T extends Enum > Converter<String, T> getConverter (Class<T> aClass) { return s -> (T)Enum.valueOf(aClass, s.trim()); } }
然后再自定义配置中加入该转换器,参数解析是对于字符串到枚举类会自动利用该转换器进行转换处理.
1 2 3 4 5 6 7 8 9 @Configuration public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addFormatters (FormatterRegistry registry) { registry.addConverterFactory(new StringToEnumConvert ()); } }
方法如何执行? 解决了参数后直接利用反射执行.
1 2 3 4 5 6 7 8 protected Object doInvoke (Object... args) throws Exception { ReflectionUtils.makeAccessible(getBridgedMethod()); try { return getBridgedMethod().invoke(getBean(), args); } .... }
参考 https://www.jianshu.com/p/d64800baaa04