Pretend to look at the source code of springmvc (four) springmvc processing method return value processing

 Springmvc processing method return value processing

 The methods in the Controller we created by ourselves in springmvc can return different return values, no matter what kind of return value, the final request will be processed correctly. In the previous view analysis article, our main focus is view analysis Find the view, merge the model data, and render the view. However, in this process, we ignored how the name of the resolved view came from, the resetFul request (responseBody) had no view, how did the request interrupt the request, and what are the different return values? The general processing is modelAndValue, with these questions, in order to explore the mystery in this in more depth, today we follow the source code and walk into the small world of returnValue.

 Overview:

    The Handler of springmvc can be the same as the request parameter. You can set it to void or return null, or return String, and the object annotated with @ResponseBody.

   Most of us know that when the return value of the method is Null, the default is to treat the request path as the view path. The String type is to parse the return value string. When redirect: or forWord: will be forwarded or redirected, otherwise it will be returned according to The path of the value String finds the corresponding view processing. If it is the object type annotated by ResponseBody, then the json will be formatted and output.

  Where is the return value processing performed in the request process? , Is executed after the handler adapter (HandlerAdapter) calls the processing method and executes it. After processing, the ModelAndView can be parsed. For subsequent rendering needs, and because the return value has different types, it needs to be processed with a general processing interface. This interface is HandlerMethodReturnValueHandler , it can universally support MethodParameter type, and then universally deal with the return value type of MethodParameter , and MethodParameter is the method, class, type, and subclass extended return value and type, so that all our processing methods and The return value is assembled into the MethodParameter and handed over to the general interface of HandlerMethodReturnValueHandler for processing. This is the core principle of the return value processing of the processor method.

1. Main execution category

1. Description of core classes

    RequestMappingHandlerAdapter: Handler adapter, request main execution class.
        
        MethodReturnValueHandler : the top-level interface of the result processing class
        
            + boolean supportsReturnType(MethodParameter returnType); // Does it support
            + void handleReturnValue(Object returnValue, MethodParameter returnType, // Process
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception; 
            
        MethodParameter : MethodParameter wraps the execution method And various method parameters
        
            ReturnValueMethodParameter : MethodParameter subclass, expand the returnValue variable, and implement paramIndex=-1,
             
        ModelAndViewContainer : package view and model, mainly used to receive the processing results of MethodReturnValueHandle.     
        
        HandlerMethodReturnValueHandlerComposite : Combine various result processing classes.
        
        Common MethodReturnValueHandler implementation
        
        1) ViewNameMethodReturnValueHandler : Used to handle the return value of String type and void type.
            + supportsReturnType // Determine whether the String type is supported
            + handleReturnValue // Determine whether it is a redeect, and set the viewNmae of the String type to ModelAndViewContainer
        
        2) ModelAndViewMethodReturnValueHandler : Used to process the ModelAndView type return value
            + supportsReturnType // Determine whether the ModelAndView return value is supported
            + handleReturnValue // Copy the data of ModelAndView to ModelAndViewContainer
            
        3) RequestResponseBodyMethodProcessor : object return value used to process responseBody annotation
            + supportsReturnType // Determine whether the class or method has ResponseBody annotation
            + handleReturnValue // Actually calling multiple HttpMessageConverter processing methods .

 2. Class diagram structure

Temporarily

Second, the execution process

        1. The main execution process is analyzed.
        DispatcherServlet's doService calls doDispatch, and the internal HandlerAdapter calls handle to process the request. Our regular default request HandlerAdapter is RequestMappingHandlerAdapter for processing. The return value processing mainly relies on HandlerMethodReturnValueHandlerComposite to assemble multiple MethodReturnValueHandlers. The initialization of this composite composite class relies on the implementation of initBean's afterPropertiesSet(), which is the same as parameter processing.

@Override
			public void afterPropertiesSet() {
				// ..... 省略
				if (this.returnValueHandlers == null) {
					List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
					this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
				}
			}
						
			getDefaultReturnValueHandlers() {
				// ..... 省略
				handlers.add(new ModelAndViewMethodReturnValueHandler());
				handlers.add(new ModelMethodProcessor());
				handlers.add(new ViewMethodReturnValueHandler());
				// ..... 省略
			}

   The role of HandlerMethodReturnValueHandlerComposite is mainly to assemble multiple return value handlers. There are two methods inside. supportsReturnType traverse all MethodParameters and return if there is a supported handler. If there is a supported handler, call the handler for processing.

  Then the Handler method of RequestMappingHandlerAdapter finally calls the invokeAndHandle method of the parent class. This method internally executes the handler and calls the HandlerMethodReturnValueHandlerComposite to find a supporting handler to call the handler for return value processing. 

   public void invokeAndHandle(ServletWebRequest webRequest,
			ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
			// handler执行方法
			Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

			if (returnValue == null) {
				if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
					mavContainer.setRequestHandled(true); // 表示请求完成,没有后续操作
					return;
				}
			}
			mavContainer.setRequestHandled(false);
			try { // 返回值处理方法
				this.returnValueHandlers.handleReturnValue(
						returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
			}
			catch (Exception ex) {
			}
		}
		
	

Let's take a look at how the most common ViewNameMethodReturnValueHandler is handled.

// 通过判断返回值类型是否是String或者void,确定这个处理器是否支持,即String类型的都能支持。
		public boolean supportsReturnType(MethodParameter returnType) {
			Class<?> paramType = returnType.getParameterType();
			return (void.class.equals(paramType) || String.class.equals(paramType));
		}
		
		// 处理String类型的返回值,主要是判断是否是rediect视图,如果是的设置标记,否则就直接将reutrnValue设置成viewName	
		public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
			if (returnValue == null) { // 如果返回值为null,那么直接返回
				return;
			}
			else if (returnValue instanceof String) {
				String viewName = (String) returnValue;
				mavContainer.setViewName(viewName);
				if (isRedirectViewName(viewName)) {
					mavContainer.setRedirectModelScenario(true);
				}
			}
		}

We are looking at ModelAndViewMethodReturnValueHandler.

// 通过返回值是否是ModelAndView或它的子类型确定是否支持。
		public boolean supportsReturnType(MethodParameter returnType) {
			return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
		}
		
		// 合并ModelAndView的属性到mavContainer
		public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

			if (returnValue == null) { // 如果返回值为null,设置请求完成,并且返回
				mavContainer.setRequestHandled(true);
				return;
			}
	
			ModelAndView mav = (ModelAndView) returnValue;
			if (mav.isReference()) { // 设置viewName
				String viewName = mav.getViewName();
				mavContainer.setViewName(viewName);
				// .... 
			} else {
				View view = mav.getView();
				mavContainer.setView(view);
				// .... 
			}
			mavContainer.addAllAttributes(mav.getModel()); // 合并model数据到mavContainer包装类中。
		}

Finally, let's take a look at the return value processing of the @ResponseBody annotation by RequestResponseBodyMethodProcessor

// 通过判断类和方法是否有ResponseBody注解,确定类型是否支持。
		public boolean supportsReturnType(MethodParameter returnType) {
			return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null ||
					returnType.getMethodAnnotation(ResponseBody.class) != null);
		}
		
		// 处理就是设置mavContainner已经处理完成,然后调用父类处理方法,原理就是根据返回值类型,和
		// 请求的accept以及设置的返回值类型,最终实际上调用的是RequestMappingHandlerAdapter添加的
		// 多个HttpMessageConverter处理。详情后面补充。
		public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException {
			mavContainer.setRequestHandled(true);
			writeWithMessageConverters(returnValue, returnType, webRequest);
		}

     After the invokeAndHandle method of RequestMappingHandlerAdapter is processed, we are equivalent to packaging the return value data in the ModelAndViewContainer object. From the above we can see that this object has a variable mavContainer.setRequestHandled(true); that is, whether the request is completed, such as the ModelAndValue return type, If it is null, then the setting request is completed. @ResponseBody is always set to the request completion. After setting this variable value, subsequent processing will interrupt the processing of the rendering view.

     At the end of the invokeAndHandle method of RequestMappingHandlerAdapter, we can see the following piece of code, that is, we call getModelAndView on the ModelAndViewContainer that has processed our return value. The second line of this method makes a judgment. If the request is completed, it will directly If null is returned, subsequent judgments will not perform process operations such as rendering the view.

		return getModelAndView(mavContainer, modelFactory, webRequest);

 

        private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

		modelFactory.updateModel(webRequest, mavContainer); 
		if (mavContainer.isRequestHandled()) { // 这里请求完成会返回null,后续判断不会进行渲染视图
			return null;
		}
        // 设置model和view到ModelAndView中
		ModelMap model = mavContainer.getModel();
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		// ....
		return mav;
	}

2. Introduction of HttpMessageConverter

    HttpMessageConverter is used to process the return value of the object method annotated by @ResponseBody. It is the List property messageConverters of RequestMappingHandlerAdapter which is initialized by the constructor and initially injected with four default processing.

public RequestMappingHandlerAdapter() {
			StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
			stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316
			this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
			this.messageConverters.add(new ByteArrayHttpMessageConverter());
			this.messageConverters.add(stringHttpMessageConverter); // 处理string,因此string返回存在乱码问题
			this.messageConverters.add(new SourceHttpMessageConverter<Source>());
			this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); // 处理json
}

MappingJackson2HttpMessageConverter is the fourth element of messageConverters.

The specific process is more complicated, involving the accept mime type, the produces (return value type) set by @RequestMapping, and other complicated processing, so I won't introduce it.

Post a custom configuration of MappingJackson2HttpMessageConverter to process ie return json to download, combined with this custom configuration, you can further understand the principle during this process.

<!--json转换器,配置解决ie返回json的下载bug-->
	<bean id="mappingJackson2HttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
		<property name="supportedMediaTypes">
			<list>
				<value>text/html;charset=UTF-8</value>
				<value>text/json;charset=UTF-8</value>
				<value>application/json;charset=UTF-8</value>
			</list>
		</property>
	</bean>
	
	<!--json转换器注入回RequestMappingHandlerAdapter.springmvc默认就支持responseBody注解 -->
	<bean
		class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
		<property name="messageConverters">
			<list>
				<ref bean="mappingJackson2HttpMessageConverter" />    <!-- JSON转换器 -->
			</list>
		</property>
	</bean>

 

Fanwai-combination mode:

The parameter analysis and return value analysis of the spring-mvc processing method uses a combination mode design mode, that is, the HandlerMethodReturnValueHandlerComposite introduced in this article . What is the combination mode? The combination mode makes the user's use of a single object and a combined object consistent, that is The composite class implements the interface of a single class and wraps multiple single classes. The following is a simple demo, as a case template of the combination mode.

/**
 * 组合模式:组合模式使得用户对单个对象和组合对象的使用具有一致性
 */
public class MyCompositive {
	
	public static void main(String[] args) {
		HanlderCompositvie compositive = new HanlderCompositvie() ;
		Hanlder hanlder1 = new IntegerHanler();
		Hanlder hanlder2 = new StringHanlder();
		compositive.addHandler(hanlder1);
		compositive.addHandler(hanlder2);
		MyClient client  = new MyClient();
		client.setHandlers(compositive);
		Object result = client.handler(1);
		System.out.println(result); // IntegerHanler1
		Object result2 = client.handler("str");
		System.out.println(result2); // StringHanlderstr
		
	}
}

class MyClient {
	HanlderCompositvie handlers = new HanlderCompositvie();
	public HanlderCompositvie getHandlers() {
		return handlers;
	}
	public void setHandlers(HanlderCompositvie handlers) {
		this.handlers = handlers;
	}
	public Object handler(Object value) {
		Class<? extends Object> type = value.getClass();
		Object result = handlers.handler(value,type);
		return result;
	}
}

class HanlderCompositvie implements Hanlder {

	private final List<Hanlder> hanlders = new ArrayList<Hanlder>();

	@Override
	public Object handler(Object vlaue,Class<?> type) {
		Hanlder hanlder = getHandler(type);
		Object returnValue = hanlder.handler(vlaue,type);
		return returnValue;
	}

	@Override
	public boolean support(Class<?> type) {
		return getHandler(type) != null;
	}

	public HanlderCompositvie addHandler(Hanlder handler) {
		hanlders.add(handler);
		return this;
	}

	private Hanlder getHandler(Class<?> type) {
		for (Hanlder hanlder : hanlders) {
			if (hanlder.support(type)) {
				return hanlder;
			}
		}
		return null;
	}

}

interface Hanlder {
	boolean support(Class<?> type);
	Object handler (Object value, Class<?> type);
}

class StringHanlder implements Hanlder {

	@Override
	public boolean support(Class<?> type) {
		return String.class.equals(type);
	}
	
	@Override
	public Object handler(Object value, Class<?> type) {
		value = (String) value;
		return "StringHanlder:"+ value;
	}
}

class IntegerHanler implements Hanlder {

	@Override
	public boolean support(Class<?> type) {
		return Integer.class.equals(type);
	}

	@Override
	public Object handler(Object value, Class<?> type) {
		value = (Integer) value;
		return "IntegerHanler:" + value;
	}

}

 

 

 

 

 

Guess you like

Origin blog.csdn.net/shuixiou1/article/details/113198944