美文网首页
03-SpringMVC初始化HandlerAdapters过程

03-SpringMVC初始化HandlerAdapters过程

作者: AcientFish | 来源:发表于2020-02-13 12:24 被阅读0次

上篇文章中我们看到了Spring官方对DispatcherServlet的几个特殊Bean的介绍,其中第二个就是HandlerAdapter,它是DispatcherServlet调用handler的关键,因为它会对DispatcherServlet屏蔽细节实现,让DispatcherServlet只关心调用结果而不需要去关心调用细节(例如参数类型转换及封装等)。下面我们来分析HandlerAdapter的初始化过程
我们已经知道DispatcherServlet有一个默认配置文件,里面配置了各种策略。对于HandlerAdapter来说存在3个默认配置,他们分别是

  • HttpRequestHandlerAdapter 这个处理器是用来处理静态资源的
  • SimpleControllerHandlerAdapter 这个处理器是用来处理在实现了Controller接口或者AbstractController等Spring的默认实现类的子类
  • RequestMappingHandlerAdapter 这个处理器用来处理@RequestMapping注解的处理器,也是最常用,用的最多的一个,我们今天会着重分析这个
    DispatcherServlet在初始化几个策略时遇到使用默认策略均会调用createDefaultStrategy方法,最终通过IOC容器创建对象并执行回调。通过RequestMappingHandlerAdapter的类关系图我们可以看出RequestMappingHandlerAdapter同样实现了InitializingBean接口,因此我们关注的重点一个是在实例化阶段,一个是在回调InitializingBean接口方法阶段。
    首先我们来看实例化阶段
// 祖先类中会设置allowHeader为除了Trace外所有的HttpMethod但并未设置supportedMethods
public WebContentGenerator(boolean restrictDefaultSupportedMethods) {
        if (restrictDefaultSupportedMethods) {
            this.supportedMethods = new LinkedHashSet<>(4);
            this.supportedMethods.add(METHOD_GET);
            this.supportedMethods.add(METHOD_HEAD);
            this.supportedMethods.add(METHOD_POST);
        }
        initAllowHeader();
}

private void initAllowHeader() {
        Collection<String> allowedMethods;
        // 这里配置allowedMethods
        if (this.supportedMethods == null) {
            allowedMethods = new ArrayList<>(HttpMethod.values().length - 1);
            for (HttpMethod method : HttpMethod.values()) {
                if (method != HttpMethod.TRACE) {
                    allowedMethods.add(method.name());
                }
            }
        }
        else if (this.supportedMethods.contains(HttpMethod.OPTIONS.name())) {
            allowedMethods = this.supportedMethods;
        }
        else {
            allowedMethods = new ArrayList<>(this.supportedMethods);
            allowedMethods.add(HttpMethod.OPTIONS.name());

        }
        this.allowHeader = StringUtils.collectionToCommaDelimitedString(allowedMethods);
    }

// 在自身的构造器中添加了StringHttpMessageConverter、ByteArrayHttpMessageConverter、
// SourceHttpMessageConverter、AllEncompassingFormHttpMessageConverter
public RequestMappingHandlerAdapter() {
        StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
        stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

        this.messageConverters = new ArrayList<>(4);
        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(stringHttpMessageConverter);
        try {
            this.messageConverters.add(new SourceHttpMessageConverter<>());
        }
        catch (Error err) {
            // Ignore when no TransformerFactory implementation is available
        }
        this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    }

上述代码中初始化了4种消息转换器,针对不同的content-type,设置不同的消息转换器

  • StringHttpMessageConverter 支持text/plain或所有/的请求类型
  • ByteArrayHttpMessageConverter 支持application/octet-stream或所有/的请求类型
  • SourceHttpMessageConverter 支持text/xml、application/xml、application/*-xml这三种类型的请求
  • AllEncompassingFormHttpMessageConverter 支持application/x-www-form-urlencoded的读写(file upload)和multipart/form-data格式的写操作(RestTemplate或WebClient中发送数据)。同时会根据classpath是否包含class文件来决定是否添加对xml和json的处理。
    /**
     * jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
     * jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
                        ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
     * jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
     * jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
     * gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
     * jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
     */
    public AllEncompassingFormHttpMessageConverter() {
        try {
            addPartConverter(new SourceHttpMessageConverter<>());
        }
        catch (Error err) {
            // Ignore when no TransformerFactory implementation is available
        }

        if (jaxb2Present && !jackson2XmlPresent) {
            addPartConverter(new Jaxb2RootElementHttpMessageConverter());
        }

        if (jackson2Present) {
            addPartConverter(new MappingJackson2HttpMessageConverter());
        }
        else if (gsonPresent) {
            addPartConverter(new GsonHttpMessageConverter());
        }
        else if (jsonbPresent) {
            addPartConverter(new JsonbHttpMessageConverter());
        }

        if (jackson2XmlPresent) {
            addPartConverter(new MappingJackson2XmlHttpMessageConverter());
        }

        if (jackson2SmilePresent) {
            addPartConverter(new MappingJackson2SmileHttpMessageConverter());
        }
    }

实例化阶段主要就做了这些操作,后面在分析DispatcherServlet处理过程时详细分析各转换器的作用。
下面我们再来关注初始化后的回调,开始之前我们先来看两张图


RequestMethod.png ReturnValue.png

以上两张图来自Spring官方文档,介绍了SpringMVC的Controller中支持的请求参数类型和返回值类型。其中我们眼熟的@RequestParam、@PathVariable、@RequestBody、Map、Model、ServletRequest、ServletResponse、@ResponseBody、ModelAndView等等。下面就会针对上述的类型注册相关解析器或处理器。

    @Override
    public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        // 在这个方法内会获取当前IOC容器中所有注有@ControllerAdvice的Bean,将他们包装成ControllerAdviceBean。
        // 并将他们分条件缓存。条件包括注有@InitBinder的方法,注有@ModelAttribute但没有@RequestMapping的方法。
        // 还会判断ControllerAdviceBean上是否注有RequestBodyAdvice或ResponseBodyAdvice,若存在也会缓存
        initControllerAdviceCache();
        
        // 这里会初始化参数解析器(包括内建解析器和通过setCustomArgumentResolvers方法自定义的解析器)
        if (this.argumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
            this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        // 这里会初始化一系列解析@InitBinder的解析器
        if (this.initBinderArgumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
            this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        // 这里会初始化返回值处理器()包括内建处理器和通过setReturnValueHandlers自定义的处理器)
        if (this.returnValueHandlers == null) {
            List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
            this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
        }
    }

至此初始化过程基本结束,后面的异常处理、视图解析、本地化、样式、文件上传和请求转发处理我们以后在分析,下一篇我们会开始分析DispatcherServlet的处理过程。
在查看官方文档的时候发现Spring现在推荐使用MVC Config作为最佳入口,因为它申明了必须的Special Bean同时提供要给高等级的自定义配置回调API。


WebMVCConfig.png

只有无法找到MVC Config时才会使用DispatcherServlet.properties进行默认配置。后面有机会我们会重新分析MVC Config方式下的操作流程。

相关文章

网友评论

      本文标题:03-SpringMVC初始化HandlerAdapters过程

      本文链接:https://www.haomeiwen.com/subject/oumkfhtx.html