1、概念
HandlerMapping通过request找到了Handler,HandlerAdapter是具体用来干活的,每个HandlerAdapter封装了一种Handler的具体使用方法。
2、HandlerAdapter基本结构
imageHandlerAdapter是一个接口实现这个接口的有四个类,其中实现该接口的AbstractHandlerMethodAdapter的子类结构最为复杂,同时也是由于处理的Handler不是固定的格式。下面主要来介绍RequestMappingHandlerAdapter。
具体的请求处理过程可以分为三步
- 准备好处理器所需要的参数
- 使用处理器处理请求
- 处理返回值,也就是将不同类型的返回值统一处理成ModelAndView类型
理解参数的绑定之前需要先弄清楚三个问题
- 都有哪些参数需要绑定(请求方法时,参数的格式多种多样)
- 参数的值的来源
- 具体进行绑定的方法
参数的来源一共有6种
- request中相关的参数,主要包括url中的参数、post过来的参数以及请求头所包含的值
- cookie中的参数
- session中的参数
- 设置到FlashMap中的参数,这种参数主要用于redirect的参数传递
- SessionAttribute传递的参数,这类参数通过@SessionAttribute注释传递
- 通过相应的注释@ModelAttribute的方法进行设置的参数
参数的具体解析是使用HandlerMethodArgumentResolver类型的组件完成的,不同类型的参数使用不同的ArgumentResolver来解析。
3、RequestMappingHandlerAdapter自身结构
3.1 RequestMappingHandlerAdapter创建
RequestMappingHandlerAdapter的创建是在afterPropertiesSet方法中实现,其中主要内容就是初始化argumentResolver、initBinderArgumentResolver、returnValueHandler以及
@ControllerAdvice注释的类相关的modelAttributeAdviceCache、initBinderAdviceCache和responseBodyAdvice这6个属性
- argumentResolver:用于给处理器方法和注释了@ModelAttribute的方法设置参数
- initBinderArgumentResolver:用于给注释了@initBinder的方法设置参数
- returnValueHandlers:用于将处理器的返回值处理成ModelAndView的类型
- modelAttributeAdviceCache和initBinderAdviceCache:分别用于缓存@ControllerAdvice注释的类里面注释了@ModelAttribute和@InitBinder的方法
- responseBodyAdvice:用来保存前面介绍过的实现了@ResponseBodyAdvice接口、可以修改返回的ResponseBody的类
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
//初始化注注释了@ControllerAdvice的类的相关属性
initControllerAdviceCache();
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);
}
}
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
if (logger.isInfoEnabled()) {
logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
}
//获取到所有注释了@ControllerAdvice的bean
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
//根据Order排序
Collections.sort(beans, new OrderComparator());
List<Object> responseBodyAdviceBeans = new ArrayList<Object>();
for (ControllerAdviceBean bean : beans) {
//查找了注释了@ModelAttribute而且没有注释@RequestMapping的方法
Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(bean, attrMethods);
logger.info("Detected @ModelAttribute methods in " + bean);
}
//查找注释了@InitBinder的方法
Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(bean, binderMethods);
logger.info("Detected @InitBinder methods in " + bean);
}
//查找实现了ResponseBodyAdvice接口的类
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
responseBodyAdviceBeans.add(bean);
logger.info("Detected ResponseBodyAdvice bean in " + bean);
}
}
if (!responseBodyAdviceBeans.isEmpty()) {
this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
}
}
查看后面三个属性属性的初始化中getDefaultXXX方法
/**
* Return the list of argument resolvers to use including built-in resolvers
* and custom resolvers provided via {@link #setCustomArgumentResolvers}.
*/
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
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()));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments 添加自定义的参数解析器
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
//方法中将默认的参数处理器全部添加进来
//注:在添加参数解析器时,顺序是无法进行改变的
3.2.RequestMappingHandlerAdapter使用
RequestMappingHandlerAdapter处理请求的入口方法是handlerInternal
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//判断Handler是否有@SessionAttribute注释的参数
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
}
else {
// Uses configured default cacheSeconds setting.
checkAndPrepare(request, response, true);
}
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandleMethod(request, response, handlerMethod);
}
}
}
return invokeHandleMethod(request, response, handlerMethod);
}
/**
* Check and prepare the given request and response according to the settings
* of this generator. Checks for supported methods and a required session,(根据给定的请求属性设置,检查方法和session)
* and applies the given number of cache seconds.(请求一定时间的缓存时间)
* @param request current HTTP request
* @param response current HTTP response
* @param cacheSeconds positive number of seconds into the future that the
* response should be cacheable for, 0 to prevent caching
* @param lastModified if the mapped handler provides Last-Modified support
* @throws ServletException if the request cannot be handled because a check failed
*/
protected final void checkAndPrepare(
HttpServletRequest request, HttpServletResponse response, int cacheSeconds, boolean lastModified)
throws ServletException {
// Check whether we should support the request method.
String method = request.getMethod();
if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
throw new HttpRequestMethodNotSupportedException(
method, StringUtils.toStringArray(this.supportedMethods));
}
// Check whether a session is required.
if (this.requireSession) {
if (request.getSession(false) == null) {
throw new HttpSessionRequiredException("Pre-existing session required but none found");
}
}
// Do declarative cache control.
// Revalidate if the controller supports last-modified.
applyCacheSeconds(response, cacheSeconds, lastModified);
}
/**
* Apply the given cache seconds and generate respective HTTP headers.
* <p>That is, allow caching for the given number of seconds in the
* case of a positive value, prevent caching if given a 0 value, else
* do nothing (i.e. leave caching to the client).
* @param response the current HTTP response
* @param seconds the (positive) number of seconds into the future that
* the response should be cacheable for; 0 to prevent caching; and
* a negative value to leave caching to the client.
* @param mustRevalidate whether the client should revalidate the resource
* (typically only necessary for controllers with last-modified support)
*/
protected final void applyCacheSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) {
if (seconds > 0) {
//给缓存设置过期时间
cacheForSeconds(response, seconds, mustRevalidate);
}
else if (seconds == 0) {
//阻止使用缓存
preventCaching(response);
}
// Leave caching to the client otherwise.
}
protected final void cacheForSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) {
if (this.useExpiresHeader) {
// HTTP 1.0 header
response.setDateHeader(HEADER_EXPIRES, System.currentTimeMillis() + seconds * 1000L);
}
if (this.useCacheControlHeader) {
// HTTP 1.1 header
String headerValue = "max-age=" + seconds;
if (mustRevalidate || this.alwaysMustRevalidate) {
headerValue += ", must-revalidate";
}
response.setHeader(HEADER_CACHE_CONTROL, headerValue);
}
}
protected final void preventCaching(HttpServletResponse response) {
response.setHeader(HEADER_PRAGMA, "no-cache");
if (this.useExpiresHeader) {
// HTTP 1.0 header
response.setDateHeader(HEADER_EXPIRES, 1L);
}
if (this.useCacheControlHeader) {
// HTTP 1.1 header: "no-cache" is the standard value,
// "no-store" is necessary to prevent caching on FireFox.
response.setHeader(HEADER_CACHE_CONTROL, "no-cache");
if (this.useCacheControlNoStore) {
response.addHeader(HEADER_CACHE_CONTROL, "no-store");
}
}
}
缓存的设置其实就是对Response的Header进行了相应的设置,上面的代码实际含义是,如果有@SessionAttribute注释则阻止使用缓存,否则就什么也不做
invokeHandlerMethod方法初始化了三个类型的变量分别是
- WebDataBinderFactory
- ModelFactory
- ServletInvocableHandlerMethod
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
*/
private ModelAndView invokeHandleMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
final 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("Found concurrent result value [" + result + "]");
}
requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
}
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
WebDataBinderFactory主要是用来创建WebDataBinder,它的功能是实现参数和String之间的类型转换,WebDataBinderFactory就是将符合条件的的注释了@InitBinder的方法找出来,这里的InitBinder方法包括两部分:1、注释了@ControllerAdvice的并且符合要求的全局处理器里面的InitBinder方法。2、处理器自身的InitBinder方法
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
Class<?> handlerType = handlerMethod.getBeanType();
//检查当前Handler中InitBinder方法是否已经存在缓存中
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
//如果没有则查找并且设置到缓存中
methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
//定义保存InitBinder方法的临时变量
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
// Global methods first(首先添加全局变量到initBinderMethods)
for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache .entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
Object bean = entry.getKey().resolveBean();
for (Method method : entry.getValue()) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
}
//将当前Handler中的InitBinder方法添加到initBinderMethos
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);
}
ModelFactoryz是用来处理Model的,主要包含两个功能:1、在处理器具体处理之前对Model进行初始化2、在处理完请求后对Model参数进行更新。
Model初始化分为三个部分:1、将原来的SessionAttribute中的值设置到Model。2、执行相应注释了@ModelAttribute的方法并将其值设置到Mode。3、处理器中注释了@ModelAttribute的参数如果同时在SessionAttribute中也配置了,而且在mavContainer中还没有值则从全部SessionAttribute中查找出来并设置进去
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
//获取SessionAttributeHandler
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
//获取处理器的类型
Class<?> handlerType = handlerMethod.getBeanType();
//获取处理器类中注释了@ModelAttribute而且没有注释@RequestMapping的类型第一次获取后添加到缓存,以后直接从缓存中获取
Set<Method> methods = this.modelAttributeCache.get(handlerType);
//
if (methods == null) {
methods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
// Global methods first(首先添加全局的方法)
for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
Object bean = entry.getKey().resolveBean();
for (Method method : entry.getValue()) {
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);
}
ServletInvocableHandlerMethod继承自HandlerMethod,并且可以直接执行。实际请求的处理就是通过它来执行的,参数绑定、处理请求以及返回值处理都在它里面完成
private ServletInvocableHandlerMethod createRequestMappingMethod(
HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
ServletInvocableHandlerMethod requestMethod;
requestMethod = new ServletInvocableHandlerMethod(handlerMethod);
requestMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
requestMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
requestMethod.setDataBinderFactory(binderFactory);
requestMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
return requestMethod;
}
前面invokeHandlerMethod方法中的三个变量弄清楚之后就比较容易理解了,上面三个变量完了之后下面还有三个变量需要处理
- 1、新建传递参数的ModelAndViewContainer容器,并将相应的参数设置到其Model中
- 2、执行请求
- 3、请求处理完后进行一些后置处理
新建ModelAndViewContainer类型的mavContainer参数,用于保存Model和View,它贯穿于整个处理过程,然后对mavContainer进行了设置,主要包括三部分内容:1、将FlashMap中的数据设置到Model;2、使用modelFactory将SessionAttribute和注释了@ModelAttribute的方法的参数设置到Model;3、根据配置对ignoreDefaultModelOnRedirect进行了参数设置,这个参数在分析ModelAndViewContainer的时候再详细讲解
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
执行请求,具体方法是直接调用ServletInvocableHandlerMethod里的invokeAndHandle方法执行
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
处理完请求后的后置处理,这是在getModelAndView方法中处理的。一共做了三件事:1、调用ModelFactory的updateModel方法更新了Model。2、根据mavContainer创建了ModelAndView。3、如果mavContainer里的model是RedirectAttribute类型,则将其设置到FlashMap
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);
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
网友评论