java源码 - SpringMVC(5)之 HandlerAdapter
HandlerMapping通过request找到了Handler,HandlerAdapter是具体使用Handler来干活的,每个HandlerAdapter封装了一种Handler的具体使用方法。
有时候注解翻译过来就不说人话了。
RequestMapping-HandlerAdapter 十分难。
文章目录
- 1. 依赖关系图总概
- 2. 直接调用的Adapter
- 3. RequestMappingHandlerAdapter
- 3.1 父类AbstractHandlerMethodAdapter
- 3.2 RequestMappingHandlerAdapter概述
- 3.3 RequestMappingHandlerAdapter结构
- 3.4 ModelAndViewContainer
- 3.5 SessionAttributesHandler和SessionAttributeStore
- 3.6 ModelFactory
- 3.7 ServletInvocableHandlerMethod
- 3.8 HandlerMethodArgumentResolver
- 3.9 HandlerMethodReturnValueHandler
1. 依赖关系图总概
总体起来说:
RequestMappingHandlerAdapter所处理的Handler可以是任意的方法,没有任何约束,所以最难,重点也就在这个类。
HandlerFunctionAdapter为5.x新加入的类。
/ * *
* MVC框架SPI,允许参数化核心MVC工作流。
*
* <p必须为每个处理程序类型实现的>接口来处理请求。
*此接口用于允许{@link DispatcherServlet}无限期地运行
*可扩展的。{@code DispatcherServlet}通过此方法访问所有安装的处理程序
*这个接口,这意味着它不包含特定于任何处理器类型的代码。
*
注意处理程序的类型可以是{@code Object}。这是为了
*不需要集成其他框架的处理程序
*自定义编码,以及允许注解驱动的处理程序对象
*不要遵从任何特定的Java接口。
*
该接口不适合应用程序开发人员使用。它是可用的
*给那些想要开发自己的web工作流的处理者。
*
*注意:{@code HandlerAdapter}实现者可以实现{@link
* org.springframework.core。接口能够指定排序
*由{@code DispatcherServlet}应用的顺序(以及优先级)。
*未排序的实例被视为最低优先级。
*
* @see org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
* @see org.springframework.web.servlet.handler.SimpleServletHandlerAdapter
*/
public interface HandlerAdapter {
/ * *
*给定一个处理程序实例,返回是否使用这个{@code HandlerAdapter}
*可以支持它。典型的handleradapter将基于处理程序进行决策
*类型。handleradapter通常只支持每种处理器类型。
*
一个典型实现:
* < p > {@code
* return (handler instanceof MyHandler);
*}
*要检查的@param handler handler对象
* @return该对象是否可以使用给定的处理程序
* /
boolean supports(Object handler);
/ * *
*使用给定的处理程序来处理此请求。
*所需的工作流程可能有很大的不同。
* @param请求当前HTTP请求
* @param响应当前HTTP响应
* @param处理器要使用的处理器。此对象必须以前已被传递
*到该接口的{@code supports}方法,该方法必须具有
*返回{@code true}。
* @抛出错误时的异常
返回一个ModelAndView对象,其中包含视图的名称和所需的参数
*模型数据,或{@code null},如果请求已被直接处理
* /
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/ * *
*与HttpServlet的{@code getLastModified}方法相同的契约。
如果处理程序类不支持,*可以简单地返回-1。
* @param请求当前HTTP请求
* @param处理器要使用的处理器
返回给定处理程序的lastModified值
* @see javax.servlet.http.HttpServlet # getLastModified
* @see org.springframework.web.servlet.mvc.LastModified # getLastModified
*/
long getLastModified(HttpServletRequest request, Object handler);
}
2. 直接调用的Adapter
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
org.springframework.web.servlet.handler.SimpleServletHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof Servlet);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((Servlet) handler).service(request, response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
这三个都是直接调用实在是没什么技术含量。
3. RequestMappingHandlerAdapter
3.1 父类AbstractHandlerMethodAdapter
先来看其父类,非常简单,是三个模板方法:
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
protected abstract boolean supportsInternal(HandlerMethod handlerMethod);
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
@Override
public final long getLastModified(HttpServletRequest request, Object handler) {
return getLastModifiedInternal(request, (HandlerMethod) handler);
}
protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);
还实现了另外实现了Order接口,可以在配置时设置顺序:
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
3.2 RequestMappingHandlerAdapter概述
非常难的部分来了
先来看其父类模板方法部分的实现:
/ * *
*始终返回{@code true},因为任何方法参数和返回值
类型将以某种方式处理。无法识别的方法参数
*被任何HandlerMethodArgumentResolver解释为一个请求参数
*如果它是一个简单类型,或作为模型属性,否则。返回值
*没有被任何HandlerMethodReturnValueHandler识别将被解释
*作为模型属性。
* /
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
supportsInternal直接返回true,也就是不增加判断Handler的条件,只需要满足父类中的HandlerMethod类型的要求就可以了;
/ * *
这个实现总是返回-1。一个{@code @RequestMapping}方法可以
*计算lastModified值,调用{@link WebRequest#checkNotModified(long)},
*,如果调用的结果是{@code true},则返回{@code null}。
* /
@Override
protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
return -1;
}
最重要的就是handleInternal方法,就是这个方法实际使用Handler处理请求。具体处理过程大致可以分为三步:
1)备好处理器所需要的参数。
2)使用处理器处理请求。
3)处理返回值,也就是将不同类型的返回值统一处理成ModelAndView类型。
这三步里面第2步是最简单的,直接使用反射技术调用处理器执行就可以了,第3步也还算简单,最麻烦的是第1步,也就是参数的准备工作,这一步根据处理器的需要设置参数,而参数的类型、个数都是不确定的,所以难度非常大,另外这个过程中使用了大量的组件,这也是这一步的代码不容易理解的重要原因之一。
要想理解参数的绑定需要先想明白三个问题:
- 都有哪些参数需要绑定。
- 参数的值的来源。
- 具体进行绑定的方法。
参数来源有6个
:
- request中相关的参数,主要包括url中的参数、post过来的参数以及请求头所包含的值。
- cookie中的参数。
- session中的参数。
- 设置到FlashMap中的参数,这种参数主要用于redirect的参数传递。
- SessionAttributes传递的参数,这类参数通过@SessionAttributes注释传递,后面会详细讲解。
- 通过相应的注释了@ModelAttribute的方法进行设置的参数。
3.3 RequestMappingHandlerAdapter结构
3.3.1 初始化
该类实现了InitializingBean接口:用于初始化一些组件
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null) {
//argumentResolvers:用于给处理器方法和注释了@ModelAttribute的方法设置参数。
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
//initBinderArgumentResolvers:用于给注释了@initBinder的方法设置参数。
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
//returnValueHandlers:用于将处理器的返回值处理成ModelAndView的类型。
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
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);
}
//查找注释了@ModelAttribute的方法
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
//缓存@ControllerAdvice注释的类里面注释了@ModelAttribute和@InitBinder的方法
//也就是全局的@ModelAttribute和@InitBinder方法。
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
//查找注释了@initBinder的方法
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
////保存实现了ResponseBodyAdvice接口、可以修改返回的ResponseBody的类。
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
if (logger.isDebugEnabled()) {
int modelSize = this.modelAttributeAdviceCache.size();
int binderSize = this.initBinderAdviceCache.size();
int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);
int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);
if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {
logger.debug("ControllerAdvice beans: none");
}
else {
logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +
" @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");
}
}
}
@ModelAttribute和@InitBinder一般用于ControllerAdvice。
每个处理器自己的@ModelAttribute和@InitBinder方法是在第一次使用处理器处理请求时缓存起来的,这种做法既不需要启动时就花时间遍历每个Controller查找@ModelAttribute和@InitBinder方法,又能在调用过一次后再调用相同处理器处理请求时不需要再次查找而从缓存中获取。这两种缓存的思路类似于单例模式中的饿汉式和懒汉式。
再来看看三个getDefaultXXX方法:
/ * *
*返回要使用的参数解析器列表,包括内置解析器
*和通过{@link #setCustomArgumentResolvers}提供的自定义解析器。
* /
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
//基于注释的参数解析
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
//基于类型的参数解析
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
//自定义参数
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
//万能
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
//另外两个类似就不贴了。
通过注释可以看到,这里的解析器可以分为四类:通过注释解析的解析器、通过类型解析的解析器、自定义的解析器和可以解析所有类型的解析器。第三类是可以自己定义的解析器,定义方法是自己按要求写个resolver然后通过customArgumentResolvers属性注册到RequestMappingHandlerAdapter。需要注意的是,自定义的解析器是在前两种类型的解析器都无法解析的时候才会使用到,这个顺序无法改变!所以如果要想自己写一个解析器来解析@PathVariable注释的PathVariable类型的参数,是无法实现的,即使写出来并注册到RequestMappingHandlerAdapter上面也不会被调用。Spring MVC自己定义的解析器的顺序也是固定的,不可以改变。
3.3.2 使用
如上面所说的,使用的方法为父类的模板方法:handleInternal()
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
//如果需要,在同步块中执行invokeHandlerMethod。
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
//不需要互斥锁
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 会话上根本不需要同步…
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
首先看checkRequest():org/springframework/web/servlet/support/WebContentGenerator.java
/ * *
*检查给定的请求是否支持方法和需要的会话,如果有的话。
* @param请求当前HTTP请求
* @抛出ServletException,如果请求不能处理,因为检查失败
* /
protected final void checkRequest(HttpServletRequest request) throws ServletException {
//检查是否应该支持request方法。
String method = request.getMethod();
if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
}
//检查是否需要会话。
if (this.requireSession && request.getSession(false) == null) {
throw new HttpSessionRequiredException("Pre-existing session required but none found");
}
}
supported-Methods属性用来保存所有支持的request类型,如果为空则不检查,否则用它检查是否支持当前请求的类型,如果不支持则抛出异常。supportedMethods默认为空,可以在注册RequestMappingHandlerAdapter的时候对其进行设置,而且如果在构造方法中给restrict-DefaultSupportedMethods传入true,supportedMethods会默认设置Get、Head、Post三个值,也就是只支持这三种类型request请求
/ * *
创建一个新的WebContentGenerator。
* @param restrictDefaultSupportedMethods {@code true
*生成器默认支持HTTP方法GET, HEAD和POST,
*或{@code false}(如果不受限制)
* /
public WebContentGenerator(boolean restrictDefaultSupportedMethods) {
if (restrictDefaultSupportedMethods) {
this.supportedMethods = new LinkedHashSet<>(4);
this.supportedMethods.add(METHOD_GET);
this.supportedMethods.add(METHOD_HEAD);
this.supportedMethods.add(METHOD_POST);
}
initAllowHeader();
}
其次如果requireSession为true,则通过request.getSession(false)检查session是否存在,如果不存在则抛出异常,不过requireSession的默认值为false,所以默认也不检查。
接着看:invokeHandlerMethod()
/ * *
*调用{@link RequestMapping}处理程序方法准备{@link ModelAndView}
*如果需要查看分辨率。
* @see # createInvocableHandlerMethod (HandlerMethod)
* /
@Nullable
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();
}
}
在invokeHandleMethod方法中首先使用request和response创建了ServletWebRequest类型的webRequest,在ArgumentResolver解析参数时使用的request就是这个webRequest,当然如果我们的处理器需要HttpServletRequest类型的参数,ArgumentResolver会给我们设置原始的request。
接着对WebDataBinderFactory、ModelFactory、ServletInvocableHandlerMethod这三个类型的变量进行了定义和初始化,下面先分别介绍一下这三个变量。
- WebDataBinderFactory
WebDataBinderFactory的作用从名字就可以看出是用来创建WebDataBinder的,WebDataBinder用于参数绑定,主要功能就是实现参数跟String之间的类型转换,ArgumentResolver在进行参数解析的过程中会用到WebDataBinder,另外ModelFactory在更新Model时也会用到它。
WebDataBinderFactory的创建过程就是将符合条件的注释了@InitBinder的方法找出来,并使用它们新建出ServletRequestDataBinderFactory类型的WebDataBinderFactory。这里的InitBinder方法包括两部分:一部分是注释了@ControllerAdvice的并且符合要求的全局处理器里面的InitBinder方法;第二部分就是处理器自身的InitBinder方法,添加的顺序是先添加全局的后添加自身的。第二类InitBinder方法会在第一次调用后保存到缓存中,以后直接从缓存获取就可以了。查找注释了@InitBinder方法的方法和以前一样,使用HandlerMethodSelector.selectMethods来找,而全局的InitBinder方法在创建RequestMappingHandlerAdapter的时候已经设置到缓存中了。
代码如下:
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
Class<?> handlerType = handlerMethod.getBeanType();
//检查当前方法是否已经存入缓存
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
//没有,设置到缓存
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
//保存InitBinder方法的临时变量
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// 全局的方法添加到InitBinderMethods
this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
//将当前Handler中的InitBinder添加到InitBinderMethods
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);
}
- ModelFactory
ModelFactory是用来处理Model的,主要包含两个功能:①在处理器具体处理之前对Model进行初始化;②在处理完请求后对Model参数进行更新。
给Model初始化具体包括三部分内容:
①将原来的SessionAttributes中的值设置到Model;
②执行相应注释了@ModelAttribute的方法并将其值设置到Mode;
③处理器中注释了@ModelAttribute的参数如果同时在SessionAttributes中也配置了,而且在mavContainer中还没有值则从全部SessionAttributes(可能是其他处理器设置的值)中查找出并设置进去。
对Model更新是先对SessionAttributes进行设置,设置规则是如果处理器里调用了SessionStatus#setComplete则将SessionAttributes清空,否则将mavContainer的defaultModel(可以理解为Model,后面ModelAndViewContainer中会详细讲解)中相应的参数设置到SessionAttributes中,然后按需要给Model设置参数对应的BindingResult。
从这里可以看出调用SessionStatus#setComplete清空SessionAttributes是在整个处理执行完以后才执行的,也就是说这条语句在处理器中的位置并不重要,放在处理器的开头或者结尾都不会影响当前处理器对SessionAttributes的使用。
方法如下:
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
//获取SessionAttributesHandler
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
//获取类型
Class<?> handlerType = handlerMethod.getBeanType();
//缓存查询
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<>();
// 全局方法优先
this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
从最后一句新建ModelFactory中可以看出主要使用了三个参数,第一个是注释了@ModelAttribute的方法,第二个是WebDataBinderFactory,第三个是SessionAttributesHandler。
其中WebDataBinderFactory使用的就是上面创建出来的WebDataBinderFactory;Session-AttributesHandler的创建方法getSessionAttributesHandler在前面已经介绍过了;注释了@ModelAttribute的方法分两部分:一部分是注释了@ControllerAdvice的类中定义的全局的@ModelAttribute方法;另一部分是当前处理器自身的@ModelAttribute方法,添加规则是先添加全局的后添加自己的。
- ServletInvocableHandlerMethod
ServletInvocableHandlerMethod类型非常重要,它继承自HandlerMethod,并且可以直接执行。实际请求的处理就是通过它来执行的,参数绑定、处理请求以及返回值处理都在它里边完成。
/ * *
*从给定的{@link HandlerMethod}定义创建一个{@link ServletInvocableHandlerMethod}。
* @param handlerMethod定义
@return对应的{@link ServletInvocableHandlerMethod}(或其自定义子类)
* /
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
return new ServletInvocableHandlerMethod(handlerMethod);
}
org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java
/ * *
*从{@code HandlerMethod}创建一个实例。
* /
public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod);
}
org/springframework/web/method/support/InvocableHandlerMethod.java
/* *
*从{@code HandlerMethod}创建一个实例。
*/
public InvocableHandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod);
}
org/springframework/web/method/HandlerMethod.java
/ * *
复制构造函数在子类中使用。
* /
protected HandlerMethod(HandlerMethod handlerMethod) {
Assert.notNull(handlerMethod, "HandlerMethod is required");
this.bean = handlerMethod.bean;
this.beanFactory = handlerMethod.beanFactory;
this.beanType = handlerMethod.beanType;
this.method = handlerMethod.method;
this.bridgedMethod = handlerMethod.bridgedMethod;
this.parameters = handlerMethod.parameters;
this.responseStatus = handlerMethod.responseStatus;
this.responseStatusReason = handlerMethod.responseStatusReason;
this.description = handlerMethod.description;
this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod;
}
使用handlerMethod新建类ServletInvocableHandlerMethod类,然后将argumentResolvers、returnValueHandlers、binderFactory和parameterNameDiscoverer设置进去就完成了。
这样初始的三个变量的初始化就完成了。
接下来的三步:
①新建传递参数的ModelAndViewContainer容器,并将相应参数设置到其Model中;
②执行请求;
③请求处理完后进行一些后置处理。
- 新建ModelAndViewContainer类型的mavContainer参数,用于保存Model和View,它贯穿于整个处理过程(注意,在处理请求时使用的并不是ModelAndView),然后对mavContainer进行了设置,主要包括三部分内容:①将FlashMap中的数据设置到Model;②使用modelFactory将SessionAttributes和注释了@ModelAttribute的方法的参数设置到Model;③根据配置对ignoreDefaultModelOnRedirect进行了设置,这个参数在分析ModelAndViewContainer的时候再详细讲解。
到这里传递参数的容器就准备好了。设置完mavContainer后又做了一些异步处理的相关的工作
- 执行请求,具体方法是直接调用ServletInvocableHandlerMethod里的invokeAndHandle方法执行的
- 处理完请求后的后置处理,这是在getModelAndView方法中处理的。一共做了三件事:
①调用ModelFactory的updateModel方法更新了Model(包括设置了SessionAttributes和给Model设置BindingResult);
②根据mavContainer创建了ModelAndView;
③如果mavContainer里的model是RedirectAttributes类型,则将其值设置到FlashMap。
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
3.4 ModelAndViewContainer
ModelAndViewContainer承担着整个请求过程中数据的传递工作。
/**
*记录模型和查看由
和在调用的过程中* @link HandlerMethodReturnValueHandlers}一个控制器方法。
{@link #setRequestHandled}标志可以用来指示请求
*已直接处理,不需要查看分辨率。
在实例化时自动创建一个默认的{@link Model}。
*可通过{@link #setRedirectModel}提供另一个模型实例
*用于重定向场景。当设置{@link #setRedirectModelScenario}时
*到{@code true}信号重定向场景,{@link #getModel()}
*返回重定向模型,而不是默认模型。
*/
public class ModelAndViewContainer {
其内字段:
//如果为true则在处理器返回redirect视图时一定不使用defaultModel。
private boolean ignoreDefaultModelOnRedirect = false;
//视图,Object类型的,可以是实际视图也可以是String类型的逻辑视图。
@Nullable
private Object view;
//默认使用的Model。
private final ModelMap defaultModel = new BindingAwareModelMap();
//redirect类型的Model。
@Nullable
private ModelMap redirectModel;
//处理器返回redirect视图的标志。
private boolean redirectModelScenario = false;
@Nullable
private HttpStatus status;
private final Set<String> noBinding = new HashSet<>(4);
private final Set<String> bindingDisabled = new HashSet<>(4);
//用于设置SessionAttribute使用完的标志。
private final SessionStatus sessionStatus = new SimpleSessionStatus();
//请求是否已经处理完成的标志。
private boolean requestHandled = false;
处理器中RedirectAttributes类型的参数ArgumentResolver会传入redirectModel,它实际上是RedirectAttributesModelMap类型。
/ * *
*返回要使用的模型——“默认”或“重定向”模型。
*如果{@code redirectModelScenario=false}或使用默认模型
没有重定向模型(例如,RedirectAttributes没有被声明为
和{@code ignoreDefaultModelOnRedirect=false}。
* /
public ModelMap getModel() {
if (useDefaultModel()) {
return this.defaultModel;
}
else {
if (this.redirectModel == null) {
this.redirectModel = new ModelMap();
}
return this.redirectModel;
}
}
/**
*是否使用默认模型或重定向模型。
*/
private boolean useDefaultModel() {
return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
}
返回defaultModel的情况:①处理器返回的不是redirect视图;②处理器返回的是redirect视图但是redirectModel为null,而且ignoreDefaultModelOnRedirect也是false。
返回redirectModel的情况:①处理器返回redirect视图,并且redirectModel不为null;②处理器返回的是redirect视图,并且ignoreDefaultModelOnRedirect为true。
ignoreDefaultModelOnRedirect可以在RequestMappingHandlerAdapter中设置。判断处理器返回的是不是redirect视图的标志设置在redirectModelScenario中,它是在ReturnValueHandler中设置的,ReturnValueHandler如果判断到是redirect视图就会将redirectModelScenario设置为true。也就是说在ReturnValueHandler处理前ModelAndViewContainer的getModel返回的一定是defaultModel,处理后才可能是redirectModel。
现在再返回去看RequestMappingHandlerAdapter中的getModelAndView方法getModel后判断Model是不是RedirectAttributes类型就清楚是怎么回事了。在getModel返回redirectModel的情况下,在处理器中设置到Model中的参数就不会被使用了(设置SessionAttribute除外)。这样也没有什么危害,因为只有redirect的情况才会返回redirectModel,而这种情况是不需要渲染页面的,所以defaultModel里的参数本来也没什么用。同样,如果返回defaultModel,设置到RedirectAttributes中的参数也将丢弃,也就是说在返回的View不是redirect类型时,即使处理器使用RedirectAttributes参数设置了值也不会传递到下一个请求。
再其他的就是一些属性的CURD方法。
3.5 SessionAttributesHandler和SessionAttributeStore
SessionAttributesHandler与@SessionAttributes注释相对应,用于对Session-Attributes操作,其中包含判断某个参数是否可以被处理以及批量对多个参数进行处理等功能。具体对单个参数的操作是交给SessionAttributeStore去完成的,它的默认实现DefaultSessionAttributeStore使用ServletWebRequest将参数设置到了Session中。Session-AttributesHandler是在ModelFactory中使用的。
3.6 ModelFactory
ModelFactory是用来维护Model的,具体包含两个功能:①初始化Model;②处理器执行后将Model中相应的参数更新到SessionAttributes中。
3.6.1 初始化
/ * *
*按照以下顺序填充模型:
* < ol >
*
检索列出为{@code @SessionAttributes}的“已知”会话属性。
调用{@code @ModelAttribute}方法
*
查找{@code @ModelAttribute}方法参数也被列为
* {@code @SessionAttributes},并确保它们出现在模型生成中
*必要时例外。
* < / ol >
* @param请求当前请求
一个带有要初始化模型的容器
为模型初始化的方法
@throw异常可能由{@code @ModelAttribute}方法引起
*/
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
container.mergeAttributes(sessionAttributes);
invokeModelAttributeMethods(request, container);
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);
}
}
}
①从SessionAttributes中取出保存的参数,并合并到mavContainer中;
②执行注释了@ModelAttribute的方法并将结果设置到Model;
③判断既注释了@ModelAttribute又在@SessionAttributes注释中(参数名或者参数类型在注释中设置着)的参数是否已经设置到了mavContainer中,如果没有则使用SessionAttributesHandler从SessionAttributes中获取并设置到mavContainer中。
/ * *
调用模型属性方法来填充模型。
只有在模型中不存在时才添加属性。
* /
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
throws Exception {
while (!this.modelMethods.isEmpty()) {
//获取注释了@ModelAttribute的方法
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
//获取@ModelAttribute的属性
ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
Assert.state(ann != null, "No ModelAttribute annotation");
if (container.containsAttribute(ann.name())) {
//如果容器中已经有了参数名,则跳过
if (!ann.binding()) {
container.setBindingDisabled(ann.name());
}
continue;
}
//执行注释的方法
Object returnValue = modelMethod.invokeForRequest(request, container);
if (!modelMethod.isVoid()){
//获取参数名
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
if (!ann.binding()) {
container.setBindingDisabled(returnValueName);
}
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}
}
这里遍历每个注释了@ModelAttribute的方法,然后从注释中获取参数名,如果获取到了(注释中设置了value),而且在mavContainer中已经存在此参数了则跳过此方法,否则执行方法。
执行完之后判断返回值是不是Void类型,如果是则说明这个方法是自己将参数设置到Model中的,这里就不处理了,否则使用getNameForReturnValue方法获取到参数名,并判断是否已存在mavContainer中,如果不存在则设置进去。
获取参数名的规则方法:
/**
*为给定的返回值派生模型属性名。结果将
*基于:
* < ol >
*
方法{@code ModelAttribute}注释值
*
声明的返回类型(如果它比{@code对象}更具体)
*
实际返回值类型
* < / ol >
方法调用返回的值
方法返回类型的描述符
* @return派生名称(never {@code null}或空字符串)
*/
public static String getNameForReturnValue(@Nullable Object returnValue, MethodParameter returnType) {
ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class);
if (ann != null && StringUtils.hasText(ann.value())) {
return ann.value();
}
else {
Method method = returnType.getMethod();
Assert.state(method != null, "No handler method");
Class<?> containingClass = returnType.getContainingClass();
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
}
}
这里首先获取了返回值的@ModelAttribute注释,也就是方法的@ModelAttribute注释,如果设置了value则直接将其作为参数名返回,否则使用Conventions的静态方法get-VariableNameForReturnTyp根据方法、返回值类型和返回值获取参数名:
/**
*确定给定返回类型的常规变量名
方法,考虑泛型集合类型,如果有的话,考虑,下落
*返回给定的返回值,如果方法声明不是特定的
*足够,例如{@code Object}返回类型或无类型集合。
此方法支持响应类型:
* {@code Mono < com.myapp. Product >} becomes {@code "productMono"}
* {@code < com.myapp flux. MyProduct>} becomes {@code "myProductFlux"}
* * {@code Observable<com.myapp.MyProduct>} becomes {@code "myProductObservable"}
Param method the method to generate a variable name for
* param resolvedType the Resolved Return Type of the Method
* @param value the return value (may be {@code null} if not available)
* @return the generated variable name
*/
public static String getVariableNameForReturnType(Method method, Class<?> resolvedType, @Nullable Object value) {
Assert.notNull(method, "Method must not be null");
if (Object.class == resolvedType) {
if (value == null) {
throw new IllegalArgumentException(
"Cannot generate variable name for an Object return type with null value");
}
return getVariableName(value);
}
Class<?> valueClass;
boolean pluralize = false;
String reactiveSuffix = "";
if (resolvedType.isArray()) {
valueClass = resolvedType.getComponentType();
pluralize = true;
}
else if (Collection.class.isAssignableFrom(resolvedType)) {
valueClass = ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric();
if (valueClass == null) {
if (!(value instanceof Collection)) {
throw new IllegalArgumentException("Cannot generate variable name " +
"for non-typed Collection return type and a non-Collection value");
}
Collection<?> collection = (Collection<?>) value;
if (collection.isEmpty()) {
throw new IllegalArgumentException("Cannot generate variable name " +
"for non-typed Collection return type and an empty Collection value");
}
Object valueToCheck = peekAhead(collection);
valueClass = getClassForValue(valueToCheck);
}
pluralize = true;
}
else {
valueClass = resolvedType;
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(valueClass);
if (adapter != null && !adapter.getDescriptor().isNoValue()) {
reactiveSuffix = ClassUtils.getShortName(valueClass);
valueClass = ResolvableType.forMethodReturnType(method).getGeneric().toClass();
}
}
String name = ClassUtils.getShortNameAsProperty(valueClass);
return (pluralize ? pluralize(name) : name + reactiveSuffix);
}
代码比较长,它的核心逻辑就是获取返回值类型的“ShortName”。“ShortName”是使用ClassUtils的getShortNameAsProperty方法获取的,具体逻辑是先获取到去掉包名之后的类名,然后再判断类名是不是大于一个字符,而且前两个字符都是大写,如果是则直接返回,否则将第一个字符变成小写返回。
不过这里的方法返回值类型如果是Object则会使用返回值的实际类型,如果返回值为数组或者Collection类型时会使用内部实际包装的类型,并在最后加“List”。
这一步的整个流程是首先会判断返回值是不是Void类型,如果是则不处理了,如果不是则先判断注释里有没有value,如果有则使用注释的value做参数名,如果没有则根据上面说的规则来解析出参数名,最后判断得到的参数名是否已经存在mavContainer中,如果不存在则将其和返回值添加到mavContainer中。
等等,再往后就看不下去了。
Model中参数的优先级是这样的:①FlashMap中保存的参数优先级最高,它在ModelFactory前面执行;②SessionAttributes中保存的参数的优先级第二,它不可以覆盖FlashMap中设置的参数;③通过注释了@ModelAttribute的方法设置的参数优先级第三;④注释了@ModelAttribute而且从别的处理器的SessionAttributes中获取的参数优先级最低。而且从前面创建ModelFactory的过程可以看出,注释了@ModelAttribute的方法是全局的优先,处理器自己定义的次之。
3.7 ServletInvocableHandlerMethod
ServletInvocableHandlerMethod其实也是一种HandlerMethod,只是增加了方法执行的功能。当然相应地也增加了参数解析、返回值处理等相关功能。
3.7.1 HandlerMethod
HandlerMethod前面已经介绍过了,用于封装Handler和其中具体处理请求的Method,分别对应其中的bean和method属性,除了这两个还有三个属性:beanFactory、bridgedMethod和parameters。beanFactory主要用于新建HandlerMethod时传入的Handler(也就是bean属性)是String的情况,这时需要使用beanFactory根据传入的String作为beanName获取到对应的bean,并设置为Handler;bridgedMethod指如果method是bridge method则设置为其所对应的原有方法,否则直接设置为method;parameters代表处理请求的方法的参数。
一个MethodParameter类型的对象表示一个方法的参数,对MethodParameter主要是理解其参数的含义:
private final int parameterIndex;
@Nullable
private volatile Parameter parameter;
private int nestingLevel;
/** Map from Integer level to Integer type index. */
@Nullable
Map<Integer, Integer> typeIndexesPerLevel;
/** The containing class. Could also be supplied by overriding {@link #getContainingClass()} */
@Nullable
private volatile Class<?> containingClass;
@Nullable
private volatile Class<?> parameterType;
@Nullable
private volatile Type genericParameterType;
@Nullable
private volatile Annotation[] parameterAnnotations;
@Nullable
private volatile ParameterNameDiscoverer parameterNameDiscoverer;
@Nullable
private volatile String parameterName;
- method:参数所在的方法。□constructor:参数的构成方法。
- parameterIndex:参数的序号,也就是第几个参数,从0开始计数。
- nestingLevel:嵌套级别,如果是复合参数会用到,比如,有一个Listparams参数,则params的嵌套级别为1,List内部的String的嵌套级别为2。
- typeIndexesPerLevel:保存每层嵌套参数的序数。
- containingClass:容器的类型,也就是参数所属方法所在的类。
- parameterType:参数的类型。
- genericParameterType:Type型的参数类型,也是参数的类型,但它的类型是Type。
- parameterAnnotations:参数的注释。
- parameterNameDiscoverer:参数名称查找器。□parameterName:参数名称。
在HandlerMethod中定义了两个内部类来封装参数,一个封装方法调用的参数,一个封装方法返回的参数,它们主要使用method和parameterIndex来创建MethodParameter,封装返回值的ReturnValueMethodParameter继承自封装调用参数的HandlerMethodParameter,它们使用的method都是bridgedMethod,返回值使用的parameterIndex是-1。
什么是bridge method?(桥方法)
在HandlerMethod中的bridgedMethod指的是被桥的方法(注意是bridged而不是bridge),也就是原来的方法。比如,HandlerMethod中的method如果是Object类型的id方法,bridgedMethod就是String类型的id方法,如果method是String类型的id方法,bridgedMethod将和method代表同一个方法,如果不涉及泛型bridgedMethod和method都是同一个方法。
3.7.2 InvocableHandlerMethod
InvocableHandlerMethod继承自HandlerMethod,在父类基础上添加了调用的功能,也就是说InvocableHandlerMethod可以直接调用内部属性method对应的方法(严格来说应该是bridgedMethod)。
新增了三个字段:
- dataBinderFactory:WebDataBinderFactory类型,可以创建WebDataBinder,用于参数解析器ArgumentResolver中。
- argumentResolvers:HandlerMethodArgumentResolverComposite类型,用于解析参数。
- parameterNameDiscoverer:ParameterNameDiscoverer类型,用来获取参数名,用于MethodParameter中。
InvocableHandlerMethod中Method调用的方法是invokeForRequest:
/**
*在给定请求的上下文中解析其参数值后调用该方法。
*
参数值通常通过解析
* {@link HandlerMethodArgumentResolvers}。
* {@code providedArgs}参数可以提供直接使用的参数值,
*即无争论解决。提供的参数值的示例包括
* {@link WebDataBinder}, {@link SessionStatus},或抛出异常实例。
*在参数解析器之前检查提供的参数值。
*
委托给{@link #getMethodArgumentValues}并调用{@link #doInvoke
*解决参数。
* @param请求当前请求
这个请求的ModelAndViewContainer
* @param提供了“给定”参数匹配类型,没有解决
被调用的方法返回的原始值
* @抛出异常,如果没有找到合适的参数解析器,
*或者该方法引发异常
* @see # getMethodArgumentValues
* @see # doInvoke
*/
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
/ * *
*获取当前请求的方法参数值,检查提供的
*参数值,并退回到配置的参数解析器。
结果数组将被传递到{@link #doInvoke}中。
* /
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取方法参数,在handlerMethod中
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
//用于保存解析出的参数的值
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
//设置参数名解析器
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
//如果参数已经在providedArgs提供了,则直接设置到parameter
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//解析参数
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
//将堆栈跟踪留到以后,异常可能会被解决和处理…
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
/ * *
用给定的参数值调用处理程序方法。
* /
@Nullable
protected Object doInvoke(Object... args) throws Exception {
//使桥方法强制可用
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(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 {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
一条是准备方法所需要的参数,使用的是getMethodArgumentValues方法,另一条用于具体调用Method,具体使用的方法是doInvoke方法。
实际上前面说过的注释了@InitBinder的方法和注释了@ModelAttribute的方法就是封装成了Invocable-HandlerMethod对象,然后直接执行的。
3.7.3 ServletInvocableHandlerMethod
ServletInvocableHandlerMethod继承自InvocableHandlerMethod,在父类基础上增加了三个功能:①对@ResponseStatus注释的支持;②对返回值的处理;③对异步处理结果的处理。
@ResponseStatus注释用于处理器方法或者返回值上,作用是对返回Response的Status进行设置,它有两个参数:value和reason,value是HttpStatus类型,不能为空,reason是String类型,表示错误的原因,默认为空字符串(不是null)。当一个方法注释了@ResponseStatus后,返回的response会使用注释中的Status,如果处理器返回值为空或者reason不为空,则将中断处理直接返回(不再渲染页面)。实际环境中用的并不是很多。
ServletInvocableHandlerMethod处理请求使用的是invokeAndHandle方法:
/ * *
调用该方法并通过其中一个方法处理返回值
*已配置{@link HandlerMethodReturnValueHandler
* @param webRequest当前请求
这个请求的ModelAndViewContainer
* @param提供“给定”参数匹配类型(未解析)
* /
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
首先调用父类的invokeForRequest执行请求,接着处理@ResponseStatus注释,最后处理返回值。处理@ResponseStatus注释的方法是setResponseStatus,它会根据注释的值设置response的相关属性:
/ * *
*根据{@link ResponseStatus}标注设置响应状态。
* /
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
HttpStatus status = getResponseStatus();
if (status == null) {
return;
}
HttpServletResponse response = webRequest.getResponse();
if (response != null) {
String reason = getResponseStatusReason();
if (StringUtils.hasText(reason)) {
response.sendError(status.value(), reason);
}
else {
response.setStatus(status.value());
}
}
//被重定向视图拾取
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
}
处理返回值的逻辑是先判断返回值是不是null,如果是null,只要request的notModified为真、注释了@ResponseStatus和mavContainer的requestHandled为true这三项中有一项成立则设置为请求已经处理并返回,如果返回值不为null,而@ResponseStatus注释里存在reason,也会将请求设置为已处理并返回。设置已处理的方法前面已经讲过,就是设置mavContainer的requestHandled属性为true。如果上面那些条件都不成立则将mavContainer的requestHandled设置为false,并使用returnValueHandlers处理返回值。
3.8 HandlerMethodArgumentResolver
HandlerMethodArgumentResolver是用来为处理器解析参数的
/ * *
*用于将方法参数解析为参数值的策略接口
一个给定请求的上下文
* @see HandlerMethodReturnValueHandler
* /
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
两个方法:一个用于判断是否可以解析传入的参数,另一个就是用于实际解析参数。
这里解析器的数量是非常多的。
接下来就只解析注释了@PathVariable的参数类型的PathVariableMethodArgumentResolver解析器
3.8.1 PathVariableMethodArgumentResolver
先看其父类
org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver:
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
//根据参数类型获取NameValueInfo
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
//具体解析参数,是模板方法,在子类实现
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
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());
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
通过注释大家可以看到,首先根据参数类型获取到NamedValueInfo,然后将它传入模板方法resolveName由子类具体解析,最后对解析的结果进行处理。这里用到的Named-ValueInfo是一个内部类,其中包含的三个属性分别表示参数名、是否必须存在和默认值;
该方法馁的其他方法就不深入看了,我快看吐了。
3.9 HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler用在ServletInvocableHandlerMethod中,作用是处理处理器执行后的返回值,主要有三个功能:①将相应参数添加到Model;②设置View;③如果请求已经处理完则设置ModelAndViewContainer的requestHandled为true。
/ * *
*策略接口来处理调用函数返回的值
处理程序方法。
* @see HandlerMethodArgumentResolver
* /
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
一个用于判断是否支持,一个用于具体处理返回值。
其具体实现也是十分的多,用于处理各种不同的场景。
总结:
HandlerAdapter整个处理过程可以分为三步步:解析参数、执行请求和处理返回值。
解析参数过程中用到的参数来源有多个,大体可以分为两大类,一类从Model中来,另一类从request中来,前者通过FlashMapManager和ModelFactory管理。
具体解析过程不同类型的参数使用不同的HandlerMethodArgumentResolver进行解析,有的Resolver使用了WebDataBinderFactory创建的WebDataBinder,可以通过@InitBinder向WebDataBinderFactory中注册WebDataBinder。
执行请求是用的HandlerMethod的子类ServletInvocableHandlerMethod(实际执行是InvocableHandlerMethod)。
返回值是使用HandlerMethodReturnValueHandler进行解析的,不同类型返回值对应不同的HandlerMethodReturnValueHandler。
整个处理过程中ModelAndViewContainer起着参数传递的作用。
本文地址:https://blog.csdn.net/littlewhitevg/article/details/107392887
下一篇: MySQL字符集设定与查询