# # # ServletInvocableHandlerMethod inheritance structure

As shown in figure: ServletInvocableHandlerMethod compared is also a kind of HandlerMethod HandlerMethod increased the argument parsing, the return value processing, and other functions, the most important thing is that a new method of executive function

Let’s examine each of these components in turn


###HandlerMethod

HandlerMethod is used in the HandlerMapping component to encapsulate the Handler and the Method used to process the requestCopy the code

Attributes of the HandlerMethod:

// Handler
private final Object bean;
// When the Handler passed in is of type String, we need to use the beanFactory to fetch the bean
private final BeanFactory beanFactory;
/ / bean type
private finalClass<? > beanType;// method
private final Method method;
// If method is bridge method set to old method, otherwise set to method
private final Method bridgedMethod;
// The parameters of the method to process the request
private final MethodParameter[] parameters;

private final HandlerMethod resolvedFromHandlerMethod;
Copy the code
Note that all properties of the HandlerMethod are final. If the Handler is String, you need to look up the bean from the container to change the bean property to the bean in the container using the createWithResolvedBean method Create a new HandlerMethod using the bean found in the container and the original properties of the HandlerMethodCopy the code

CreateWithResolvedBean source code:

public HandlerMethod createWithResolvedBean(a) {
	Object handler = this.bean;
	// Handler is a String that gets the Bean from the container
	if (this.bean instanceof String) {
		String beanName = (String) this.bean;
		handler = this.beanFactory.getBean(beanName);
	}
	// Create a new HandlerMethod using the current HandlerMethod and handler
	return new HandlerMethod(this, handler);
}

private HandlerMethod(HandlerMethod handlerMethod, Object handler) {
	Assert.notNull(handlerMethod, "HandlerMethod is required");
	Assert.notNull(handler, "Handler object is required");
	this.bean = handler;
	this.beanFactory = handlerMethod.beanFactory;
	this.beanType = handlerMethod.beanType;
	this.method = handlerMethod.method;
	this.bridgedMethod = handlerMethod.bridgedMethod;
	this.parameters = handlerMethod.parameters;
	this.resolvedFromHandlerMethod = handlerMethod;
}
Copy the code
Here, the parameters are of type MethodParameter[], so let's look at this typeCopy the code

###MethodParameter

MethodParameter

// The method of the parameter
private final Method method;
// How to make a parameterprivate final Constructor<? >constructor;
// The parameter needs to start from 0
private final int parameterIndex;
// Level of nesting
private int nestingLevel = 1;
// Save the serial number of each nested parameter
Map<Integer, Integer> typeIndexesPerLevel;
// The container type, the class of the method to which the parameter belongsprivate volatile Class<? > containingClass;// Parameter typeprivate volatile Class<? > parameterType;// Type Specifies the parameter Type
private volatile Type genericParameterType;
// Parameter annotations
private volatile Annotation[] parameterAnnotations;
// Parameter name finder
private volatile ParameterNameDiscoverer parameterNameDiscoverer;
// Parameter name
private volatile String parameterName;

private volatile MethodParameter nestedMethodParameter;
Copy the code
ParameterNameDiscoverer is a parameterNameDiscoverer component that can be used to find the name of a parameter HandlerMothod defines two inner classes to encapsulate parameters: HandlerMethodParameter encapsulation method invocation parameters ReturnValueMethodParameter encapsulation method returns the ReturnValueMethodParameter inherited from HandlerMethodParameter They create methodParameters primarily with method and parameterIndex. The method they use is both bridgedMethod and parameterIndex is -1 for the return valueCopy the code

HandlerMethodParameter source code:

protected class HandlerMethodParameter extends SynthesizingMethodParameter {

	public HandlerMethodParameter(int index) {
		super(HandlerMethod.this.bridgedMethod, index);
	}

	protected HandlerMethodParameter(HandlerMethodParameter original) {
		super(original);
	}

	@Override
	publicClass<? > getContainingClass() {return HandlerMethod.this.getBeanType();
	}

	@Override
	public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
		return HandlerMethod.this.getMethodAnnotation(annotationType);
	}

	@Override
	public <T extends Annotation> boolean hasMethodAnnotation(Class<T> annotationType) {
		return HandlerMethod.this.hasMethodAnnotation(annotationType);
	}

	@Override
	public HandlerMethodParameter clone(a) {
		return new HandlerMethodParameter(this); }}Copy the code

ReturnValueMethodParameter source code:

private class ReturnValueMethodParameter extends HandlerMethodParameter {

	private final Object returnValue;

	public ReturnValueMethodParameter(Object returnValue) {
		super(-1);
		this.returnValue = returnValue;
	}

	protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
		super(original);
		this.returnValue = original.returnValue;
	}

	@Override
	publicClass<? > getParameterType() {return (this.returnValue ! =null ? this.returnValue.getClass() : super.getParameterType());
	}

	@Override
	public ReturnValueMethodParameter clone(a) {
		return new ReturnValueMethodParameter(this); }}Copy the code

###InvocableHandlerMethod

InvocableHandlerMethod inherits from The HandlerMethod. InvocableHandlerMethod can call the corresponding method of the inner method directlyCopy the code

Three attributes have been added to the InvocableHandlerMethod

private WebDataBinderFactory dataBinderFactory;
private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
Copy the code
DataBinderFactory The WebDataBinderFactory type that can be created to use the WebDataBinder in the ArgumentSolver argumentResolvers HandlerMethodArgumentResolverComposite type Used to resolve parameter parameterNameDiscoverer parameterNameDiscoverer type Used to get the parameter name, used in MethodParameterCopy the code

The invocation of Method in InvocableHandlerMethod is invokeForRequest

InvokeForRequest source code:

public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
	
	// Get method parameters
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"' with arguments " + Arrays.toString(args));
	}
	/ / call the Method
	Object returnValue = doInvoke(args);
	if (logger.isTraceEnabled()) {
		logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"] returned [" + returnValue + "]");
	}
	return returnValue;
}
Copy the code
As you can see, the doInvoke method is the one that actually executes the request. DoInvoke is the most central method in the entire HandlerMethod family of processorsCopy the code

DoInvoke source code:

protected Object doInvoke(Object... args) throws Exception {
	// Force methods to be callable
	ReflectionUtils.makeAccessible(getBridgedMethod());
	try {
		/ / execution
		return getBridgedMethod().invoke(getBean(), args);
	}
	catch(IllegalArgumentException ex) { assertTargetBean(getBridgedMethod(), getBean(), args); String text = (ex.getMessage() ! =null ? ex.getMessage() : "Illegal argument");
		throw new IllegalStateException(getInvocationErrorMessage(text, args), ex);
	}
	catch (InvocationTargetException ex) {
		// Unwrap for HandlerExceptionResolvers ...
		Throwable targetException = ex.getTargetException();
		if (targetException instanceof RuntimeException) {
			throw (RuntimeException) targetException;
		}
		else if (targetException instanceof Error) {
			throw (Error) targetException;
		}
		else if (targetException instanceof Exception) {
			throw (Exception) targetException;
		}
		else {
			String text = getInvocationErrorMessage("Failed to invoke handler method", args);
			throw newIllegalStateException(text, targetException); }}}Copy the code
Except for exception handling, the core sentence is getBridgedMethod().invoke(getBean(), args); Real execution method is direct call bridgedMethod invoke method call before using ReflectionUtils. MakeAccessible methods into can call methods Timely order handler method is private can also be called hereCopy the code

GetMethodArgumentValues source code:

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

	// Get method parameters
	MethodParameter[] parameters = getMethodParameters();
	// Save the parsed parameter values
	Object[] args = new Object[parameters.length];
	for (int i = 0; i < parameters.length; i++) {
		MethodParameter parameter = parameters[i];
		// Set parameterNameDiscoverer to parameter
		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
		// If the parameters of the response type are already provided in providedArgs, set them directly to parameter
		args[i] = resolveProvidedArgument(parameter, providedArgs);
		if(args[i] ! =null) {
			continue;
		}
		// Use argumentResolvers to resolve the arguments
		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);
				}
				throwex; }}// If no argument is parsed, an exception is thrown
		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;
}
Copy the code
1. Call the parent getMethodParameters Method to get all the parameters of Method 2. Define an array of Object type args to store the parsed parameter value 3. 1) find 2) used in providedArgs argumentResolvers parsing note: call in RequestMappingHandlerAdapter does not provide providedArgs Therefore, only argumentResolvers can be used for resolution. 4 Set parameterNameDiscoverer to get the parameter name ParameterNameDiscoverer can be defined in RequestMappingHandlerAdapter configuration using the default DefaultParameterNameDiscovererCopy the code

The InvocableHandlerMethod is an extension of the HandlerMetod method that allows you to parse parameters

Methods annotated with @initBinder and @ModeAttribute are wrapped in an InvocableHandlerMethod object and executed directly


###ServletInvocableHandlerMethod

ServletInvocableHandlerMethod inherited from InvocableHandlerMethod on the basis of the parent class to add the three functions: 1, 2 to @ ResponseStatus annotation spending, the return value of processing 3, the processing of the result of the asynchronous processingCopy the code

@ ResponseStatus annotations

The @responseStatus annotation is used on a handler method or return value to set the Status that returns a Response. It takes two arguments :value(HttpStatus type) and Reason (String type, null by default) When a method annotates @responseStatus, the Status returned uses the Status in the comment. If the handler returns a null value or reason is not null, interrupt processing is returned.Copy the code

Return value processing

For the processing of the return value is the use of returnValueHandlers properties, returnValueHandlers is HandlerMethodReturnValueHandler typeCopy the code

Asynchronous processing is not done for the time being


###invokeAndHandle

ServletInvocableHandlerMethod request is processed using the invokeAndHandle method (note, not invokeAndHandler, no r)Copy the code

InvokeAndHandle source code:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

	// Invoke the parent class invokeForRequest to execute the request
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	// Handle @responseStatus annotation
	setResponseStatus(webRequest);
	// Process the return value
	if (returnValue == null) {// Check whether the return value is null
		// Request NotModified is true
		/ / @ ResponseStatus
		// RequestHandled=true      
		// If one of the three conditions is true, the request is processed and returned
		if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
			mavContainer.setRequestHandled(true);
			return; }}// Return value is not null, @responseStatus has reason
	// The setup request is processed and returned
	else if (StringUtils.hasText(this.responseReason)) {
		mavContainer.setRequestHandled(true);
		return;
	}

	// Set RequestHandled=false to complete the request
	// Use returnValueHandlers to handle returned values
	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);
		}
		throwex; }}Copy the code

setResponseStatus:

private void setResponseStatus(ServletWebRequest webRequest) throws IOException {

	if (this.responseStatus == null) {
		return;
	}
	if (StringUtils.hasText(this.responseReason)) {
		webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
	}
	else {
		webRequest.getResponse().setStatus(this.responseStatus.value());
	}
	// Set to request properties for use in redirect
	webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
}
Copy the code