Simule mirar el código fuente de springmvc (cuatro) método de procesamiento de springmvc procesamiento de valor de retorno

 Método de procesamiento Springmvc procesamiento de valor de retorno

 Los métodos en el Controlador que creamos nosotros mismos en springmvc pueden devolver diferentes valores de retorno, no importa qué tipo de valor de retorno, la solicitud final se procesará correctamente. En el artículo de análisis de vista anterior, nuestro enfoque principal es el análisis de vista Encontrar la vista, fusionar los datos del modelo y renderizar la vista. Sin embargo, en este proceso, ignoramos cómo vino el nombre de la vista resuelta, la solicitud resetFul (responseBody) no tenía vista, cómo la solicitud interrumpió la solicitud y cuáles son los ¿Diferentes valores de retorno? El procesamiento general es modelAndValue, con estas preguntas, para explorar el misterio en esto con más profundidad, hoy seguimos el código fuente y entramos en el pequeño mundo de returnValue.

 Visión general:

    El controlador de springmvc puede ser el mismo que el parámetro de solicitud. Puede configurarlo para anular o devolver nulo, o devolver String, y el objeto anotado con @ResponseBody.

   La mayoría de nosotros sabemos que cuando el valor de retorno del método es Nulo, el valor predeterminado es tratar la ruta de la solicitud como la ruta de la vista. El tipo de cadena es analizar la cadena de valor de retorno. Cuando se redirecciona: o forWord: se reenviará o redireccionará , de lo contrario, se devolverá de acuerdo con La ruta del valor String encuentra el procesamiento de la vista correspondiente. Si es el tipo de objeto anotado por ResponseBody, entonces el json será formateado y generado.

  ¿Dónde se realiza el procesamiento del valor devuelto en el proceso de solicitud? , Se ejecuta después de que el adaptador de controlador (HandlerAdapter) llama al método de procesamiento y lo ejecuta. Después del procesamiento, se puede analizar ModelAndView. Para las necesidades de renderización posteriores, y debido a que el valor de retorno tiene diferentes tipos, debe procesarse con una interfaz de procesamiento general. Esta interfaz es HandlerMethodReturnValueHandler , puede admitir universalmente el tipo MethodParameter y luego manejar universalmente el tipo de valor de retorno de MethodParameter , y MethodParameter es el método, clase, tipo y subclase, valor y tipo de retorno extendido, de modo que todos nuestros métodos de procesamiento y el retorno value se ensambla en MethodParameter y se entrega a la interfaz general de HandlerMethodReturnValueHandler para su procesamiento Este es el principio central del procesamiento del valor de retorno del método del procesador.

1. Categoría de ejecución principal

1. Descripción de las clases principales

    RequestMappingHandlerAdapter: Adaptador de controlador, solicita la clase de ejecución principal.
        
        MethodReturnValueHandler : la interfaz de nivel superior de la clase de tratamiento resultado
        
            + supportsReturnType booleano (MethodParameter returnType); // ¿Es compatible
            + void handleReturnValue (returnValue objeto, MethodParameter returnType, // Proceso
            ModelAndViewContainer mavContainer, NativeWebRequest WebRequest) lanza excepción; 
            
        MethodParameter : MethodParameter envuelve el método de ejecución y varios parámetros del método
        
            ReturnValueMethodParameter : subclase MethodParameter, expande la variable returnValue e implementa paramIndex = -1,
             
        ModelAndViewContainer : vista de paquete y modelo, utilizado principalmente para recibir los resultados de procesamiento de MethodReturnValueHandle.     
        
        HandlerMethodReturnValueHandlerComposite : combina varias clases de procesamiento de resultados.
        
        Implementación de Common MethodReturnValueHandler
        
        1) ViewNameMethodReturnValueHandler : se utiliza para manejar el valor de retorno del tipo String y el tipo void.
            + supportsReturnType // Determinar si el tipo String es compatible
            + handleReturnValue // Determinar si es un canje y establecer la viewNmae del tipo String en ModelAndViewContainer
        
        2) ModelAndViewMethodReturnValueHandler : Se utiliza para procesar el valor de retorno del tipo ModelAndView
            + supportsReturnType // Determinar si el valor de retorno ModelAndView es compatible
            + handleReturnValue // Copie los datos de ModelAndView en ModelAndViewContainer
            
        3) RequestResponseBodyMethodProcessor : valor de retorno del objeto utilizado para procesar la anotación responseBody
            + supportsReturnType // Determine si la clase o método tiene anotación ResponseBody
            + handleReturnValuettp métodos de procesamiento.

 2. Estructura del diagrama de clases

Temporalmente

En segundo lugar, el proceso de ejecución

        1. Se analiza el proceso de ejecución principal.
        DoService de DispatcherServlet llama a doDispatch, y las llamadas internas de HandlerAdapter manejan para procesar la solicitud. Nuestra solicitud estándar por defecto HandlerAdapter es RequestMappingHandlerAdapter para su procesamiento. El procesamiento del valor de retorno se basa principalmente en HandlerMethodReturnValueHandlerComposite para ensamblar múltiples MethodReturnValueHandlers. La inicialización de esta clase compuesta compuesta se basa en la implementación de initBean's afterPropertiesSet (), que es lo mismo que el procesamiento de parámetros.

@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());
				// ..... 省略
			}

   El rol de HandlerMethodReturnValueHandlerComposite es principalmente ensamblar múltiples manejadores de valor de retorno. Hay dos métodos dentro. SupportsReturnType atraviesa todos los MethodParameters y regresa si hay un manejador compatible. Si hay un manejador compatible, llama al manejador para procesar.

  Luego, el método Handler de RequestMappingHandlerAdapter finalmente llama al método invokeAndHandle de la clase principal. Este método ejecuta internamente el controlador y llama al HandlerMethodReturnValueHandlerComposite para encontrar un controlador de apoyo para llamar al controlador para el procesamiento del valor de retorno. 

   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) {
			}
		}
		
	

Echemos un vistazo a cómo se maneja el ViewNameMethodReturnValueHandler más común.

// 通过判断返回值类型是否是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);
				}
			}
		}

Estamos viendo 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包装类中。
		}

Finalmente, echemos un vistazo al procesamiento del valor de retorno de la anotación @ResponseBody por 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);
		}

     Después de que se procesa el método invokeAndHandle de RequestMappingHandlerAdapter, somos equivalentes a empaquetar los datos del valor de retorno en el objeto ModelAndViewContainer. De lo anterior podemos ver que este objeto tiene una variable mavContainer.setRequestHandled (verdadero); es decir, si la solicitud está completa , como el tipo de retorno ModelAndValue, si es nulo, la solicitud de configuración se completa. @ResponseBody siempre se establece en la finalización de la solicitud. Después de establecer este valor de variable, el procesamiento posterior interrumpirá el procesamiento de la vista de representación.

     Al final del método invokeAndHandle de RequestMappingHandlerAdapter, podemos ver el siguiente fragmento de código, es decir, llamamos a getModelAndView en el ModelAndViewContainer que ha procesado nuestro valor de retorno. La segunda línea de este método hace un juicio. Si la solicitud se completa , lo hará directamente. Si se devuelve null, los juicios posteriores no realizarán operaciones de proceso como la representación de la vista.

		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. Introducción de HttpMessageConverter

    HttpMessageConverter se usa para procesar el valor de retorno del método de objeto anotado por @ResponseBody.Es la propiedad List messageConverters de RequestMappingHandlerAdapter que es inicializada por el constructor e inicialmente inyectada con cuatro procesamientos predeterminados.

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 es el cuarto elemento de messageConverters.

El proceso específico es más complicado, involucra el tipo de aceptación mime, el produce (tipo de valor de retorno) establecido por @RequestMapping y otro procesamiento complicado, por lo que no lo presentaré.

Publique una configuración personalizada de MappingJackson2HttpMessageConverter para procesar, es decir, devolver json para descargar, combinado con esta configuración personalizada, puede comprender mejor el principio durante este proceso.

<!--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>

 

Modo de combinación Fanwai:

El análisis de parámetros y el análisis del valor de retorno del método de procesamiento spring-mvc utilizan un modo de diseño de modo de combinación, es decir, el HandlerMethodReturnValueHandlerComposite presentado en este artículo . ¿Qué es el modo de combinación? El modo de combinación hace que el usuario use un solo objeto y un Objeto consistente, es decir, la clase compuesta implementa la interfaz de una sola clase y envuelve múltiples clases únicas. La siguiente es una demostración simple, como plantilla de caso del modo de combinación.

/**
 * 组合模式:组合模式使得用户对单个对象和组合对象的使用具有一致性
 */
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;
	}

}

 

 

 

 

 

Supongo que te gusta

Origin blog.csdn.net/shuixiou1/article/details/113198944
Recomendado
Clasificación