spring设计好美:走入spring请求分发

Posted Small leaf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring设计好美:走入spring请求分发相关的知识,希望对你有一定的参考价值。

本编主要是讲解,spring请求这整个过程中,以及有哪些地方可以扩展

1.简单聊聊springweb

springweb只需要从tomcat如何加载web项目来理解即可。更加简单一点,tomcat是如何加载Sevlet。

tomcat7之前,我们需要配置web.xml
tomcat启动之后会去加载我们的web项目,去查找web.xml。按照web.xml有一定的顺序进行加载,并且tomcat会创建ServletContext上下文。

我们web.xml配置了两个类
org.springframework.web.context.ContextLoaderListener
org.springframework.web.servlet.DispatcherServlet

1.1 两个核心的Sevlet Api

1.ServletContextListener
容器启动的时候会去调用,ServletContextListener.contextInitialized
ContextLoaderListener就是contextInitialized,容器启动的时候就会去调用contextInitialized,初始化我们的容器。
2.Servlet
tomcat启动的时候如果设置了sevlet的start等级,或者第一次请求的时候,会初始化sevlet,也就是Servlet.init
而DispatcherServlet也就是Servlet
然后DispatcherServlet就初始化了我们的web容器信息,视图,模板等等。

1.2 消失的web.xml

tomcat7之后我们可以不配置web.xml。springboot 以及javaconfig方式都不需要web.xml那是如何启动的呢?
1.
tomcat7启动时会有一个方法
ContextConfig,processServletContainerInitializers方法。会去加载javax.servlet.ServletContainerInitializer
并且会去初始化@HandlesTypes注解里面的信息。知道这两个要点就行。
2.
在spring-web的jar文件中,有一个META-INF/services目录下javax.servlet.ServletContainerInitializer文件,这个java的spi机制的都知道,这是java的服务提供发现机制,通过这样的约束方式,容器会自动找到这个文件并根据文件中配置的类名进行实例化

找到了org.springframework.web.SpringServletContainerInitializer

3.@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer

我们来看SpringServletContainerInitializer.onStartup

3.WebApplicationInitializer的实现类
AbstractDispatcherServletInitializer

看看它的onStartup方法有个
registerDispatcherServlet,
瞬间豁然开朗,初始化了DispatchSevlet,这样和我们前面的web.xml方式全部串起来了。

2.请求开始

首先请求是从sevlet开始的,也就是调用了sevice方法。
直接看流程图。

1.service
判断请求类型,然后调用指定方法,以post请求为例。
2.doPost
调用了processRequest(request, response);
3.processRequest
加工请求,发送一些监听事件

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException 

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		initContextHolders(request, localeContext, requestAttributes);

		try 
			doService(request, response);
		
		catch (ServletException | IOException ex) 
			failureCause = ex;
			throw ex;
		
		catch (Throwable ex) 
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		

		finally 
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) 
				requestAttributes.requestCompleted();
			

			if (logger.isDebugEnabled()) 
				if (failureCause != null) 
					this.logger.debug("Could not complete request", failureCause);
				
				else 
					if (asyncManager.isConcurrentHandlingStarted()) 
						logger.debug("Leaving response open for concurrent processing");
					
					else 
						this.logger.debug("Successfully completed request");
					
				
			

			publishRequestHandledEvent(request, response, startTime, failureCause);
		
	

2.1 请求全局变量,RequestContextHolder

从上面代码中我们可以看到,请求前,我们初始化了LocaleContextHolder,RequestContextHolder保存了每次请求的request对象。
那么我们每次请求,均可以用这个类来获取request对象。

2.2 监听ServletRequestHandledEvent事件

从publishRequestHandledEvent(request, response, startTime, failureCause)

	public ServletRequestHandledEvent(Object source, String requestUrl,
			String clientAddress, String method, String servletName, @Nullable String sessionId,
			@Nullable String userName, long processingTimeMillis, @Nullable Throwable failureCause, int statusCode) 

		super(source, sessionId, userName, processingTimeMillis, failureCause);
		this.requestUrl = requestUrl;
		this.clientAddress = clientAddress;
		this.method = method;
		this.servletName = servletName;
		this.statusCode = statusCode;
	

请求结束后发送了这样一个默认事件,如果我们在请求后需要用到这个事件,监听一下就可以了。


继续往下走
4.doService
对请求添加了一些特殊的参数。并调用了doDispatch
5.doDispatch


2.3 配置文件请求

processedRequest = checkMultipart(request);

	protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException 
		if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) 
			if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) 
				logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
						"this typically results from an additional MultipartFilter in web.xml");
			
			else if (hasMultipartException(request) ) 
				logger.debug("Multipart resolution failed for current request before - " +
						"skipping re-resolution for undisturbed error rendering");
			
			else 
				try 
					return this.multipartResolver.resolveMultipart(request);
				
				catch (MultipartException ex) 
					if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) 
						logger.debug("Multipart resolution failed for error dispatch", ex);
						// Keep processing error dispatch with regular request handle below
					
					else 
						throw ex;
					
				
			
		
		// If not returned before: return original request.
		return request;
	

我们用@Bean初始化文件处理方法即可。
例如

  @Bean
    public MultipartResolver multipartResolver() throws IOException 
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
        commonsMultipartResolver.setUploadTempDir(new FileSystemResource("/temp"));
        return commonsMultipartResolver;
    

2.4 请求如何找到对应的执行方法handler

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) 
		for (T mapping : mappings) 
			T match = getMatchingMapping(mapping, request);
			if (match != null) 
				matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
			
		
	

从我们注册映射中,取出执行方法

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) 
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) 
			if (interceptor instanceof MappedInterceptor) 
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) 
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				
			
			else 
				chain.addInterceptor(interceptor);
			
		
		return chain;
	

2.5 使用HandlerInterceptor进行拦截

从上面的代码可以看出,我们根据拦截器去判断是否匹配,匹配的话就加入到我们的HandlerExecutionChain中,这样稍后就会进行处理。

	if (!mappedHandler.applyPreHandle(processedRequest, response)) 
					return;
				
	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception 
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) 
			for (int i = 0; i < interceptors.length; i++) 
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) 
					triggerAfterCompletion(request, response, null);
					return false;
				
				this.interceptorIndex = i;
			
		
		return true;
	
mappedHandler.applyPostHandle(processedRequest, response, mv);
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
			throws Exception 

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) 
			for (int i = interceptors.length - 1; i >= 0; i--) 
				HandlerInterceptor interceptor = interceptors[i];
				interceptor.postHandle(request, response, this.handler, mv);
			
		

从代码可以看出,如果preHandle返回false,这条请求将不通过,不执行,直接返回

2.6 请求参数是如何获取的HandlerMethodArgumentResolver

public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception 

		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) 
			logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
					"' with arguments " + Arrays.toString(args));
		
		Object returnValue = doInvoke(args);
		if (logger.isTraceEnabled()) 
			logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
					"] returned [" + returnValue + "]");
		
		return returnValue;
	

这里是进行参数判断的

private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception 

		MethodParameter[] parameters = getMethodParameters();
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) 
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = resolveProvidedArgument(parameter, providedArgs);
			if (args[i] != null) 
				continue;
			
			if (this.argumentResolvers.supportsParameter(parameter)) 
				try 
					args[i] = this.argumentResolvers.resolveArgument(
							parameter, mavContainer, request, this.dataBinderFactory);
					continue;
				
				catch (Exception ex) 
					if (logger.isDebugEnabled()) 
						logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
					
					throw ex;
				
			
			if (args[i] == null) 
				throw new IllegalStateException("Could not resolve method parameter at index " +
						parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
						": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
			
		
		return args;
	

最主要的就是argumentResolvers
也就是HandlerMethodArgumentResolverComposite方法的参数校验HandlerMethodArgumentResolver
HandlerMethodArgumentResolver有两个方法,一直是否支持,一个是进行处理
写到这里,同样我们也可以模仿进行自定义扩展,
这就是针对接口编程的魅力

举两个例子PathVariableMethodArgumentResolver,也就是我们请求路径参数@PathVariable

@Override
	public boolean supportsParameter(MethodParameter parameter) 
		if (!parameter.hasParameterAnnotation(PathVariable.class)) 
			return false;
		
		if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) 
			String paramName = parameter.getParameterAnnotation(PathVariable.class).value();
			return StringUtils.hasText(paramName);
		
		return true;
	

RequestResponseBodyMethodProcessor,也即是我们

@Override
	public boolean supportsParameter(MethodParameter parameter) 
		return parameter.hasParameterAnnotation(RequestBody.class);
	

一目了然豁然开朗。

这里回顾一样,我们的请求进来,通过HandlerAdapter也就是进行参数判断,
有很多种参数HandlerMethodArgumentResolver对应不同的处理方法。支持就进行解析,不支持就报错。

2.7 请求参数是如何转化的?比如@RequestBody

PathVariableMethodArgumentResolver请求路径上的

	@SuppressWarnings("unchecked")
	protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception 
		Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
				HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
		return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
	
@Override
	@SuppressWarnings("unchecked")
	protected void handleResolvedValue(Object arg, String name, MethodParameter parameter,
			ModelAndViewContainer mavContainer, NativeWebRequest request) 

		String key = View.PATH_VARIABLES;
		int scope = RequestAttributes.SCOPE_REQUEST;
		Map<String, Object> pathVars = (Map<String, Object>) request.getAttribute(key, scope);
		if (pathVars == null) 
			pathVars = new HashMap<String, Object>();
			request.setAttribute(key, pathVars, scope);
		
		pathVars.put(name, arg);
	

也就是把请求的路径的参数获取出来,然后赋予给方法的参数值上。

同理:RequestResponseBodyMethodProcessor

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception 

		parameter = parameter.nestedIfOptional();
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
		String name = Conventions.getVariableNameForParameter(parameter);

		WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
		if (arg != null) 
			validateIfApplicable(binder, parameter);
			if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) 
				throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
			
		
		mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());

		return adaptArgumentIfNecessary(arg, parameter);
	

2.8 Jackson如何进行消息转化的,我们怎么自定义?HttpMessageConverter

从上面的Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
从代码一步步走

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException 

		MediaType contentType;
		boolean noContentType = false;
		try 
			contentType = inputMessage.getHeaders().getContentType();
		
		catch (InvalidMediaTypeException ex) 
			throw new HttpMediaTypeNotSupportedException(ex.getMessage());
		
		if (contentType == null) 
			noContentType = true;
			contentType = MediaType.APPLICATION_OCTET_STREAM;
		

		Class<?> contextClass = (parameter != null ? parameter.getContainingClass() : null);
		Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
		if (targetClass == null) 
			ResolvableType resolvableType = (parameter != null ?
					ResolvableType.forMethodParameter(parameter) : ResolvableType.forType(targetType));
			targetClass = (Class<T>) resolvableType.resolve();
		

		HttpMethod httpMethod = ((HttpRequest) inputMessage).getMethod();
		Object body = NO_VALUE;

		try 
			inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);

			for (HttpMessageConverter<?> converter : this.messageConverters) 
				Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
				if (converter instanceof GenericHttpMessageConverter) 
					GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
					if (genericConverter.canRead(targetType, contextClass, contentType)) 
						if (logger.isDebugEnabled()) 
							logger.debug("Read [" + targetType + "] as \\"" + contentType + "\\" with [" + converter + "]");
						
						if (inputMessage.getBody() != null) 
							inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
							body = genericConverter.read(targetType, contextClass, inputMessage);
							body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
						
						else 
							body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
						
						break;
					
				
				else if (targetClass != null) 
					if (converter.canRead(targetClass, contentType)) 
						if (logger.isDebugEnabled()) 
							logger.debug("Read [" + targetType + "] as \\"" + contentType + "\\" with [" + converter + "]");
						
						if (inputMessage.getBody() != null) 
							inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
							body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
							body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
						
						else 
							body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
						
						break;
					
				
			
		
		catch (IOException ex) 
			throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
		

		if (body == NO_VALUE) 
			if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
					(noContentType && inputMessage.getBody() == null)) 
				return null;
			
			throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
		

		return body;
	

这是核心代码慢慢来分析。
1.获得contentType请求类型
2.获取请求的接口以及参数类型
3.遍历messageConverters进行判断
4.判断是不是GenericHttpMessageConverter类型的消息转化器,如果不是并且参数类不为空就调用HttpMessageConverter.canRead判断能否读,
例如如果你是对象,这里是一个String转化器当然不能read。

拿Jackson来举例
class org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
这是转化类它是GenericHttpMessageConverter

	@Override
	public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) 
		if (!canRead(mediaType)) 
			return false;
		
		JavaType javaType = getJavaType(type, contextClass);
		AtomicReference<Throwable> causeRef = new AtomicReference<Throwable>();
		if (this.objectMapper.canDeserialize(javaType, causeRef)) 
			return true;
		
		logWarningIfNecessary(javaType, causeRef.get());
		return false;
	

首先判断支持的类型,application/json,然后判断这里的请求参数类是否可以用jackson进行解析

if (genericConverter.canRead(targetType, contextClass, contentType)) 
						if (logger.isDebugEnabled()) 
							logger.debug("Read [" + targetType + "] as \\"" + contentType + "\\" with [" + converter + "]");
						
						if (inputMessage.getBody() != null) 
							inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
							body = genericConverter.read(targetType, contextClass, inputMessage);
							body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
						
						else 
							body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
						
						break;
					

如果可以
1.先取出请求体
2.再用jackson读出对象

这里有两个
getAdvice().beforeBodyRead
getAdvice().afterBodyRead
这是一个扩展点,稍后讲解

@Override
	public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException 

		JavaType javaType = getJavaType(type, contextClass);
		return readJavaType(javaType, inputMessage);
	

	private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) 
		try 
			if (inputMessage instanceof MappingJacksonInputMessage) 
				Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
				if (deserializationView != null) 
					return this.objectMapper.readerWithView(deserializationView).forType(javaType).
							readValue(inputMessage.getBody());
				
			
			return this.objectMapper.readValue(inputMessage.getBody(), javaType);
		
		catch (JsonProcessingException ex) 
			throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex);
		
		catch (IOException ex) 
			throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
		
	

这里就用jackson读出了解析出了对象。

2.8 队请求体进行数据处理?例如加密解密呢?RequestBodyAdvice

我们看到用jackson进行处理的时候

inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
							body = genericConverter.read(targetType, contextClass, inputMessage);
							body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
@Override
	public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,
			Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException 

		for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) 
			if (advice.supports(parameter, targetType, converterType)) 
				request = advice.beforeBodyRead(request, parameter, targetType, converterType);
			
		
		return request;
	

	@Override
	public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType, Class<? extends HttpMessageConverter<?>> converterType) 

		for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) 
			if (advice.supports(parameter, targetType, converterType)) 
				body = advice.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
			
		
		return body;
	

你们看错就是它,RequestBodyAdvice
它有四个方法
supports:是否支持,自己做处理,比如过滤等等
handleEmptyBody:空的body如何处理
beforeBodyRead:请求体预处理
afterBodyRead:
如何做加密解密
beforeBodyRead对请求体做解密,
afterBodyRead

2.9 请求参数如何校验处理@Valid

@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception 

		parameter = parameter.nestedIfOptional();
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
		String name = Conventions.getVariableNameForParameter(parameter);

		WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
		if (arg != null) 
			validateIfApplicable(binder, parameter);
			if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) 
				throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
			
		
		mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());

		return adaptArgumentIfNecessary(arg, parameter);
	
	protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) 
		Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation ann : annotations) 
			Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
			if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) 
				Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
				Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] hints);
				binder.validate(validationHints);
				break;
			
		
	

判断如果有Valid 就进行验证,这里用的WebMvcValidator.
接下来无非就是取出所有字段,反射取出验证条件,进行判断,如果验证不过,就放入错误中。

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()) 
				mavContainer.setRequestHandled(true);
				return;
			
		
		else if (StringUtils.hasText(getResponseStatusReason())) 
			mavContainer.setRequestHandled(true);
			return;
		

		mavContainer.setRequestHandled(false);
		try 
			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;
		
	

this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
返回值处理

2.10 返回值统一处理:HandlerMethodReturnValueHandler

@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception 

		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) 
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	

	private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) 
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) 
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) 
				continue;
			
			if (handler.supportsReturnType(returnType)) 
				return handler;
			
		
		return null;
	

同样先判断是否支持
例如RequestResponseBodyMethodProcessor


	@Override
	public boolean supportsReturnType(MethodParameter returnType) 
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	

判断方法是否有ResponseBody有才用这个处理。

	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException 

		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	

writeWithMessageConverters和请求的时候类似。

1.取出响应返回值类型,
2.进行消息转化类型判断,
3.是否可以写,可以写就写,

如果我们要自定义返回响应体就可以模仿@ResponseBody自己写一个类似的,实现HandlerMethodReturnValueHandler即可。

2.11 请求体返回值统一处理:ResponseBodyAdvice

if (((GenericHttpMessageConverter) messageConverter).canWrite(
							declaredType, valueType, selectedMediaType)) 
						outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
								(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
								inputMessage, outputMessage);
						if (outputValue != null) 
							addContentDispositionHeader(inputMessage, outputMessage);
							((GenericHttpMessageConverter) messageConverter).write(
									outputValue, declaredType, selectedMediaType, outputMessage);
							if (logger.isDebugEnabled()) 
								logger.debug("Written [" + outputValue + "] as \\"" + selectedMediaType +
										"\\" using [" + messageConverter + "]");
							
						
						return;
					

和请求体统一处理一直,
这里就可以做加密处理了

响应体处理完后,直接outputstream输出到返回体中。

接下来就是其他视图的转化的,比如jsp等等。这个就不往下研究了。

springweb的核心就是sevlet,万变不离其中,就如spring生命周期一样,spring提供了很多额外的扩展给我们使用,比如拦截器,RequestBodyAdviceAdapter,ResponseBodyAdvice,消息转化器等等。
只要抓住了核心,不管它怎么变化,都是小菜一碟。

以上是关于spring设计好美:走入spring请求分发的主要内容,如果未能解决你的问题,请参考以下文章

Spring HTTP请求过程

spring源码解析系列

springmvc请求处理详解

spring cloud Alibaba 阿里微服务 flowable 工作流 自定义表单 模块设计方案

基于Spring Boot+Vue博客系统的设计与实现(附源码)

spring boot 使用redis 管理用户会员登录