• SpringMVC源码情操陶冶-RequestMappingHandlerAdapter适配器


    承接前文SpringMVC源码情操陶冶-HandlerAdapter适配器简析。RequestMappingHandlerAdapter适配器组件是专门处理RequestMappingHandlerMapping返回的HandlerMethod对象,由此对象返回相应的视图对象或者直接返回数据给客户端

    RequestMappingHandlerAdapter的xml配置应用

    常用的针对handlerAdapter适配器的配置如下

    <mvc:annotation-driven>
    	<!--对直接返回数据前端而不是视图的消息处理器配置-->
    	<mvc:message-converters register-defaults="true">
    		<ref bean="jsonMessageConverter" />
    		<ref bean="xmlMessageConverter" />
    		<bean class="com.jing.springmvc.message.converter.StringHttpMessageConverter"/>
    	</mvc:message-converters>
    	<!--方法参数解析器-->
    	<mvc:argument-resolvers>
    		....
    	</mvc:argument-resolvers>
    	<!--返回内容处理器,其会包含上述的mvc:message-converters配置-->
    	<mvc:return-value-handlers>
    		...
    	</mvc:return-value-handlers>
    </mvc:annotation-driven>
    

    RequestMappingHandlerAdapter代码层面解析

    首先我们可以看下RequestMappingHandlerAdapter的构造函数

    	/**
    	 * Default constructor.
    	 */
    	public RequestMappingHandlerAdapter() {
    
    		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
    		stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316
    
    		this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
    		this.messageConverters.add(new ByteArrayHttpMessageConverter());
    		this.messageConverters.add(stringHttpMessageConverter);
    		this.messageConverters.add(new SourceHttpMessageConverter<Source>());
    		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    	}
    
    1. 上述代表如果不指定mvc:message-converters配置,则默认采用上述的消息处理器集合

    2. mvc:message-converters中的属性register-defaults默认值为true ,其除了上述的消息处理器集合,还会添加MappingJackson2HttpMessageConverter等消息处理器 ;
      如果指定register-defaultsfalse则会采用配置的消息处理器而忽略默认的配置

    再而我们继续看下RequestMappingHandlerAdapter.class的继承关系

    public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
    		implements BeanFactoryAware, InitializingBean{}
    

    我们会看到非常熟悉的InitializingBean接口,其afterPropertiesSet()方法会在其内部属性设置完毕后被调用,首先我们就从这个方法入手。

    RequestMappingHandlerAdapter#afterPropertiesSet()

    代码如下

    	@Override
    	public void afterPropertiesSet() {
    		// 加载mvc:argument-resolvers的配置信息以及应用默认的配置
    		if (this.argumentResolvers == null) {
    			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
    			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    		}
    		// 加载mvc:argument-resolvers的配置信息以及应用默认的配置
    		if (this.initBinderArgumentResolvers == null) {
    			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
    			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    		}
    		// 加载mvc:message-converters的配置信息以及应用默认的配置
    		if (this.returnValueHandlers == null) {
    			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
    			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    		}
    		// 初始化缓存所有@ControllerAdvice类上的含有@ModelAttributes和@InitBinder注解方法集合
    		initControllerAdviceCache();
    	}
    

    针对上述的代码我们罗列下默认的参数处理、返回值处理是配置哪些处理类

    1. 参数处理类列表
    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
    
    		// Annotation-based argument resolution
    		// @RequestParam和@RequestParamMap
    		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    		resolvers.add(new RequestParamMapMethodArgumentResolver());
    		// @PathVariable和@PathVariableMap
    		resolvers.add(new PathVariableMethodArgumentResolver());
    		resolvers.add(new PathVariableMapMethodArgumentResolver());
    		// @MatrixVariable和@MatrixVariableMap
    		resolvers.add(new MatrixVariableMethodArgumentResolver());
    		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    		resolvers.add(new ServletModelAttributeMethodProcessor(false));
    		// @RequestBody
    		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());
    
    		// 加载mvc:argument-resolver的自定义配置
    		if (getCustomArgumentResolvers() != null) {
    			resolvers.addAll(getCustomArgumentResolvers());
    		}
    
    		// Catch-all
    		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
    		resolvers.add(new ServletModelAttributeMethodProcessor(true));
    
    		return resolvers;
    	}
    
    1. 主要应用于我们常用的方法参数的注解,比如@RequestParam@PathVarible@RequestBody等注解方法,多余的读者可自行分析

    2. 上述的@RequestBody@RequestPart注解处理类也会用到mvc:message-converter的配置

    3. 此处作下备注:RequestParamMethodArgumentResolver其也会处理不带注解的参数,默认根据代码的参数名去request对象拿取值

    1. 返回值处理类列表
    	/**
    	 * Return the list of return value handlers to use including built-in and
    	 * custom handlers provided via {@link #setReturnValueHandlers}.
    	 */
    	private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
    		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
    
    		// Single-purpose return value types
    		handlers.add(new ModelAndViewMethodReturnValueHandler());
    		handlers.add(new ModelMethodProcessor());
    		handlers.add(new ViewMethodReturnValueHandler());
    		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager));
    		handlers.add(new HttpHeadersReturnValueHandler());
    		handlers.add(new CallableMethodReturnValueHandler());
    		handlers.add(new DeferredResultMethodReturnValueHandler());
    		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
    
    		// Annotation-based return value types
    		handlers.add(new ModelAttributeMethodProcessor(false));
    		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager));
    
    		// Multi-purpose return value types
    		handlers.add(new ViewNameMethodReturnValueHandler());
    		handlers.add(new MapMethodProcessor());
    
    		// 自定义的返回类型处理器
    		if (getCustomReturnValueHandlers() != null) {
    			handlers.addAll(getCustomReturnValueHandlers());
    		}
    
    		// Catch-all
    		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
    			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
    		}
    		else {
    			handlers.add(new ModelAttributeMethodProcessor(true));
    		}
    
    		return handlers;
    	}
    
    1. 主要应用于我们常用的方法返回类型,比如ModelModelAndViewViewString@ResponseBody等返回类型,多余的读者可自行分析

    2. 上述的@ResponseBody注解也会用到mvc:message-converter的配置,多用于将数据直接写入响应体中

    RequestMappingHandlerAdapter#handleInternal()

    完整的处理RequestMappingHandlerMapping所含有的对象HandlerMethod,废话少说,我们直接看下源码

    @Override
    	protected final ModelAndView handleInternal(HttpServletRequest request,
    			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    		// checkAndPrepare,处理前的准备工作①
    		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
    		    // 相应的类上存在@SessionAttribute注解则走此处
    			// 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);
    				}
    			}
    		}
    		// invokeHandleMethod,处理HandleMethod对象开始业务逻辑②
    		return invokeHandleMethod(request, response, handlerMethod);
    	}
    

    上述罗列了最主要的两个执行方法,按照先后顺序已标注为①和②,现在我们分别看下方法的逻辑

    WebContentGenerator#checkAndPrepare()-检查并作预备工作①

    我们直接通过源码来讲解

    protected final void checkAndPrepare(
    			HttpServletRequest request, HttpServletResponse response, int cacheSeconds, boolean lastModified)
    			throws ServletException {
    
    		// 检查请求的方法是否被支持
    		// RequestMappingHandlerAdapter默认指定的supportedMethods为null
    		String method = request.getMethod();
    		if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
    			throw new HttpRequestMethodNotSupportedException(
    					method, StringUtils.toStringArray(this.supportedMethods));
    		}
    
    		// 检查是否session对象必须存在,默认为false
    		if (this.requireSession) {
    			if (request.getSession(false) == null) {
    				throw new HttpSessionRequiredException("Pre-existing session required but none found");
    			}
    		}
    
    		// 是否增加cache头响应,默认不加
    		applyCacheSeconds(response, cacheSeconds, lastModified);
    	}
    
    1. 从上述的代码解析看,此处只是为了检查请求的方式是否被支持、session对象是否必须存在、cache响应头是否需要新增。

    2. 通过代码跟踪与发现,RequestMappingHandlerAdapter对象并不需要上述的检查,所以此方法可略过

    RequestMappingHandlerAdapter#invokeHandleMethod()-关键处理逻辑②

    	/**
    	 * 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);
    		// HandlerMethod对应实体类的@InitBinder注解方法集合以及所关联的@ControllAdvice注解类的@InitBinder方法集合
    		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    		// 同上,对应@ModelAttribute方法集合
    		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    		
    		// 包装成ServletInvocableHandlerMethod对象
    		ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
    		
    		// 整合model对象集合1.request对象的org.springframework.web.servlet.DispatcherServlet.INPUT_FLASH_MAP属性值 2.解析带@ModelAttribute注解的方法返回值 3.session保存的属性集合
    		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    		modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
    		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    		
    		// 异步请求的处理配置,servlet 3.0+才会被引用
    		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);
    	}
    

    具体的代码备注上述已展示,可以看下最主要的部分ServletInvocableHandlerMethod#invokeAndHandle(),其实如何处理具体的业务逻辑

    ServletInvocableHandlerMethod#invokeAndHandle()

    直接上源码

    public final void invokeAndHandle(ServletWebRequest webRequest,
    			ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    		// 处理业务逻辑代码得到返回值
    		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    		
    		// 设置返回状态码和返回信息,通常与@ResponseStatus结合使用
    		setResponseStatus(webRequest);
    
    		....
    		....
    		
    
    		try {
    			// 检索returnValueHandlers集合,查找对应返回类型的处理器用于处理请求
    			this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    		}
    		catch (Exception ex) {
    			if (logger.isTraceEnabled()) {
    				logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
    			}
    			throw ex;
    		}
    	}
    
    1. 上述代码如果方法结合了@ResponseStatus注解,则如果指定了reason字段值且非空,则会直接发送错误至前台,错误信息则为reason指定的内容。具体的可查看下@ResponseStatus注解文档

    2. 通过之前设置的returnValueHandlers集合来匹配返回类型从而处理多方式的响应

    为了方便理解上述的第二点,举个处理@RequestBody@ResponseBody注解方法的返回方式,此处为RequestResponseBodyMethodProcessor

    	@Override
    	public void handleReturnValue(Object returnValue, MethodParameter returnType,
    			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
    			throws IOException, HttpMediaTypeNotAcceptableException {
    		// 设置处理状态为已处理
    		mavContainer.setRequestHandled(true);
    		// 一般此处的返回值为String对象或者自定义的对象,切记是一个对象,而非void
    		if (returnValue != null) {
    			// 此处就是通过mvc:message-converters指定的或者默认的消息处理器来处理内容
    			writeWithMessageConverters(returnValue, returnType, webRequest);
    		}
    	}
    

    从此处我们可以直接关注AbstractMessageConverterMethodProcessor#writeWithMessageConverters()方法

    	protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
    			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
    			throws IOException, HttpMediaTypeNotAcceptableException {
    
    		Class<?> returnValueClass = returnValue.getClass();
    		HttpServletRequest servletRequest = inputMessage.getServletRequest();
    		// 大多用于获取请求包头的accept头,表明客户端需要接收什么类型的媒体类型
    		List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest);
    		// 优先应用@RequestMapping的produce属性,表示生产何种类型的媒体类型,否则则采用messageConverters集合下指定的supportMediaTypes属性集合
    		List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass);
    
    		// 匹配查找结果
    		Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
    		for (MediaType requestedType : requestedMediaTypes) {
    			for (MediaType producibleType : producibleMediaTypes) {
    				if (requestedType.isCompatibleWith(producibleType)) {
    					compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
    				}
    			}
    		}
    		// 没有匹配的媒体类型处理,则抛异常
    		if (compatibleMediaTypes.isEmpty()) {
    			throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
    		}
    
    		List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
    		MediaType.sortBySpecificityAndQuality(mediaTypes);
    
    		......
    		......
    
    		if (selectedMediaType != null) {
    			selectedMediaType = selectedMediaType.removeQualityValue();
    			// 遍历消息处理器集合,统一通过canWrite()方法来做条件判断,采用write()方法写入数据
    			for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
    				if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
    					((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage);
    					if (logger.isDebugEnabled()) {
    						logger.debug("Written [" + returnValue + "] as "" + selectedMediaType + "" using [" +
    								messageConverter + "]");
    					}
    					return;
    				}
    			}
    		}
    		// 抛异常
    		throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
    	}
    
    1. 优先比较消息的媒体类型是否能被响应。不能被响应则会抛出异常
      - 请求体的媒体类型获取:Accept头、请求路径的文件后缀名等
      - 响应体的媒体类型获取:@RequestMapping注解属性produce(优先)、messageConverters配置的supportMediaTypes集合(其次)、所有类型均支持(默认)

    2. 写入响应体时,优先判断消息处理器是否支持返回内容对应的类,是则开始写入数据到响应体。此点多应用于json对象的返回包装,读者也可以多关注spring内置的MappingJackson2HttpMessageConverter消息处理器

    3. 上述条件均不满足的情况下,会抛出Could not find acceptable representation等错误信息

    RequestMappingHandlerAdapter#getModelAndView()-判断是否需要返回视图对象

    	private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
    			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
    
    		modelFactory.updateModel(webRequest, mavContainer);
    		// 请求如果已经标记为已处理,则返回null的视图对象。
    		if (mavContainer.isRequestHandled()) {
    			return null;
    		}
    		// 将含有的ModelMap集合塞到视图对象中,此处主要可以由视图解析插件传值给前台页面调用
    		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;
    	}
    

    主要通过ModelAndViewContainer#requestHandled()方法标记来判断是否返回视图对象,此标记为true表示不经过视图返回给前端而是通过其他形式的返回比如直接返回数据给前端,具体可看前面的RequestResponseBodyMethodProcessor的代码分析。

    总结

    最好结合SpringMVC源码情操陶冶-DispatcherServlet简析(二)SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器两篇博文来阅读,则会对HandlerAdapter适配器有更深的理解

  • 相关阅读:
    Windows XP中万能断点
    c#运算符 ?
    转神秘的程序员
    经典解决“线程间操作无效
    文件上传
    dowload.aspx
    mail
    js 正则
    新年快乐
    DataTable Compute
  • 原文地址:https://www.cnblogs.com/question-sky/p/8529862.html
Copyright © 2020-2023  润新知