springboot-方法处理1-方法处理器

1.问题

接上回研究全局异常处理器的过程中,发现spring是通过参数解析器(HandlerMethodArgumentResolver)解析全局异常处理器的参数的。本文稍微研究一下参数解析器;

2.调用过程

调用全局异常处理方法的过程分为如下三步。本文主要研究方法处理器(HandlerMethod),后续两篇研究参数解析器(HandlerMethodArgumentResolver)和返回值处理器(HandlerMethodReturnValueHandler)

  • 创建方法处理器(HandlerMethod),解析方法的参数,并处理返回结果;
  • 方法处理器委托参数解析器(HandlerMethodArgumentResolver)解析方法的参数;
  • 方法处理器委托返回值处理器(HandlerMethodReturnValueHandler)处理方法的返回结果;

3.使用示例(重点)

在全局全局异常处理器中使用各种类型的参数;

3.1.默认参数解析器

  • request由ServletRequestMethodArgumentResolver解析,默认可以使用;
  • @RequestParam由RequestParamMapMethodArgumentResolver解析,默认不可以使用。需要通过WebMvcConfigurationSupport的addArgumentResolvers方法添加;
/**
 * 测试全局异常处理器定义参数
 */
@RestControllerAdvice
public class GoldjetInterfacePlatformExceptionHander extends AppExceptionHandler {
    
    //**request由ServletRequestMethodArgumentResolver解析,默认可以使用
    //**@RequestParam由RequestParamMapMethodArgumentResolver解析,默认不可以使用,需要手动添加
    @ExceptionHandler({MyException.class})
    public ResultDTO handleRRException(AppException e, ServletRequest request, @RequestParam Map map) {
        return ResultDTO.error(e.getCode(), e.getMsg());
    }
}

/**
 * 测试全局异常处理器添加参数解析器
 * spring的大部分配置都是由WebMvcConfigurationSupport处理。重写此类,应用自定义的配置
 */
@Configuration
public class MyConfig extends WebMvcConfigurationSupport {

    //**全局异常处理器添加@RequestParam参数解析器
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        val resolver = new RequestParamMapMethodArgumentResolver();
        argumentResolvers.add(resolver);
    }
}

全局异常处理器获取参数

3.2.自定义参数解析器

  • 研究了一下参数解析器(HandlerMethodArgumentResolver)后,发现实现WebArgumentResolver接口是实现自定义参数解析器更好的选择,具体可以看看下一篇;
  • 本文实现功能:在全局异常处理方法中,如果参数是自定义类(Test)则调用spring的service组件,并获取Test对象的功能;

A.全局异常处理器

增加一个自定义参数类型(Test);

//**全局异常处理器
@RestControllerAdvice
class MyExceptionHandler {

    @ExceptionHandler({MyException.class})
    public Map handleMyException(MyException e, ServletRequest request, @RequestParam Map map, Test test) {
        val rst =  new HashMap<>();
            rst.put("code", "500");
            rst.put("msg", e.getMessage());
            rst.put("request", request.getServerName());
            rst.put("map", map);
            rst.put("test", test);

        return rst;
    }
}

B.自定义WebArgumentResolver

如果目标参数类型为Test,则调用TestService获取Test对象;

/**
 * 自定义WebArgumentResolver
 * WebArgumentResolver是一个函数式接口,是spring预留用于解析用户自定义参数类型的SPI。
 * 使用该接口可以很方便的自定义参数解析器,解析自定义的参数类型。
 * 我们应该优先考虑该接口扩展自定义参数解析器;
 *
 * 本文实现调用其它spring组件,获取内存中数据的功能
 */
public class MyWebArgumentResolver implements WebArgumentResolver {

    //**如果目标参数类型为Test,则调用TestService获取Test对象
    @Nullable
    @Override
    public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception {
        if(methodParameter.getParameterType().equals(Test.class)) {
            TestService service = (TestService)ApplicationContextUtil.getBean("testService");
            return service.getTest(methodParameter.getParameterName());
        }

        return null;
    }
}

//**自定类型
@Data
class Test {
    private String name;
    private int age;
}

@Slf4j
@Service
class TestService {

    //**自定义参数解析器调用该方法,获取Test实例
    public Test getTest(String parameterName) {
        log.info("参数名:{}", parameterName);

        val rst =  new Test();
            rst.setName("张三");
            rst.setAge(33);

        return rst;
    }
}

C.注册MyWebArgumentResolver

ServletWebArgumentResolverAdapter和自定义WebArgumentResolver配合使用,实现自定义参数解析;

/**
 * 测试全局异常处理器添加参数解析器
 * spring的大部分配置都是由WebMvcConfigurationSupport处理。重写此类,应用自定义的配置
 */
@Configuration
public class MyConfig extends WebMvcConfigurationSupport {

    //**全局异常处理器添加@RequestParam参数解析器
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        //**解析目标方法@RequestParam标注的参数
        val resolver1 = new RequestParamMapMethodArgumentResolver();

        //**自定义WebArgumentResolver
        val myResolver  = new MyWebArgumentResolver();
        //**ServletWebArgumentResolverAdapter和自定义WebArgumentResolver配合使用,实现自定义参数解析
        val resolver2 = new ServletWebArgumentResolverAdapter(myResolver);

        argumentResolvers.add(resolver1);
        argumentResolvers.add(resolver2);
    }
}

自定义参数处理器解析参数

4.方法处理器简介

4.1.类结构

方法处理器结构

4.2.运行过程

本文以调用全局异常处理器的方法为例,讲解调用过程(参数解析器和返回值处理器都使用组合模式):

  • ExceptionHandlerExceptionResolver异常处理器为异常处理方法创建方法处理器ServletInvocableHandlerMethod(HandlerMethod的子类),并为方法处理器设置参数解析器(HandlerMethodArgumentResolver)和返回值处理器(HandlerMethodReturnValueHandler);
  • 调用ServletInvocableHandlerMethod的invokeAndHandle();
  • invokeAndHandle()调用组合的参数解析器(HandlerMethodArgumentResolverComposite)解析异常处理方法的参数;
  • invokeAndHandle()调用异常处理方法并获取返回值;
  • invokeAndHandle()把http响应状态写入到响应中(如果异常处理方法配置了@ResponseStatus注解);
  • invokeAndHandle()调用组合返回值处理器(HandlerMethodReturnValueHandlerComposite)处理返回结果;

5.方法处理器源码

5.1.HandlerMethod方法处理器

spring对方法的封装,包含方法及所属的bean,参数,参数的注解等;

//**方法处理器(spring对方法的封装,包含方法及所属的bean,参数,参数的注解等)
public class HandlerMethod {
    //**方法所在的bean,可以是bean对象/bean名称
	private final Object bean;
    //**如果是bean名称,使用beanFactory获取bean实例
	@Nullable
	private final BeanFactory beanFactory;
    //**bean的类对象
	private final Class<?> beanType;
    //**方法
	private final Method method;
    //**桥接方法,如果是普通的java方法,则和上面一样
	private final Method bridgedMethod;
    //**方法参数
	private final MethodParameter[] parameters;
    //**响应状态
	@Nullable
	private HttpStatus responseStatus;
    //**响应状态描述(异常状态原因)
	@Nullable
	private String responseStatusReason;
    
	@Nullable
	private HandlerMethod resolvedFromHandlerMethod;
    //**该方法实现的接口抽象方法中的参数注解
	@Nullable
	private volatile List<Annotation[][]> interfaceParameterAnnotations;

	private MethodParameter[] initMethodParameters() {
		int count = this.bridgedMethod.getParameterCount();
		MethodParameter[] result = new MethodParameter[count];
		for (int i = 0; i < count; i++) {
			HandlerMethodParameter parameter = new HandlerMethodParameter(i);
			GenericTypeResolver.resolveParameterType(parameter, this.beanType);
			result[i] = parameter;
		}
		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();
		}
	}
    
    //**根据bean创建新的HandlerMethod对象
	public HandlerMethod createWithResolvedBean() {
		Object handler = this.bean;
		//**如果是bean名称,使用beanFactory获取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);
	}
    
    //**获取方法实现的接口抽象方法中的参数注解
	private List<Annotation[][]> getInterfaceParameterAnnotations() {
		List<Annotation[][]> parameterAnnotations = this.interfaceParameterAnnotations;
		if (parameterAnnotations == null) {
			parameterAnnotations = new ArrayList<>();
			for (Class<?> ifc : this.method.getDeclaringClass().getInterfaces()) {
				for (Method candidate : ifc.getMethods()) {
					if (isOverrideFor(candidate)) {
						parameterAnnotations.add(candidate.getParameterAnnotations());
					}
				}
			}
			this.interfaceParameterAnnotations = parameterAnnotations;
		}
		return parameterAnnotations;
	}
}

5.2.InvocableHandlerMethod方法处理器

扩展解析方法参数并调用方法的功能,具体过程如下;

  • 获取目标方法的参数列表对象(参数类型等);
  • 调用invokeForRequest()时如果传入了目标方法的参数值(规则:传入的参数对象是目标接口某个参数的实例),则以传入的参数值为准;
  • 如果没有传入目标方法的参数值,则调用参数解析器解析目标接口的参数值;
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();

    //**根据request对象解析方法参数并调用该方法
	@Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        //**获取目标方法的参数列表
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		...
		//**调用目标方法
		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);
			//**如果providedArgs中有与目标接口参数类型相同的参数,则直接当成目标接口的参数
			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);
			}
			...
		}
		return args;
	}

	//**调用方法
	@Nullable
	protected Object doInvoke(Object... args) throws Exception {
	    //**设置可见性
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try { //**根据参数列表,调用该方法,并把结果返回
			return getBridgedMethod().invoke(getBean(), args);
		}
		...
	}
}

5.3.ServletInvocableHandlerMethod方法处理器

扩展处理方法返回值的功能,具体过程如下:

  • 调用父类的invokeForRequest()方法,解析目标方法的参数值,并调用目标方法,获取返回值;
  • 如果目标方法及其父类的方法上配置了@ResponseStatus注解,则把http响应状态写入到响应中;
  • 如果目标方法有返回值,则调用返回值处理器处理返回值;
  • 如果目标方法是异步方法,可以使用返回值调用wrapConcurrentResult方法,获取新的ConcurrentResultHandlerMethod异步方法处理器。以支持调用异步方法并获取返回值;
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    //**返回值处理器(组合了其它的返回值处理器)
	@Nullable
	private HandlerMethodReturnValueHandlerComposite returnValueHandlers;

    //**
	public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        //**调用父类的invokeForRequest方法,根据request对象解析方法参数并调用该方法
		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);
		}
		...
	}

    //**把响应状态和异常状态描述写入到HttpServletResponse中,并记录到request对象中
	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);
	}
	
	//**获取新的ConcurrentResultHandlerMethod异步方法处理器。支持异步方法调用
	ServletInvocableHandlerMethod wrapConcurrentResult(Object result) {
		return new ConcurrentResultHandlerMethod(result, new ConcurrentResultMethodParameter(result));
	}
}

5.4.ConcurrentResultHandlerMethod异步方法处理器

扩展调用异步方法的功能。ConcurrentResultHandlerMethod是ServletInvocableHandlerMethod的内部类,且是私有的。实际是为ServletInvocableHandlerMethod扩展调用异步方法的功能。具体过程如下:

  • 把ServletInvocableHandlerMethod调用目标方法获取的返回值(通常是异步方法的返回值)封装成一个Callable;
  • 根据Callable创建ConcurrentResultHandlerMethod异步方法处理器,该方法处理器的目标方法即为Callable方法;
private class ConcurrentResultHandlerMethod extends ServletInvocableHandlerMethod {
    private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call");
	private final MethodParameter returnType;

    /*
     * 把ServletInvocableHandlerMethod调用目标方法获取的返回值封装成一个Callable
     * 根据Callable创建ConcurrentResultHandlerMethod异步方法处理器
     */
	public ConcurrentResultHandlerMethod(final Object result, ConcurrentResultMethodParameter returnType) {
		super((Callable<Object>) () -> {
			if (result instanceof Exception) {
				throw (Exception) result;
			}
			else if (result instanceof Throwable) {
				throw new NestedServletException("Async processing failed", (Throwable) result);
			}
			return result;
		}, CALLABLE_METHOD);

		if (ServletInvocableHandlerMethod.this.returnValueHandlers != null) {
			setHandlerMethodReturnValueHandlers(ServletInvocableHandlerMethod.this.returnValueHandlers);
		}
		this.returnType = returnType;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值