SpingBoot源码解析(十)入参出参映射类ArgumentResolver和ReturnValueHandler

文章目录

    • 一、ArgumentResolver源码分析
    • 二、注册时候的注意事项
    • 三、RequestMappingHandlerAdapter加载resolver和handler

在我们写代码的时候有时候需要做入参,出参映射,如果你们公司的网关已经实现了这个功能,那再好不过,如果没有,需要你在自己的某个服务中自己通过代码实现,这个时候Spring中的ArgumentResolver和ReturnValue类,就派上了用场。

一、ArgumentResolver源码分析

我们一个看看在Spring中,一个请求过来的时候什么时候使用到了ArgumentResolver。
在spring-boot中,默认情况下,当一个请求进来的时候,先进入DispatcherServlet,然后在handle方法中进入了RequestMappingHandlerAdapter的invokeHandlerMethod方法,一直下去会进入了InvocableHandlerMethod的getMethodArgumentValues方法,其中有这样一段:

args[i] = this.argumentResolvers.resolveArgument(
							parameter, mavContainer, request, this.dataBinderFactory);

如果你再参数前面加入了@RequestBody,就会进入RequestResponseBodyMethodProcessor的resolveArgument方法。
其时序图如下:

DispatcherSevlet RequestMapping ServletInvocab InvocableHa HandlerMetho RequestResponseBo doDispatcher() handle() invokeAndHandle() invokeForRequest() resolveArgument() resolveArgument() DispatcherSevlet RequestMapping ServletInvocab InvocableHa HandlerMetho RequestResponseBo

ReturnValueHandler是在返回的时候渲染的。不做过多解析了就。

二、注册时候的注意事项

如果你注册ArgumentResolver或ReturnValueHandler的时候像下面这样:

//    @Bean
//    public WebMvcConfigurer corsConfigurer() {
//        return new WebMvcConfigurer() {
////            @Override
////            public void addInterceptors(InterceptorRegistry registry){
////                registry.addInterceptor(new InPramMappingInterceptor())
////                        .addPathPatterns("/**");
////            }
//
////            @Override
////            public void addArgumentResolvers(List resolvers) {
////                List adapterArgumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
////                System.out.println(adapterArgumentResolvers.size());
//////                resolvers.add(new RequestResponseBodySkinDTOMethodProcessor());
////            }
//
//
//            @Override
//            public void addReturnValueHandlers(List handlers){
//            }
//        };
//    }

你会发现有时候执行不到你的ArgumentResolver,这是因为被别的拦截了,你需要像如下这样注册,才可以生效:

    @Resource
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @PostConstruct
    public void init() {
        final List<HandlerMethodReturnValueHandler> originalHandlers = new ArrayList<>(requestMappingHandlerAdapter.getReturnValueHandlers());

        // Add customed handler BEFORE the HttpEntityMethodProcessor to enable JsonReturnHandler !!
        final int httpEntityPos = obtainValueHandlerPosition(originalHandlers, HttpEntityMethodProcessor.class);
        HttpEntityMethodProcessor httpEntityMethodProcessor = (HttpEntityMethodProcessor) originalHandlers.get(httpEntityPos);
        originalHandlers.add(httpEntityPos - 1, new ResponseEntitySkinDTOReturnValueHandler(httpEntityMethodProcessor));
        requestMappingHandlerAdapter.setReturnValueHandlers(originalHandlers);

        //add argumentResolvers
        final List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(requestMappingHandlerAdapter.getArgumentResolvers());
        final int requestbodyPos = obtainResolverPosition(resolvers, RequestResponseBodyMethodProcessor.class);
        resolvers.add(requestbodyPos - 1, new RequestBodySkinDTOArgumentResolver());
        requestMappingHandlerAdapter.setArgumentResolvers(resolvers);


        return;
    }

    //查找指定hander在数组中的位置
    private int obtainValueHandlerPosition(final List<HandlerMethodReturnValueHandler> originalHandlers, Class<?> handlerClass) {
        for (int i = 0; i < originalHandlers.size(); i++) {
            final HandlerMethodReturnValueHandler valueHandler = originalHandlers.get(i);
            if (handlerClass.isAssignableFrom(valueHandler.getClass())) {
                return i;
            }
        }
        return -1;
    }

    //查找指定resolver在数组中的位置
    private int obtainResolverPosition(final List<HandlerMethodArgumentResolver> resolvers, Class<?> resloverClass) {
        for (int i = 0; i < resolvers.size(); i++) {
            final HandlerMethodArgumentResolver resolver = resolvers.get(i);
            if (resloverClass.isAssignableFrom(resolver.getClass())) {
                return i;
            }
        }
        return -1;
    }

这是为什么呢,这样从加载RequestMapingHandlerAdapter的时候开始分析了。

三、RequestMappingHandlerAdapter加载resolver和handler

在RequestMappingHandlerAdapter中,会执行如下方法加载resolver和Handler:

	@Override
	public void afterPropertiesSet() {
		// Do this first, it may add ResponseBody advice beans
		initControllerAdviceCache();

		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

在上面的getDefaultArgumentResolvers方法中,会先加载Spring默认的,然后再加载用户自己配置的,所以用户自己配置的有可能会被拦截,而通过之前说的方式,可以让自己配置的生效。

你可能感兴趣的:(spring,boot源码,SpringBoot源码)