Spring MVC 第二篇 - 关于后端处理器 HandlerMethod

目录

前言

HandlerMethod

ServletInvocableHandlerMethod

InvocableHandlerMethod

总结


前言

        通过前一篇文章,我们知道了 Spring MVC 执行 Controller 方法的大致流程,其中重要的一步就是找到合适的后端处理器handler),通过 handler 来执行目标方法,而我们常用的 @RequestMapping 标注的方法会对应的 handler 类型为 HandlerMethod,本文就来探究 HandlerMethod 到底是什么东西。

HandlerMethod

public class HandlerMethod {

	/** Logger that is available to subclasses. */
	protected final Log logger = LogFactory.getLog(getClass());
    
    // 对应标注了 @Controller 的那个 bean,可以是对象实例也可以是一个 bean 名称
	private final Object bean;

    // bean 容器
	@Nullable
	private final BeanFactory beanFactory;

    // bean 的类型
	private final Class<?> beanType;

    // 目标方法
	private final Method method;

    // 被桥接的方法,不过一般都跟 method 这个字段的值一样,目的也就是为了找到要执行的目标方法
	private final Method bridgedMethod;

    // 封装的参数信息
	private final MethodParameter[] parameters;

    // 响应码状态
	@Nullable
	private HttpStatus responseStatus;

    // 响应码对应的原因
	@Nullable
	private String responseStatusReason;

    // 通过下面 createWithResolvedBean 获取 HandlerMethod 的时候,会把 this 对象传递来
	@Nullable
	private HandlerMethod resolvedFromHandlerMethod;


	// 构造函数,给上述几个字段赋值
	public HandlerMethod(Object bean, Method method) {
		Assert.notNull(bean, "Bean is required");
		Assert.notNull(method, "Method is required");
		this.bean = bean;
		this.beanFactory = null;
		this.beanType = ClassUtils.getUserClass(bean);
		this.method = method;
		this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
		this.parameters = initMethodParameters();
		evaluateResponseStatus();
		this.description = initDescription(this.beanType, this.method);
	}

	// 构造方法
	public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
		... 省略
	}

	// 构造方法
	public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
		... 省略
	}

	// 通过 handlerMethod 复制出来一个新的 HandlerMothod, 在上篇文章中通过 HandlerMothod 类型的 Handler 来创建一个新的 ServletInvocableHandlerMethod 类型的时候,就是通过调用这个构造方法
	protected HandlerMethod(HandlerMethod handlerMethod) {
		Assert.notNull(handlerMethod, "HandlerMethod is required");
		this.bean = handlerMethod.bean;
		this.beanFactory = handlerMethod.beanFactory;
		this.beanType = handlerMethod.beanType;
		this.method = handlerMethod.method;
		this.bridgedMethod = handlerMethod.bridgedMethod;
		this.parameters = handlerMethod.parameters;
		this.responseStatus = handlerMethod.responseStatus;
		this.responseStatusReason = handlerMethod.responseStatusReason;
		this.description = handlerMethod.description;
		this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod;
	}

	// 构造方法
	private HandlerMethod(HandlerMethod handlerMethod, Object handler) {
		... 省略
	}
    
    // 针对方法的参数来生成参数的包装类
	private MethodParameter[] initMethodParameters() {
		int count = this.bridgedMethod.getParameterCount();
		MethodParameter[] result = new MethodParameter[count];
		for (int i = 0; i < count; i++) {
			result[i] = new HandlerMethodParameter(i);
		}
		return result;
	}

    // 如果方法上标注了 @ResponseStatus, 会返回该注解中的响应码和响应原因
	private void evaluateResponseStatus() {
		ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
		if (annotation == null) {
			annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
		}
		if (annotation != null) {
			this.responseStatus = annotation.code();
			this.responseStatusReason = annotation.reason();
		}
	}

	
    //...省略一些 get 方法

	// 判断该方法的返回值是否是 void
	public boolean isVoid() {
		return Void.TYPE.equals(getReturnType().getParameterType());
	}

	// 获取方法上的注解
	@Nullable
	public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) {
		return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType);
	}
	public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) {
		return AnnotatedElementUtils.hasAnnotation(this.method, annotationType);
	}

	// 通过 bean 来创建 HandlerMethod,如果此时bean还没有被创建出来,会调用 getBean 方法先创建 bean 实例,此外还会将 this 对象赋值给 resolvedFromHandlerMethod
	public HandlerMethod createWithResolvedBean() {
		Object handler = this.bean;
		if (this.bean instanceof String) {
			Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
			String beanName = (String) this.bean;
			handler = this.beanFactory.getBean(beanName);
		}
		return new HandlerMethod(this, handler);
	}

	// 类似 toString 方法
	public String getShortLogMessage() {
		return getBeanType().getName() + "#" + this.method.getName() +
				"[" + this.method.getParameterCount() + " args]";
	}

	// 返回值的类型
	private class ReturnValueMethodParameter extends HandlerMethodParameter {

		@Nullable
		private final Object returnValue;

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

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

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

		@Override
		public ReturnValueMethodParameter clone() {
			return new ReturnValueMethodParameter(this);
		}
	}

}

        可以看到 HandlerMethod 保存了 bean 的信息,也就是 Controller 的信息,由此可知是哪个对象,然后 Method 保存了对应的方法信息,可以知道要调用哪个方法,所以一个 @Controller 有多少个 @RequestMapping 标注的方法,就会有多少个 HandlerMethod。可是感觉这有点像一个实体类一样,并没有什么执行方法,虽然知道了 bean 信息和方法信息,但是通过这个类并没有办法来触发方法的执行,这个时候我们可以看下前面的文章,上一篇文章中分析 Controller 方法的时候是怎么执行的呢?我们应该留意到有一步是把 HandlerMethod 类型的实例转化为了 ServletInvocableHandlerMethod 类型的实例。所以下面继续分析一下这个类型又是干嘛的。

ServletInvocableHandlerMethod

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    
    // 发生异常时的回调方法
	private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call");

    // 用于处理方法的返回值
	@Nullable
	private HandlerMethodReturnValueHandlerComposite returnValueHandlers;


	// 构造函数
	public ServletInvocableHandlerMethod(Object handler, Method method) {
		super(handler, method);
	}
	public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
		super(handlerMethod);
	}

    // 设置处理返回值的 HandlerMethodReturnValueHandler
	public void setHandlerMethodReturnValueHandlers(HandlerMethodReturnValueHandlerComposite returnValueHandlers) {
		this.returnValueHandlers = returnValueHandlers;
	}

	// 通过该方法来触发目标方法的执行
	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()) {
				disableContentCachingIfNecessary(webRequest);
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}
        // 设置该标识代表请求未处理完成
		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
            // 处理目标方法的返回值
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}

	// 设置响应状态
	private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
		HttpStatus status = getResponseStatus();
		if (status == null) {
			return;
		}

		HttpServletResponse response = webRequest.getResponse();
		if (response != null) {
			String reason = getResponseStatusReason();
			if (StringUtils.hasText(reason)) {
				response.sendError(status.value(), reason);
			}
			else {
				response.setStatus(status.value());
			}
		}

		// To be picked up by RedirectView
		webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
	}

	// 请求是否被修改
	private boolean isRequestNotModified(ServletWebRequest webRequest) {
		return webRequest.isNotModified();
	}

	private void disableContentCachingIfNecessary(ServletWebRequest webRequest) {
		if (isRequestNotModified(webRequest)) {
			HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
			Assert.notNull(response, "Expected HttpServletResponse");
			if (StringUtils.hasText(response.getHeader(HttpHeaders.ETAG))) {
				HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
				Assert.notNull(request, "Expected HttpServletRequest");
			}
		}
	}

	private String formatErrorForReturnValue(@Nullable Object returnValue) {
		return "Error handling return value=[" + returnValue + "]" +
				(returnValue != null ? ", type=" + returnValue.getClass().getName() : "") +
				" in " + toString();
	}

	// 处理异步情形下需要获取到的 ServletInvocableHandlerMethod, 这是一个内部类
	ServletInvocableHandlerMethod wrapConcurrentResult(Object result) {
		return new ConcurrentResultHandlerMethod(result, new ConcurrentResultMethodParameter(result));
	}


	// 继承该类本身,处理异步的情形
	private class ConcurrentResultHandlerMethod extends ServletInvocableHandlerMethod {
        //... 省略代码
    }

}

          从上面可以看出,该类是 InvocableHandlerMethod 的字类,且提供了 invokeAndHandle 方法,盲猜就是为了补充 HandlerMethod 没有调用目标方法的功能,所以重点可以放在该方法上,可以该方法中通过 invokeForRequest 去调用目标方法,而 invokeForRequest 是它的父类 InvocableHandlerMethod 才有的方法,所以还需要研究下 InvocableHandlerMethod。

InvocableHandlerMethod

public class InvocableHandlerMethod extends HandlerMethod {

	private static final Object[] EMPTY_ARGS = new Object[0];


	@Nullable
	private WebDataBinderFactory dataBinderFactory;

    // 用来解析请求参数
	private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();

    // 用来获取形参名
	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    // 构造函数
	public InvocableHandlerMethod(HandlerMethod handlerMethod) {
		super(handlerMethod);
	}
	public InvocableHandlerMethod(Object bean, Method method) {
		super(bean, method);
	}
	public InvocableHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
			throws NoSuchMethodException {

		super(bean, methodName, parameterTypes);
	}

    //.. 省略 set 方法

	// 获取参数值,然后执行目标方法
	@Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        // 获取参数值
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
        // 传入参数调用目标方法
		return doInvoke(args);
	}

	// 获取参数值
	protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}

		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				// Leave stack trace for later, exception may actually be resolved and handled...
				if (logger.isDebugEnabled()) {
					String exMsg = ex.getMessage();
					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, exMsg));
					}
				}
				throw ex;
			}
		}
		return args;
	}

	// 执行目标方法
	@Nullable
	protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
            // 父类的 getBridgedMethod 获取到目标方法,通过反射执行
			return getBridgedMethod().invoke(getBean(), args);
		}
		catch (IllegalArgumentException ex) {
			assertTargetBean(getBridgedMethod(), getBean(), args);
			String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
			throw new IllegalStateException(formatInvokeError(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 {
				throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
			}
		}
	}

}

        该类是 ServletInvocableHandlerMethod 的父类,同时又是 HandlerMethod 的字类,该类可以方法的请求参数值,也可以获取到父类的 bean 和目标方法,然后给其传入参数通过反射来执行目标方法。

总结

        从上面的分析可以知道 HandlerMethod 保存了 Controller 对象和目标方法,但是却没有给出执行目标方法的接口。InvocableHandlerMethod  作为 HandlerMethod 字类对其进行了增强,增加了解析请求参数值和执行目标方法的功能,但是它好像跟 Servlet 没有什么关系,没法跟 HTTP 联系起来,所以又有了 ServletInvocableHandlerMethod, 它是对 InvocableHandlerMethod 的进一步增强。ServletInvocableHandlerMethod 借助父类就有了执行目标方法的功能,此外添加了对 @ResponseStatus 的支持,通过 HandlerMethodReturnValueHandlerComposite 来继续操作返回值以及对异步结果的处理,用来跟 Servlet 的 API 对接。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring MVC中配置Multi-part解析器需要以下几个步骤: 1. 添加Maven依赖 ```xml <!-- Spring MVC Multi-part Dependency --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> ``` 2. 配置Multi-part解析器 在Spring配置文件中添加如下配置: ```xml <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="1000000" /> </bean> ``` 在这个示例中,我们使用了CommonsMultipartResolver作为Multi-part解析器,并设置了最大上传文件大小为1MB。 3. 在控制器中使用Multi-part解析器 你可以在控制器中通过@RequestParam注解获取上传的文件,例如: ```java @RequestMapping(value = "/uploadFile", method = RequestMethod.POST) public String uploadFile(@RequestParam("file") MultipartFile file) { // 处理上传的文件 return "redirect:/"; } ``` 在这个示例代码中,我们使用@RequestParam注解将上传的文件绑定到MultipartFile对象上。你可以在控制器中使用MultipartFile对象处理上传的文件。 注意,如果你使用的是Spring Boot,Multi-part解析器已经默认配置好了,你只需要在控制器中使用即可。例如: ```java @PostMapping("/upload") public String handleFileUpload(@RequestParam("file") MultipartFile file) { // 处理上传的文件 return "redirect:/"; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值