在前面几篇文章分析了请求转发、Controller查找及拦截器的加载等信息,那么当带有参数的请求发送到服务端,SpringMVC又是怎样把请求参数,分析转换后传入到对应的方法中的呢?本篇文章主要分析请求参数的解析、类型转换及数据的绑定。
再次分析DispatcherServlet 中的 doDispatch方法发现,在获取到 Handler后会再次跟进Handler的找到执行此handler的适配器。如所示:
1、查看HandlerAdapter接口的方法列表
public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
说明:
此处主要关心 supports方法,此方法是返回此适配器是否支持执行对应handler。上文分析的所有的 Controller 解析成的Handler是通过适配器 RequestMappingHandlerAdapter 处理的,接下来将分析 次实现了。
2、适配器 RequestMappingHandlerAdapter 继承关系
3、初始化
RequestMappingHandlerAdapter实现了 InitializingBean 接口,属性Spring 接口的都知道,此接口只有一个方法 afterPropertiesSet(),是在类初始化完成后调用的方法。那么接下来咱们看看此类中afterPropertiesSet() 方法的具体实现。
public void afterPropertiesSet() {
// 初始化 加了注解 @ControllerAdvice 的类的属性信息
initControllerAdviceCache();
//主要实现三个变量 argumentResolvers initBinderArgumentResolvers returnValueHandlers
if (this.argumentResolvers == null) {
List resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
接着再看看 initControllerAdviceCace 方法的具体实现
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
// 在Spring容器中获取所有加了注解 @ControllerAdvice 的类
List adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
List
4、handle 处理方法
RequestMappingHandlerAdapter类的handle方法是在其父类中实现的,但在父类中只是调用了模板方法 handleInternal ,handleInternal方法又有具体的子类进行实现,那么接下来在分析一下 handleInternal 方法。
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
//检测方法给定的Request是否支持 且是否要求 session。
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
// 省略 同步Session调用
}
else {
// No synchronization on session demanded at all...
//主要方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
此种的重点方法为 invokeHandlerMethod 此方法较为复杂,接下来主要分析一下此方法。
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);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
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();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
此方法的功能分析:
1)、首先使用request和response创建了ServletWebRequest类型的webRequest,在ArgumentResolver解析参数时使用的request就是这个webRequest,当然如果我们的处理器需要HttpServletRequest类型的参数,ArgumentResolver会给我们设置原始的request。
2)、接着对WebDataBinderFactory、ModelFactory、ServletInvocableHandlerMethod这三个类型的变量进行了定义和初始化,下面先分别介绍一下这三个变量。
对三个变量的解析:
- WebDataBinderFactory 的作用从名字就可以看出是用来创建WebDataBinder的,WebDataBinder用于参数绑定,主要功能就是实现参数跟String之间的类型转换,ArgumentResolver在进行参数解析的过程中会用到WebDataBinder,另外ModelFactory在更新Model时也会用到它。
- ModelFactory是用来处理Model的,主要包含两个功能:①在处理器具体处理之前对Model进行初始化;②在处理完请求后对Model参数进行更新。
- ServletInvocableHandlerMethod 类型非常重要,它继承自HandlerMethod,并且可以直接执行。实际请求的处理就是通过它来执行的,参数绑定、处理请求以及返回值处理都在它里边完成。