1. handleInternal方法简析
上一节分析了SpringMVC获取handler及HandlerAdapter的过程,接下来就要真正开始处理Controller了。我们以AbstractHandlerMethodAdapter为例来来分析一下其具体的处理过程。
在此过程中会包含SpringMVC流程处理的的关键部分。例如参数获取及解析、异步处理、调用Controller中的方法、返回视图等等。。。
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response,
HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 1.检测当前请求,验证请求方法合法性和session合法性
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 2.根据synchronizeOnSession值判断,当前请求是否需串行化访问。
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
// 获取最佳互斥锁,即同步当前回话对象;如未能获取到互斥锁,将返回HttpSession对象本身
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
// 即无最佳互斥锁,也未能获取到HttpSession,则当前回话无需串行化访问
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// 3.相应信息不包含Cache-Control
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
这里会涉及到一部分异步操作的代码,留在后面介绍。具体的处理方法委托给了invokeHandlerMethod方法。
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//WebDataBinderFactory --> 工厂类,为目标对象创建一个WebDataBinder实例
// 1.WebDataBinder继承了DataBinder类,为web请求提供了参数绑定服务
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取ModelFactory:
// 2.ModelFactory可以协助控制器在调用方法之前初始化模型,并在调用之后更新模型
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 创建ServletInvocableHandlerMethod对象
// 3.ServletInvocableHandlerMethod继承并扩展了InvocableHandlerMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 4.尝试绑定参数、返回值解析器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 5.创建ModelAndViewContainer,并初始化Model对象
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("Resume with async result ["
+ (result instanceof CharSequence ? "\"" + result + "\"" : result) + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 7.调用Controller中的具体方法并处理返回值
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 8.返回ModelAndView对象
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
// 完成请求后续处理,并将当前请求置为未激活
webRequest.requestCompleted();
}
}
invokeHandlerMethod方法还是很复杂的,下面我们对该方法进行详细的分析。。。
2.getDataBinderFactory方法分析
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
// 1.获取handlerType,即目标类
Class<?> handlerType = handlerMethod.getBeanType();
// 2.优先尝试从缓存中获取对应的InitBinder方法
Set<Method> methods = this.initBinderCache.get(handlerType);
// 如未能从缓存中获取,则根据handlerType对应的类,去类中查找所有标注了@InitBinder注解的方法,并将其缓存
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
// 3.从标注了@ControllerAdvice类中寻找InitBinder方法,并优先为其创建InvocableHandlerMethod对象
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// Global methods first
this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
// 4.为普通的InitBinder创建InvocableHandlerMethod对象
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
// 5.创建InitBinderDataBinderFactory对象
return createDataBinderFactory(initBinderMethods);
}
该方法的处理流程很简单,但是这里涉及到两个点,普通的@InitBinder注解和标注了@ControllerAdvice注解类中的@InitBinder注解。从代码中可以看到,@ControllerAdvice注解中的InitBinder方法,是作为全局方法优先创建的。而普通的InitBinder方法只能应用于其类本身,无法作为全局对象应用到所有的Controller方法。
3.getModelFactory方法
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
// 1.处理@SessionAttributes注解
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
// 2.处理@ModelAttribute注解
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
// Global methods first
// 3.优先处理全局@ModelAttribute注解的方法,例如被@ControllerAdvice标注的类中存在被@ModelAttribute注解的方法,则优先处理
this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> {
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
// 4.循环所有标注了@ModelAttribute注解的方法,并创建InvocableHandlerMethod对象
// InvocableHandlerMethod:负责具体的HandlerMethod的调用、参数解析等工作
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
// 5.返回ModelFactory对象
// ModelFactory:协助在控制器方法调用之前初始化模型,并在调用之后更新它。
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
该方法主要作用是处理@ModelAttribute和@SessionAttributes两个注解。关于两者的作用,不多赘述了!在最后创建了ModelFactory对象,该对象的作用可以协助在控制器方法调用之前初始化模型,并在调用之后更新它。后面还会有具体的介绍。
4.ModelFactory的initModel初始化
上一步创建了ModelFactory对象实例,接下来看其initModel具体都做了什么工作:
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
// 1.解析并合并@SessionAttributes注解
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
container.mergeAttributes(sessionAttributes);
// 2.调用被@ModelAttribute注解的方法
invokeModelAttributeMethods(request, container);
// 3.查找标注了@ModelAttribute、@SessionAttributes的方法参数,确保其解析过程中不会发生异常
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!container.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
container.addAttribute(name, value);
}
}
}
注意这里会有一个Expected session attribute xxx
的异常,如果类上标注了@SessionAttributes注解,且在方法中标注了@ModelAttribute注解,如果@ModelAttribute为空,则会抛出此异常。。。
异步处理相关的内容留在后面的章节再介绍。流程介绍到这里,还剩下关键的参数处理、Controller具体方法调用、视图解析返回等工作,篇幅有限,留在下一章介绍。