欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

SpringMVC 源代码分析(八)--参数解析器

最编程 2024-04-03 07:29:07
...

1.参数解析器介绍

参数解析器用于解析Handler方法参数,例如经常看到Handler方法签名中有HttpServletRequest、HttpServletResponse等对象,还有请求参数中有@RequestBody、@PathVariable等注解修饰的对象,这些对象都是经过参数解析器解析后注入的

SpringMVC中默认的参数解析器见RequestMappingHandlerAdapter#getDefaultArgumentResolvers

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

    // 基于注解的参数解析器
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    resolvers.add(new PathVariableMethodArgumentResolver());
    resolvers.add(new PathVariableMapMethodArgumentResolver());
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    resolvers.add(new ServletModelAttributeMethodProcessor(false));
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    resolvers.add(new RequestAttributeMethodArgumentResolver());
 
    // 基于类型的参数解析器
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RedirectAttributesMethodArgumentResolver());
    resolvers.add(new ModelMethodProcessor());
    resolvers.add(new MapMethodProcessor());
    resolvers.add(new ErrorsMethodArgumentResolver());
    resolvers.add(new SessionStatusMethodArgumentResolver());
    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

    // 添加自定义参数解析器
    if (getCustomArgumentResolvers() != null) {
       resolvers.addAll(getCustomArgumentResolvers());
    }

    // 能够处理所有场景
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
    resolvers.add(new ServletModelAttributeMethodProcessor(true));

    return resolvers;
}

这些参数解析器会被封装到HandlerMethodArgumentResolverComposite中,处理参数时,会按照加入的顺序依次调用每个参数解析器执行解析任务,直到其中某个参数解析器能够解析此参数,则返回参数解析结果

2.自定义参数解析器

在某些场景下,我们可以自定义参数解析器,将某些数据通过参数传递到Handler方法中,例如在支付的场景中,我想要将渠道信息从请求头header中设置到Handler方法的参数中,这时我们可以自定义注解,然后再自定义参数解析器实现HandlerMethodArgumentResolver接口,将渠道的值传递到Handler方法中,示例如下:

1)创建Channel注解及自定义参数解析器

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Channel {
}

public class ChannelArgumentResolver implements HandlerMethodArgumentResolver {
    // 是否支持解析此参数
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        Channel channel = methodParameter.getParameterAnnotation(Channel.class);
        return channel != null;
    }
    
    // 解析参数
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        String channel = nativeWebRequest.getHeader("x-chnl");
        return channel;
    }
}

2)在配置类中加入自定义参数解析器

这样初始HandlerAdapter时,就会将自定义的参数解析器加入到参数解析器集合中

@Configuration
@ComponentScan
public class WebConfig {
    @Bean
    public ServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    @Bean
    public DispatcherServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
        DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        registrationBean.setLoadOnStartup(1);
        return registrationBean;
    }

    @Bean
    public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();
        // 添加自定义参数解析器
        handlerAdapter.setCustomArgumentResolvers(Arrays.asList(new ChannelArgumentResolver()));
        return handlerAdapter;
    }
}

3)添加控制器

@RestController
public class Controller {
    @GetMapping("/test")
    public void test(@Channel String channel) {
        System.out.println("渠道:" + channel);
    }
}

4)添加启动类

public class Test {
    public static void main(String[] args) {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    }
}

启动程序后使用postman调用http://127.0.0.1:8080/test,并在请求头中加入x-chnl=google,就能够打印出渠道信息,说明自定义参数解析器能够将渠道信息设置到test方法的参数中

3.参数解析器原理

参数解析器都实现了HandlerMethodArgumentResolver接口,它提供了如下两个方法