一崇棠、前言
在前面幾篇文章分析了請求轉(zhuǎn)發(fā)、Controller查找及攔截器的加載等信息诽嘉,那么當(dāng)帶有參數(shù)的請求發(fā)送到服務(wù)端侠姑,SpringMVC又是怎樣把請求參數(shù)创橄,分析轉(zhuǎn)換后傳入到對應(yīng)的方法中的呢?本篇文章主要分析請求參數(shù)的解析莽红、類型轉(zhuǎn)換及數(shù)據(jù)的綁定妥畏。
二、請求執(zhí)行者適配器
再次分析DispatcherServlet 中的 doDispatch方法發(fā)現(xiàn)安吁,在獲取到 Handler后會再次跟進Handler的找到執(zhí)行此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);
}
說明:
此處主要關(guān)心 supports方法鬼店,此方法是返回此適配器是否支持執(zhí)行對應(yīng)handler网棍。上文分析的所有的 Controller 解析成的Handler是通過適配器 RequestMappingHandlerAdapter 處理的,接下來將分析 次實現(xiàn)了妇智。
2滥玷、適配器 RequestMappingHandlerAdapter 繼承關(guān)系
?
3氏身、初始化
RequestMappingHandlerAdapter實現(xiàn)了 InitializingBean 接口,屬性Spring 接口的都知道惑畴,此接口只有一個方法 afterPropertiesSet(),是在類初始化完成后調(diào)用的方法蛋欣。那么接下來咱們看看此類中afterPropertiesSet() 方法的具體實現(xiàn)。
public void afterPropertiesSet() {
// 初始化 加了注解 @ControllerAdvice 的類的屬性信息
initControllerAdviceCache();
//主要實現(xiàn)三個變量 argumentResolvers initBinderArgumentResolvers returnValueHandlers
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
接著再看看 initControllerAdviceCace 方法的具體實現(xiàn)
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
// 在Spring容器中獲取所有加了注解 @ControllerAdvice 的類
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
//在當(dāng)前類中獲取所有添加了注解 @ModelAttribute 且沒有添加 注解@RequestMapping的方法桨菜,并且添加到緩存中豁状。
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
////在當(dāng)前類中獲取所有添加了注解 @InitBinder的方法,并且添加到緩存中倒得。
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
//判斷是否實現(xiàn)了 接口 RequestBodyAdvice
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
}
4、handle 處理方法
RequestMappingHandlerAdapter類的handle方法是在其父類中實現(xiàn)的夭禽,但在父類中只是調(diào)用了模板方法 handleInternal 霞掺,handleInternal方法又有具體的子類進行實現(xiàn),那么接下來在分析一下 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調(diào)用
}
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 此方法較為復(fù)雜,接下來主要分析一下此方法潮梯。
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創(chuàng)建了ServletWebRequest類型的webRequest,在ArgumentResolver解析參數(shù)時使用的request就是這個webRequest秉馏,當(dāng)然如果我們的處理器需要HttpServletRequest類型的參數(shù)耙旦,ArgumentResolver會給我們設(shè)置原始的request。
2)萝究、接著對WebDataBinderFactory免都、ModelFactory、ServletInvocableHandlerMethod這三個類型的變量進行了定義和初始化帆竹,下面先分別介紹一下這三個變量绕娘。
對三個變量的解析:
- WebDataBinderFactory 的作用從名字就可以看出是用來創(chuàng)建WebDataBinder的,WebDataBinder用于參數(shù)綁定栽连,主要功能就是實現(xiàn)參數(shù)跟String之間的類型轉(zhuǎn)換险领,ArgumentResolver在進行參數(shù)解析的過程中會用到WebDataBinder,另外ModelFactory在更新Model時也會用到它秒紧。
- ModelFactory是用來處理Model的绢陌,主要包含兩個功能:①在處理器具體處理之前對Model進行初始化;②在處理完請求后對Model參數(shù)進行更新噩茄。
- ServletInvocableHandlerMethod 類型非常重要下面,它繼承自HandlerMethod,并且可以直接執(zhí)行绩聘。實際請求的處理就是通過它來執(zhí)行的沥割,參數(shù)綁定耗啦、處理請求以及返回值處理都在它里邊完成。