springmvc--10--@EnableWebMvc注解原理

springmvc–@EnableWebMvc注解原理

文章目录

1 用途

参考spring官网,我们发现@EnableWebMvc注解和<mvc:annotation-driven>具有相同的功能,都是用来启用springmvc的默认配置。当扫描到这个注解之后,就会向容器中注入一些默认组件。

那么这个注解是如何实现springmvc默认配置的呢?往下看

2 @EnableWebMvc

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

我们发现,实际上就是通过@Import注解向容器中注册了一个组件DelegatingWebMvcConfiguration,所以现在核心是DelegatingWebMvcConfiguration类干了什么?

3 DelegatingWebMvcConfiguration

先来看一下类图

在这里插入图片描述

两个Aware接口向该组件中自动注入一个ApplicationContext对象和ServletContext对象

现在我们要带着两个问题去看一下该类的源码

  • 为什么这个组件能够使用户自定义的配置类生效呢?
  • 这个组件定义了哪些默认配置?

3.1 DelegatingWebMvcConfiguration源码

DelegatingWebMvcConfiguration类非常简单,只有一个字段configurers,持有所有的配置类对象。另外还需要注意一个方法setConfigurers(List<WebMvcConfigurer> configurers)方法, 该方法标注了@Autowired(required = false)注解,会被自动执行。

@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    //组合模式,所有配置类组合
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


    /**
     * 此处添加了@Autowired(required = false)注解
     * 那么该方法在属性填充阶段就会被执行,其中方法参数来自于容器
     * 会自动从容器中找到类型匹配的bean,然后反射执行方法
     */
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            //将容器中WebMvcConfigurer对象全部放入WebMvcConfigurerComposite中
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }


    /********************************************************************************/

    /**
     * 下面的这些方法都差不多
     * 向WebMvcConfigurerComposite的每一个配置类中添加配置具体的过程见3.1.1
     */
    @Override
    protected void configurePathMatch(PathMatchConfigurer configurer) {
        this.configurers.configurePathMatch(configurer);
    }

    @Override
    protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        this.configurers.configureContentNegotiation(configurer);
    }

    @Override
    protected void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        this.configurers.configureAsyncSupport(configurer);
    }

    @Override
    protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        this.configurers.configureDefaultServletHandling(configurer);
    }

    @Override
    protected void addFormatters(FormatterRegistry registry) {
        this.configurers.addFormatters(registry);
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        this.configurers.addInterceptors(registry);
    }

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        this.configurers.addResourceHandlers(registry);
    }

    @Override
    protected void addCorsMappings(CorsRegistry registry) {
        this.configurers.addCorsMappings(registry);
    }

    @Override
    protected void addViewControllers(ViewControllerRegistry registry) {
        this.configurers.addViewControllers(registry);
    }

    @Override
    protected void configureViewResolvers(ViewResolverRegistry registry) {
        this.configurers.configureViewResolvers(registry);
    }

    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        this.configurers.addArgumentResolvers(argumentResolvers);
    }

    @Override
    protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        this.configurers.addReturnValueHandlers(returnValueHandlers);
    }

    @Override
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        this.configurers.configureMessageConverters(converters);
    }

    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        this.configurers.extendMessageConverters(converters);
    }

    @Override
    protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
    }

    @Override
    protected void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        this.configurers.extendHandlerExceptionResolvers(exceptionResolvers);
    }
    
    /********************************************************************************/

    //这个方法会获取配置类定义的唯一的Validator
    @Override
    @Nullable
    protected Validator getValidator() {
        return this.configurers.getValidator();
    }

    //这个方法会获取配置类定义的唯一的MessageCodesResolver
    @Override
    @Nullable
    protected MessageCodesResolver getMessageCodesResolver() {
        return this.configurers.getMessageCodesResolver();
    }

}

可以看到,在DelegatingWebMvcConfiguration组件属性填充阶段,会自动的查找容器中所有类型为WebMvcConfigurerbean对象,然后以它们为参数,反射调用setConfigurers(List<WebMvcConfigurer> configurers)方法,将所有的配置类对象放入WebMvcConfigurerComposite中。

除此之外,它还重写了父类的一些方法(模板方法模式),用来向配置类中注册配置和获取配置类中一些配置对象,比如ValidatorMessageCodesResolver

3.2 WebMvcConfigurerComposite

下面是该类的源码,方法都很简单,主要注意以下四点:

  • 该类持有所有的配置类对象
  • 添加一项配置的时候会将配置注册给所有的配置类对象
  • getValidator()方法只能被一个配置类重写,只允许一个配置类返回一个Validator对象
  • getMessageCodesResolver()方法只能被一个配置类重写,只允许一个配置类返回一个MessageCodesResolver对象
class WebMvcConfigurerComposite implements WebMvcConfigurer {

    //持有的配置类对象
    private final List<WebMvcConfigurer> delegates = new ArrayList<>();


    public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.delegates.addAll(configurers);
        }
    }


    //添加一项配置的时候会将配置注册给所有的配置类对象
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configurePathMatch(configurer);
        }
    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configureContentNegotiation(configurer);
        }
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configureAsyncSupport(configurer);
        }
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configureDefaultServletHandling(configurer);
        }
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addFormatters(registry);
        }
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addInterceptors(registry);
        }
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addResourceHandlers(registry);
        }
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addCorsMappings(registry);
        }
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addViewControllers(registry);
        }
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configureViewResolvers(registry);
        }
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addArgumentResolvers(argumentResolvers);
        }
    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addReturnValueHandlers(returnValueHandlers);
        }
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configureMessageConverters(converters);
        }
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.extendMessageConverters(converters);
        }
    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.configureHandlerExceptionResolvers(exceptionResolvers);
        }
    }

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.extendHandlerExceptionResolvers(exceptionResolvers);
        }
    }

    /**
     * 这个方法需要注意一下
     * Validator必须是唯一的,也就说,只允许一个配置重写该方法返回一个Validator
     * 有多个就会抛出异常
     */
    @Override
    public Validator getValidator() {
        Validator selected = null;
        for (WebMvcConfigurer configurer : this.delegates) {
            Validator validator = configurer.getValidator();
            if (validator != null) {
                if (selected != null) {
                    throw new IllegalStateException("No unique Validator found: {" +
                                                    selected + ", " + validator + "}");
                }
                selected = validator;
            }
        }
        return selected;
    }


    /**
     * 这个方法也需要注意一下
     * MessageCodesResolver必须是唯一的,也就说,
     * 只允许一个配置重写该方法返回一个MessageCodesResolver,有多个就会抛出异常
     */
    @Override
    @Nullable
    public MessageCodesResolver getMessageCodesResolver() {
        MessageCodesResolver selected = null;
        for (WebMvcConfigurer configurer : this.delegates) {
            MessageCodesResolver messageCodesResolver = configurer.getMessageCodesResolver();
            if (messageCodesResolver != null) {
                if (selected != null) {
                    throw new IllegalStateException("No unique MessageCodesResolver found: {" +
                                                    selected + ", " + messageCodesResolver + "}");
                }
                selected = messageCodesResolver;
            }
        }
        return selected;
    }

}

3.3 WebMvcConfigurationSupport

这是最重要的一个类,该类里面有很多@Bean方法,用来向容器中注册组件

3.3.1 静态私有字段

private static final boolean romePresent;

private static final boolean jaxb2Present;

private static final boolean jackson2Present;

private static final boolean jackson2XmlPresent;

private static final boolean jackson2SmilePresent;

private static final boolean jackson2CborPresent;

private static final boolean gsonPresent;

private static final boolean jsonbPresent;

static {
    ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
    romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
    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);
    jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
    gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
    jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
}

这些静态字段是一些标志量,通过ClassUtils.isPresent()方法判断是否导入了对应的包,如果导入了,就将对应的静态字段置为true,后面就可以根据这些标志量反射实例化对象了。

这也解释了为什么只要我们导入了jackson包,就可以自动向HandlerAdapter中注册MappingJackson2HttpMessageConverterMappingJackson2XmlHttpMessageConverter对象

3.3.2 普通字段

//web应用上下文
@Nullable
private ApplicationContext applicationContext;

//servlet上下文
@Nullable
private ServletContext servletContext;

//拦截器
@Nullable
private List<Object> interceptors;

//路径匹配配置器
@Nullable
private PathMatchConfigurer pathMatchConfigurer;

//内容协商管理器
@Nullable
private ContentNegotiationManager contentNegotiationManager;

//参数解析器
@Nullable
private List<HandlerMethodArgumentResolver> argumentResolvers;

//返回值处理器
@Nullable
private List<HandlerMethodReturnValueHandler> returnValueHandlers;

//http消息转换器
@Nullable
private List<HttpMessageConverter<?>> messageConverters;

//跨域配置
@Nullable
private Map<String, CorsConfiguration> corsConfigurations;

后面的小章节都是该类定义的@Bean方法,我们看看它到底向容器中放入了哪些组件?

3.3.3 注册RequestMappingHandlerMapping

@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
    @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
    @Qualifier("mvcConversionService") FormattingConversionService conversionService,
    @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

    //创建一个RequestMappingHandlerMapping对象,就是简单的new一个,见3.3.3.1
    RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    //设置顺序,第一位
    mapping.setOrder(0);
    /** 
     * getInterceptors()方法可以获取到用户注册和系统默认的所有拦截器对象,见3.3.3.2
     * 然后将这些拦截器全部放入处理器映射器中
     */
    mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
    //设置内容协商管理器
    mapping.setContentNegotiationManager(contentNegotiationManager);
    //获取用户注册的所有跨域配置,见3.3.3.3
    mapping.setCorsConfigurations(getCorsConfigurations());

    //获取路径匹配配置器,见3.3.3.4
    PathMatchConfigurer configurer = getPathMatchConfigurer();

    //已经被废弃掉了不推荐使用,直接跳过
    Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
    if (useSuffixPatternMatch != null) {
        mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
    }
    //已经被废弃掉了不推荐使用,直接跳过
    Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
    if (useRegisteredSuffixPatternMatch != null) {
        mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
    }
    
    //是否使用尾斜杠匹配
    Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
    if (useTrailingSlashMatch != null) {
        mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
    }

    //获取路径匹配配置器中设置的UrlPathHelper
    UrlPathHelper pathHelper = configurer.getUrlPathHelper();
    //使用这个UrlPathHelper覆盖默认的UrlPathHelper
    if (pathHelper != null) {
        mapping.setUrlPathHelper(pathHelper);
    }
    //获取在路径匹配配置器中配置的路径匹配器
    PathMatcher pathMatcher = configurer.getPathMatcher();
    //覆盖默认的路径匹配器
    if (pathMatcher != null) {
        mapping.setPathMatcher(pathMatcher);
    }
    
    //获取配置所有路径前缀
    Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
    if (pathPrefixes != null) {
        mapping.setPathPrefixes(pathPrefixes);
    }

    return mapping;
}

我们可以发现,这个方法为RequestMappingHandlerMapping做了一大堆配置,如下所示

  • 获取用户和系统配置的所有拦截器并注册进去
  • 获取容器中内容协商管理器并注册进去
  • 获取用户注册的所有跨域配置并注册进去
  • 获取路径匹配配置器,然后将路径匹配配置器的配置覆盖进去,主要覆盖一下4中配置
    • 尾斜杠匹配
    • UrlPathHelper
    • PathMatcher
    • pathPrefixes
3.3.3.1 创建一个RequestMappingHandlerMapping对象
/**
 * Protected method for plugging in a custom subclass of
 * {@link RequestMappingHandlerMapping}.
 * @since 4.0
 */
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
    return new RequestMappingHandlerMapping();
}

就是简单的new一个RequestMappingHandlerMapping对象

3.3.3.2 获取用户注册和系统默认的所有拦截器对象
/**
 * Provide access to the shared handler interceptors used to configure
 * {@link HandlerMapping} instances with.
 * <p>This method cannot be overridden; use {@link #addInterceptors} instead.
 */
protected final Object[] getInterceptors(
    FormattingConversionService mvcConversionService,
    ResourceUrlProvider mvcResourceUrlProvider) {
    if (this.interceptors == null) {
        //拦截器注册中心
        InterceptorRegistry registry = new InterceptorRegistry();
        /**
         * 该方法被子类重写,会以这个拦截器注册中心为参数调用所有配置类对象中
         * addInterceptors(registry)方法,用户通过这个注册中心注册拦截器对象。
         * 很明显,此处调用addInterceptors(registry)方法,
         * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
         * addInterceptors(registry)方法,将配置类中配置的拦截器注册到拦截器注册中心
         * 这个注册中心会将所有类型的拦截器统一适配为InterceptorRegistration类型,方便管理
         */
        addInterceptors(registry);
        //这个拦截器将FormattingConversionService保存到请求域中
        registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
        //这个拦截器将ResourceUrlProvider保存到请求域中
        registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
        //获取注册中心中所有的拦截器,会先排序,再返回
        this.interceptors = registry.getInterceptors();
    }
    return this.interceptors.toArray();
}

我们重点看一下这个拦截器注册中心是如何注册拦截器的

private final List<InterceptorRegistration> registrations = new ArrayList<>();


/**
 * Adds the provided {@link HandlerInterceptor}.
 * @param interceptor the interceptor to add
 * @return an {@link InterceptorRegistration} that allows you optionally configure the
 * registered interceptor further for example adding URL patterns it should apply to.
 */
public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
    //先适配为InterceptorRegistration类型,然后放入list集合中
    InterceptorRegistration registration = new InterceptorRegistration(interceptor);
    this.registrations.add(registration);
    return registration;
}

这个方法是拦截器相关的核心方法了,它会执行每一个WebMvcConfigurer配置类的addInterceptors(registry)方法, 将用户自定义的拦截器注册到拦截器注册中心中,最后再放入两个系统默认的拦截器,然后统一排序,并返回

3.3.3.3 获取用户注册的所有跨域配置
/**
 * Return the registered {@link CorsConfiguration} objects,
 * keyed by path pattern.
 * @since 4.2
 */
protected final Map<String, CorsConfiguration> getCorsConfigurations() {
    if (this.corsConfigurations == null) {
        //跨域配置注册中心
        CorsRegistry registry = new CorsRegistry();
        /**
         * 和上面拦截器一样,会以这个跨域配置注册中心为参数调用所有配置类对象中
         * addCorsMappings(registry)方法,用户通过这个注册中心注册跨域配置。
         * 很明显,此处调用addCorsMappings(registry)方法,
         * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
         * addCorsMappings(registry)方法,将配置类中配置的跨域配置注册到注册中心
         */
        addCorsMappings(registry);
        //得到注册中心的所有的跨域配置
        this.corsConfigurations = registry.getCorsConfigurations();
    }
    return this.corsConfigurations;
}

和上面拦截器一样,它会执行每一个WebMvcConfigurer配置类的addCorsMappings(registry)方法, 将用户自定义的跨域配置注册到跨域配置注册中心,最后返回所有的跨域配置

3.3.3.4 获取路径匹配配置器
/**
 * Callback for building the {@link PathMatchConfigurer}.
 * Delegates to {@link #configurePathMatch}.
 * @since 4.1
 */
protected PathMatchConfigurer getPathMatchConfigurer() {
    if (this.pathMatchConfigurer == null) {
        //新建一个路径匹配配置器
        this.pathMatchConfigurer = new PathMatchConfigurer();
        /**
         * 又是同样的方式,会以这个路径匹配配置器为参数调用所有配置类对象中
         * configurePathMatch(pathMatchConfigurer)方法,用户通过这个配置器注册路径匹配器。
         * 很明显,此处调用configurePathMatch(pathMatchConfigurer)方法,
         * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
         * configurePathMatch(pathMatchConfigurer)方法,将配置类中配置的路径匹配器
         * 注册到配置器中
         */
        configurePathMatch(this.pathMatchConfigurer);
    }
    return this.pathMatchConfigurer;
}

同样的方式,它会执行每一个WebMvcConfigurer配置类的configurePathMatch(pathMatchConfigurer)方法, 将用户自定义的路径匹配器注册到路径匹配配置器中,最后返回这个路劲匹配配置器

3.3.4 注册PathMatcher

/**
 * Return a global {@link PathMatcher} instance for path matching
 * patterns in {@link HandlerMapping HandlerMappings}.
 * This instance can be configured using the {@link PathMatchConfigurer}
 * in {@link #configurePathMatch(PathMatchConfigurer)}.
 * @since 4.1
 */
@Bean
public PathMatcher mvcPathMatcher() {
    /**
     * getPathMatchConfigurer()方法,获取路径匹配配置器,见3.3.3.4
     * 然后得到这个配置器中的路径匹配器,把它放入容器中
     * 如果用户未配置,则使用默认的AntPathMatcher
     */
    PathMatcher pathMatcher = getPathMatchConfigurer().getPathMatcher();
    return (pathMatcher != null ? pathMatcher : new AntPathMatcher());
}

先尝试从PathMatchConfigurer路径匹配配置器中取出用户配置的PathMatcher路径匹配器,若存在,则直接将用户配置的放入容器中,否则创建一个新的AntPathMatcher对象放入容器中

3.3.5 注册UrlPathHelper

/**
 * Return a global {@link UrlPathHelper} instance for path matching
 * patterns in {@link HandlerMapping HandlerMappings}.
 * This instance can be configured using the {@link PathMatchConfigurer}
 * in {@link #configurePathMatch(PathMatchConfigurer)}.
 * @since 4.1
 */
@Bean
public UrlPathHelper mvcUrlPathHelper() {
    /**
     * getPathMatchConfigurer()方法,获取路径匹配配置器,见3.3.3.4
     * 然后得到这个配置器中的UrlPathHelper,把它放入容器中
     * 如果用户未配置,则使用默认的UrlPathHelper
     */
    UrlPathHelper pathHelper = getPathMatchConfigurer().getUrlPathHelper();
    return (pathHelper != null ? pathHelper : new UrlPathHelper());
}

先尝试从PathMatchConfigurer路径匹配配置器中取出用户配置的UrlPathHelper,若存在,则直接将用户配置的放入容器中,否则创建一个新的UrlPathHelper对象放入容器中

3.3.6 注册ContentNegotiationManager

/**
 * Return a {@link ContentNegotiationManager} instance to use to determine
 * requested {@linkplain MediaType media types} in a given request.
 */
@Bean
public ContentNegotiationManager mvcContentNegotiationManager() {
    if (this.contentNegotiationManager == null) {
        //创建一个内容协商配置器
        ContentNegotiationConfigurer configurer = new ContentNegotiationConfigurer(this.servletContext);
        //将服务器默认能生产的媒体类型保存到内容协商配置器
        configurer.mediaTypes(getDefaultMediaTypes());
        /**
         * 和上面拦截器一样,会以这个内容协商配置器为参数调用所有配置类对象中
         * configureContentNegotiation(configurer)方法,用户通过这个配置器注册和
         * 修改内容协商管理器。很明显,此处调用configureContentNegotiation(configurer)方法,
         * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
         * configureContentNegotiation(configurer)方法,
         * 将配置类中配置的内容协商管理器注册到配置器中
         */
        configureContentNegotiation(configurer);
        //根据这个配置器的配置创建一个内容协商管理器
        this.contentNegotiationManager = configurer.buildContentNegotiationManager();
    }
    return this.contentNegotiationManager;
}

下面这个方法会获取到服务器默认能生产的媒体类型,实际上就判断是否导入了对应包

protected Map<String, MediaType> getDefaultMediaTypes() {
    Map<String, MediaType> map = new HashMap<>(4);
    //根据3.3.1的这些标志量,来得到服务器能够生产的媒体类型
    if (romePresent) {
        map.put("atom", MediaType.APPLICATION_ATOM_XML);
        map.put("rss", MediaType.APPLICATION_RSS_XML);
    }
    //导了jackson包就能生产xml,json
    if (jaxb2Present || jackson2XmlPresent) {
        map.put("xml", MediaType.APPLICATION_XML);
    }
    if (jackson2Present || gsonPresent || jsonbPresent) {
        map.put("json", MediaType.APPLICATION_JSON);
    }
    if (jackson2SmilePresent) {
        map.put("smile", MediaType.valueOf("application/x-jackson-smile"));
    }
    if (jackson2CborPresent) {
        map.put("cbor", MediaType.APPLICATION_CBOR);
    }
    return map;
}

并不是直接new一个ContentNegotiationManager对象,而是先创建一个ContentNegotiationConfigurer对象,然后将用户的自定义配置保存在ContentNegotiationConfigurer中,由ContentNegotiationConfigurer来生产ContentNegotiationManager对象,就是工厂模式,隐藏对象创建的细节。

3.3.7 注册viewControllerHandlerMapping

/**
 * Return a handler mapping ordered at 1 to map URL paths directly to
 * view names. To configure view controllers, override
 * {@link #addViewControllers}.
 */
@Bean
@Nullable
public HandlerMapping viewControllerHandlerMapping(
    @Qualifier("mvcPathMatcher") PathMatcher pathMatcher,
    @Qualifier("mvcUrlPathHelper") UrlPathHelper urlPathHelper,
    @Qualifier("mvcConversionService") FormattingConversionService conversionService,
    @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
    //视图控制注册中心
    ViewControllerRegistry registry = new ViewControllerRegistry(this.applicationContext);
    /**
     * addViewControllers(registry)这个方法我们应该不陌生,我们经常在配置类中重写该方法,
     * 注册路径->视图的映射关系。
     * 此处调用addViewControllers(registry)方法,
     * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
     * addViewControllers(registry)方法,注册路径->视图的映射关系
     */
    addViewControllers(registry);

    //真实类型为SimpleUrlHandlerMapping
    AbstractHandlerMapping handlerMapping = registry.buildHandlerMapping();
    if (handlerMapping == null) {
        return null;
    }
    handlerMapping.setPathMatcher(pathMatcher);
    handlerMapping.setUrlPathHelper(urlPathHelper);
    //将所有的拦截器设置进去
    handlerMapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
    //跨域配置
    handlerMapping.setCorsConfigurations(getCorsConfigurations());
    return handlerMapping;
}

这个还是很重要的:我们经常在配置类中重写addViewControllers(registry)方法,注册路径->视图(ParameterizableViewController)的映射关系。最终由SimpleControllerHandlerAdapter调用ParameterizableViewControllerhandleRequest()方法完成视图处理,见3.3.19

3.3.8 注册BeanNameUrlHandlerMapping

/**
 * Return a {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL
 * paths to controller bean names.
 */
@Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping(
      @Qualifier("mvcConversionService") FormattingConversionService conversionService,
      @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

   BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
   mapping.setOrder(2);
   mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
   mapping.setCorsConfigurations(getCorsConfigurations());
   return mapping;
}

已经被淘汰了,我们直接跳过

3.3.9 注册RouterFunctionMapping

/**
 * Return a {@link RouterFunctionMapping} ordered at 3 to map
 * {@linkplain org.springframework.web.servlet.function.RouterFunction router functions}.
 * Consider overriding one of these other more fine-grained methods:
 * <ul>
 * <li>{@link #addInterceptors} for adding handler interceptors.
 * <li>{@link #addCorsMappings} to configure cross origin requests processing.
 * <li>{@link #configureMessageConverters} for adding custom message converters.
 * </ul>
 * @since 5.2
 */
@Bean
public RouterFunctionMapping routerFunctionMapping(
    @Qualifier("mvcConversionService") FormattingConversionService conversionService,
    @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

    RouterFunctionMapping mapping = new RouterFunctionMapping();
    mapping.setOrder(3);
    mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
    mapping.setCorsConfigurations(getCorsConfigurations());
    mapping.setMessageConverters(getMessageConverters());
    return mapping;
}

功能路由,类似于webflux,直接忽略

3.3.10 注册resourceHandlerMapping

/**
 * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
 * resource handlers. To configure resource handling, override
 * {@link #addResourceHandlers}.
 */
@Bean
@Nullable
public HandlerMapping resourceHandlerMapping(
    @Qualifier("mvcUrlPathHelper") UrlPathHelper urlPathHelper,
    @Qualifier("mvcPathMatcher") PathMatcher pathMatcher,
    @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
    @Qualifier("mvcConversionService") FormattingConversionService conversionService,
    @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

    Assert.state(this.applicationContext != null, "No ApplicationContext set");
    Assert.state(this.servletContext != null, "No ServletContext set");

    //创建资源处理器注册中心
    ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
                                                                   this.servletContext, contentNegotiationManager, urlPathHelper);
    /**
     * 会以这个资源处理器注册中心为参数调用所有配置类对象中
     * addResourceHandlers(registry)方法,用户通过这个资源处理器注册中心设置静态资源的
     * 路径,静态资源位置,静态资源缓存时间等等。
     * 很明显,此处调用addResourceHandlers(registry)方法,
     * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
     * addResourceHandlers(registry)方法,将配置类中静态资源的配置保存到资源处理器注册中心中 
     */
    addResourceHandlers(registry);

    //通过资源处理器注册中心创建处理静态资源的SimpleUrlHandlerMapping
    AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
    if (handlerMapping == null) {
        return null;
    }
    //设置其它的一些配置,这些我们都见过,见3.3.3
    handlerMapping.setPathMatcher(pathMatcher);
    handlerMapping.setUrlPathHelper(urlPathHelper);
    handlerMapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
    handlerMapping.setCorsConfigurations(getCorsConfigurations());
    return handlerMapping;
}

这个HandlerMapping很重要,它用来处理静态资源。通过重写WebMvcConfigurer配置类的addResourceHandlers(registry)方法即可设置静态资源的路径,静态资源位置,静态资源缓存时间。然后没有配置@RequestMapping注解映射的路径就会交由这个HandlerMapping处理,只有它也不能处理才会返回404

3.3.11 注册ResourceUrlProvider

/**
 * A {@link ResourceUrlProvider} bean for use with the MVC dispatcher.
 * @since 4.1
 */
@Bean
public ResourceUrlProvider mvcResourceUrlProvider() {
    
    ResourceUrlProvider urlProvider = new ResourceUrlProvider();
    //获取用户在路径匹配配置器配置的UrlPathHelper
    UrlPathHelper pathHelper = getPathMatchConfigurer().getUrlPathHelper();
    if (pathHelper != null) {
        urlProvider.setUrlPathHelper(pathHelper);
    }
    //获取用户在路径匹配配置器配置的PathMatcher
    PathMatcher pathMatcher = getPathMatchConfigurer().getPathMatcher();
    if (pathMatcher != null) {
        urlProvider.setPathMatcher(pathMatcher);
    }
    return urlProvider;
}
  • ResourceUrlProvider是一个监听器,监听ContextRefreshedEvent事件,即refresh()方法执行完成之前触发。
  • 如果看过我上一篇文章–springmvc–9--静态资源的处理,我们应该知道,如过用户配置了静态资源映射,那么就会自动生成一个对应的ResourceHttpRequestHandler静态资源请求处理器。而ResourceUrlProvider则会将用户配置的静态资源请求处理器从SimpleUrlHandlerMapping提取出来,单独管理

3.3.12 注册defaultServletHandlerMapping

/**
 * Return a handler mapping ordered at Integer.MAX_VALUE with a mapped
 * default servlet handler. To configure "default" Servlet handling,
 * override {@link #configureDefaultServletHandling}.
 */
@Bean
@Nullable
public HandlerMapping defaultServletHandlerMapping() {
    Assert.state(this.servletContext != null, "No ServletContext set");
    //默认Servlet处理配置器
    DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(this.servletContext);
    /**
     * 会以这个默认Servlet处理的配置器为参数调用所有配置类对象中
     * configureDefaultServletHandling(configurer)方法,用户通过这个配置器启用Tomcat容器
     * 默认的Servlet,将静态资源请求转发给这个默认的Servlet处理。
     * 很明显,此处调用configureDefaultServletHandling(configurer)方法,
     * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
     * configureDefaultServletHandling(configurer)方法,启用Tomcat容器默认的Servlet
     */
    configureDefaultServletHandling(configurer);
    //应用用户配置构建一个SimpleUrlHandlerMapping
    return configurer.buildHandlerMapping();
}

不知道大家知不知道springmmvc的这项配置<mvc:default-servlet-handler/>是什么意思?

其实这个配置就是启用Tomcat容器默认的Servlet处理静态资源请求,和此处配置类的configureDefaultServletHandling(configurer)方法是对应的,用户在这个方法中调用configurer.enable();就启用了Tomcat容器默认的Servlet

3.3.13 注册RequestMappingHandlerAdapter

/**
 * Returns a {@link RequestMappingHandlerAdapter} for processing requests
 * through annotated controller methods. Consider overriding one of these
 * other more fine-grained methods:
 * <ul>
 * <li>{@link #addArgumentResolvers} for adding custom argument resolvers.
 * <li>{@link #addReturnValueHandlers} for adding custom return value handlers.
 * <li>{@link #configureMessageConverters} for adding custom message converters.
 * </ul>
 */
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
    @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
    @Qualifier("mvcConversionService") FormattingConversionService conversionService,
    @Qualifier("mvcValidator") Validator validator) {

    //创建一个RequestMappingHandlerAdapter对象,见3.3.13.1
    RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
    adapter.setContentNegotiationManager(contentNegotiationManager);
    /**
     * getMessageConverters()方法,获取所有的消息转换器,见3.3.13.2
     * 并将这些消息转换器设置到处理器适配器中
     */
    adapter.setMessageConverters(getMessageConverters());
    /**
     * getConfigurableWebBindingInitializer()方法,获取数据绑定器的初始化器,见3.3.13.3
     * 并将这个初始化器设置到处理器适配器中
     */
    adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
    /**
     * getArgumentResolvers()方法,获取所有用户自定义的参数解析器,见3.3.13.4
     * 并设置到处理器适配器中
     */
    adapter.setCustomArgumentResolvers(getArgumentResolvers());
    /**
     * getReturnValueHandlers()方法,获取所有用户自定义的返回值处理器,见3.3.13.5
     * 并设置到处理器适配器中
     */
    adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

    /**
     * 导入了jackson包,就放入两个通知
     * 这两个通知分别会在@RequestBody注解参数类型转换前后执行
     * @ResponseBody注解方法返回值写入响应前后执行
     */
    if (jackson2Present) {
        adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
        adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
    }

    /*****************************异步支持的相关配置***********************************/
    //异步支持配置器
    AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
    /**
     * 此处调用configureAsyncSupport(configurer)方法,
     * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
     * configureAsyncSupport(configurer)方法,向这个异步支持配置器中添加用户自定义的配置
     */
    configureAsyncSupport(configurer);
    if (configurer.getTaskExecutor() != null) {
        //异步执行器,实际上就是线程池,异步完成处理器调用
        adapter.setTaskExecutor(configurer.getTaskExecutor());
    }
    if (configurer.getTimeout() != null) {
        //异步执行的超时时间
        adapter.setAsyncRequestTimeout(configurer.getTimeout());
    }
    //异步支持的拦截器
    adapter.setCallableInterceptors(configurer.getCallableInterceptors());
    adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

    return adapter;
}

我们可以发现,这个方法为RequestMappingHandlerAdapter做了一大堆配置,如下所示

  • 获取容器中内容协商管理器并注册进去
  • 获取所有的消息转换器并注册进去
  • 获取数据绑定器的初始化器并注册进去
  • 获取所有用户自定义的参数解析器并注册进去
  • 获取所有用户自定义的返回值处理器并注册进去
  • 将用户自定义的异步配置覆盖进去
3.3.13.1 创建一个RequestMappingHandlerAdapter对象
/**
 * Protected method for plugging in a custom subclass of
 * {@link RequestMappingHandlerAdapter}.
 * @since 4.3
 */
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
    return new RequestMappingHandlerAdapter();
}

就是简单的new一个RequestMappingHandlerAdapter对象

3.3.13.2 获取所有的消息转换器
/**
 * Provides access to the shared {@link HttpMessageConverter HttpMessageConverters}
 * used by the {@link RequestMappingHandlerAdapter} and the
 * {@link ExceptionHandlerExceptionResolver}.
 * <p>This method cannot be overridden; use {@link #configureMessageConverters} instead.
 * Also see {@link #addDefaultHttpMessageConverters} for adding default message converters.
 */
protected final List<HttpMessageConverter<?>> getMessageConverters() {
    if (this.messageConverters == null) {
        this.messageConverters = new ArrayList<>();
        /**
         * 此处调用configureMessageConverters(list)方法,
         * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
         * configureMessageConverters(list)方法,向这个集合中添加消息转换器
         * 通过此方法添加消息转换器,springmvc容器就不会注册默认的消息转换器了
         */
        configureMessageConverters(this.messageConverters);
        if (this.messageConverters.isEmpty()) {
            //添加默认的消息转换器
            addDefaultHttpMessageConverters(this.messageConverters);
        }
        /**
         * 此处调用extendMessageConverters(list)方法,
         * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
         * extendMessageConverters(list)方法,向这个集合中添加消息转换器
         * 这个方法是在系统默认的转换器基础额外添加用户自定义的消息转换器
         */
        extendMessageConverters(this.messageConverters);
    }
    return this.messageConverters;
}

如果用户未自定义消息转换器,那么springmvc就会添加一些默认的消息转换器

/**
 * Adds a set of default HttpMessageConverter instances to the given list.
 * Subclasses can call this method from {@link #configureMessageConverters}.
 * @param messageConverters the list to add the default message converters to
 */
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
   messageConverters.add(new ByteArrayHttpMessageConverter());
   messageConverters.add(new StringHttpMessageConverter());
   messageConverters.add(new ResourceHttpMessageConverter());
   messageConverters.add(new ResourceRegionHttpMessageConverter());
   try {
      messageConverters.add(new SourceHttpMessageConverter<>());
   }
   catch (Throwable ex) {
      // Ignore when no TransformerFactory implementation is available...
   }
   messageConverters.add(new AllEncompassingFormHttpMessageConverter());

   if (romePresent) {
      messageConverters.add(new AtomFeedHttpMessageConverter());
      messageConverters.add(new RssChannelHttpMessageConverter());
   }

   if (jackson2XmlPresent) {
      Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
      if (this.applicationContext != null) {
         builder.applicationContext(this.applicationContext);
      }
      messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
   }
   else if (jaxb2Present) {
      messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
   }

   if (jackson2Present) {
      Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
      if (this.applicationContext != null) {
         builder.applicationContext(this.applicationContext);
      }
      messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
   }
   else if (gsonPresent) {
      messageConverters.add(new GsonHttpMessageConverter());
   }
   else if (jsonbPresent) {
      messageConverters.add(new JsonbHttpMessageConverter());
   }

   if (jackson2SmilePresent) {
      Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile();
      if (this.applicationContext != null) {
         builder.applicationContext(this.applicationContext);
      }
      messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
   }
   if (jackson2CborPresent) {
      Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor();
      if (this.applicationContext != null) {
         builder.applicationContext(this.applicationContext);
      }
      messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
   }
}

可以看到,如果系统导入了对应的包,就会创建对应类型的消息转换器

3.3.13.3 获取数据绑定器的初始化器
/**
 * Return the {@link ConfigurableWebBindingInitializer} to use for
 * initializing all {@link WebDataBinder} instances.
 */
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer(
    FormattingConversionService mvcConversionService, Validator mvcValidator) {

    //直接new一个数据绑定器的初始化器
    ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
    //统一转换服务
    initializer.setConversionService(mvcConversionService);
    //校验器
    initializer.setValidator(mvcValidator);
    /**
     * 此处调用getMessageCodesResolver()方法,
     * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
     * getMessageCodesResolver()方法,获取用户配置的消息解码器
     * 只允许一个配置类重写getMessageCodesResolver()方法返回一个消息解码器
     */
    MessageCodesResolver messageCodesResolver = getMessageCodesResolver();
    if (messageCodesResolver != null) {
        initializer.setMessageCodesResolver(messageCodesResolver);
    }
    return initializer;
}

该方法应用用户自定义配置,初始化一个ConfigurableWebBindingInitializer数据绑定器的初始化器

3.3.13.4 获取所有用户自定义的参数解析器
/**
 * Provide access to the shared custom argument resolvers used by the
 * {@link RequestMappingHandlerAdapter} and the {@link ExceptionHandlerExceptionResolver}.
 * <p>This method cannot be overridden; use {@link #addArgumentResolvers} instead.
 * @since 4.3
 */
protected final List<HandlerMethodArgumentResolver> getArgumentResolvers() {
    if (this.argumentResolvers == null) {
        this.argumentResolvers = new ArrayList<>();
        /**
         * 此处调用addArgumentResolvers(list)方法,
         * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
         * addArgumentResolvers(list)方法,向这个集合中添加用户自定义的参数解析器
         */
        addArgumentResolvers(this.argumentResolvers);
    }
    return this.argumentResolvers;
}

该方法获取到所有配置类addArgumentResolvers(list)方法配置的参数解析器

3.3.13.5 获取所有用户自定义的返回值处理器
/**
 * Provide access to the shared return value handlers used by the
 * {@link RequestMappingHandlerAdapter} and the {@link ExceptionHandlerExceptionResolver}.
 * <p>This method cannot be overridden; use {@link #addReturnValueHandlers} instead.
 * @since 4.3
 */
protected final List<HandlerMethodReturnValueHandler> getReturnValueHandlers() {
    if (this.returnValueHandlers == null) {
        this.returnValueHandlers = new ArrayList<>();
        /**
         * 此处调用addReturnValueHandlers(list)方法,
         * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
         * addReturnValueHandlers(list)方法,向这个集合中添加用户自定义的返回值处理器
         */
        addReturnValueHandlers(this.returnValueHandlers);
    }
    return this.returnValueHandlers;
}

该方法获取到所有配置类addReturnValueHandlers(list)方法配置的返回值处理器

3.3.14 注册HandlerFunctionAdapter

/**
 * Returns a {@link HandlerFunctionAdapter} for processing requests through
 * {@linkplain org.springframework.web.servlet.function.HandlerFunction handler functions}.
 * @since 5.2
 */
@Bean
public HandlerFunctionAdapter handlerFunctionAdapter() {
   return new HandlerFunctionAdapter();
}

3.3.9章节中注册的RouterFunctionMapping对应

3.3.15 注册FormattingConversionService

/**
 * Return a {@link FormattingConversionService} for use with annotated controllers.
 * <p>See {@link #addFormatters} as an alternative to overriding this method.
 */
@Bean
public FormattingConversionService mvcConversionService() {
    //DefaultFormattingConversionService的构造方法会自动注册一些默认的格式化器,类型转换器
    FormattingConversionService conversionService = new DefaultFormattingConversionService();
    /**
     * 此处调用addFormatters(conversionService)方法,
     * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
     * addFormatters(conversionService)方法,向这个格式转换服务中添加用户自定义的配置
     * 比如注册格式化器,类型转换器等
     */
    addFormatters(conversionService);
    return conversionService;
}

FormattingConversionService注册到容器之前,允许用户向这个格式转换服务中添加自定义的配置

3.3.16 注册Validator

/**
 * Return a global {@link Validator} instance for example for validating
 * {@code @ModelAttribute} and {@code @RequestBody} method arguments.
 * Delegates to {@link #getValidator()} first and if that returns {@code null}
 * checks the classpath for the presence of a JSR-303 implementations
 * before creating a {@code OptionalValidatorFactoryBean}.If a JSR-303
 * implementation is not available, a no-op {@link Validator} is returned.
 */
@Bean
public Validator mvcValidator() {
    /**
     * 此处调用getValidator()方法,
     * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
     * getValidator()方法,获取用户配置的唯一的那一个Validator对象
     * 比如注册格式化器,类型转换器等
     */
    Validator validator = getValidator();
    /**
     * 用户未手动配置Validator,但是
     * 导入了Validator对应的包,于是自动使用反射实例化一个Validator对象
     */
    if (validator == null) {
        if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
            Class<?> clazz;
            try {
                String className = "org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean";
                clazz = ClassUtils.forName(className, WebMvcConfigurationSupport.class.getClassLoader());
            }
            catch (ClassNotFoundException | LinkageError ex) {
                throw new BeanInitializationException("Failed to resolve default validator class", ex);
            }
            validator = (Validator) BeanUtils.instantiateClass(clazz);
        }
        //不校验的Validator
        else {
            validator = new NoOpValidator();
        }
    }
    return validator;
}

向容器中注册一个Validator,来源有三个地方

  • 用户在WebMvcConfigurer配置类中配置的惟一的Validator
  • 导入了Validator包,然后系统自动使用反射实例化一个Validator对象
  • 没有导入Validator包,那么就创建一个NoOpValidator对象,这个对象里面逻辑是空的,不进行校验

3.3.17 注册CompositeUriComponentsContributor

/**
 * Return an instance of {@link CompositeUriComponentsContributor} for use with
 * {@link org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder}.
 * @since 4.0
 */
@Bean
public CompositeUriComponentsContributor mvcUriComponentsContributor(
    @Qualifier("mvcConversionService") FormattingConversionService conversionService,
    @Qualifier("requestMappingHandlerAdapter") RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
    return new CompositeUriComponentsContributor(
        requestMappingHandlerAdapter.getArgumentResolvers(), conversionService);
}

这个暂时还不知道有什么用途,跳过

3.3.18 注册HttpRequestHandlerAdapter

/**
 * Returns a {@link HttpRequestHandlerAdapter} for processing requests
 * with {@link HttpRequestHandler HttpRequestHandlers}.
 */
@Bean
public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {
    return new HttpRequestHandlerAdapter();
}

可用来处理器3.3.103.3.12章节中注册的SimpleUrlHandlerMapping返回的处理器

它只能处理实现了HttpRequestHandler接口的处理器

3.3.19 注册SimpleControllerHandlerAdapter

/**
 * Returns a {@link SimpleControllerHandlerAdapter} for processing requests
 * with interface-based controllers.
 */
@Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
   return new SimpleControllerHandlerAdapter();
}

可用来处理器3.3.7章节中注册的SimpleUrlHandlerMapping返回的处理器

它只能处理实现了Controller接口的处理器

3.3.20 注册HandlerExceptionResolver

/**
 * Returns a {@link HandlerExceptionResolverComposite} containing a list of exception
 * resolvers obtained either through {@link #configureHandlerExceptionResolvers} or
 * through {@link #addDefaultHandlerExceptionResolvers}.
 * <p><strong>Note:</strong> This method cannot be made final due to CGLIB constraints.
 * Rather than overriding it, consider overriding {@link #configureHandlerExceptionResolvers}
 * which allows for providing a list of resolvers.
 */
@Bean
public HandlerExceptionResolver handlerExceptionResolver(
    @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
    //处理器异常解析器
    List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
    /**
     * 此处调用configureHandlerExceptionResolvers(list)方法,
     * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
     * configureHandlerExceptionResolvers(list)方法,向这个集合中添加用户自定义的
     * 处理器异常解析器。注意:通过此方法添加处理器异常解析器,springmvc容器就不会注册默认的
     * 处理器异常解析器了
     */
    configureHandlerExceptionResolvers(exceptionResolvers);
    if (exceptionResolvers.isEmpty()) {
        //添加默认的处理器异常解析器
        addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
    }
    /**
     * 此处调用extendHandlerExceptionResolvers(list)方法,
     * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
     * extendHandlerExceptionResolvers(list)方法,向这个集合中添加处理器异常解析器
     * 这个方法是在系统默认的处理器异常解析器基础额外添加用户自定义的处理器异常解析器
     */
    extendHandlerExceptionResolvers(exceptionResolvers);
    //组合模式,多个处理器异常解析器完成解析功能
    HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
    composite.setOrder(0);
    composite.setExceptionResolvers(exceptionResolvers);
    return composite;
}

如果用户未自定义处理器异常解析器,那么springmvc就会添加一些默认的处理器异常解析器

/**
 * A method available to subclasses for adding default
 * {@link HandlerExceptionResolver HandlerExceptionResolvers}.
 * <p>Adds the following exception resolvers:
 * <ul>
 * <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
 * {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
 * <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with
 * {@link org.springframework.web.bind.annotation.ResponseStatus}.
 * <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring exception types
 * </ul>
 */
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,
                                                         ContentNegotiationManager mvcContentNegotiationManager) {

    //创建一个使用@ExceptionHandler注解方法处理异常的异常解析器
    ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
    exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
    exceptionHandlerResolver.setMessageConverters(getMessageConverters());
    exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
    exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
    if (jackson2Present) {
        exceptionHandlerResolver.setResponseBodyAdvice(
            Collections.singletonList(new JsonViewResponseBodyAdvice()));
    }
    if (this.applicationContext != null) {
        exceptionHandlerResolver.setApplicationContext(this.applicationContext);
    }
    exceptionHandlerResolver.afterPropertiesSet();
    //1
    exceptionResolvers.add(exceptionHandlerResolver);

    ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
    responseStatusResolver.setMessageSource(this.applicationContext);
    //2
    exceptionResolvers.add(responseStatusResolver);

    //3
    exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}


/**
 * Protected method for plugging in a custom subclass of
 * {@link ExceptionHandlerExceptionResolver}.
 * @since 4.3
 */
protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
    return new ExceptionHandlerExceptionResolver();
}

默认注册了三个异常解析器

  • ExceptionHandlerExceptionResolver @ExceptionHandler注解方法处理异常
  • ResponseStatusExceptionResolver @ResponseStatus注解处理异常,响应指定状态
  • DefaultHandlerExceptionResolver 默认的异常解析器,根据异常类型响应指定的状态码

3.3.21 注册ViewResolver

/**
 * Register a {@link ViewResolverComposite} that contains a chain of view resolvers
 * to use for view resolution.
 * By default this resolver is ordered at 0 unless content negotiation view
 * resolution is used in which case the order is raised to
 * {@link org.springframework.core.Ordered#HIGHEST_PRECEDENCE
 * Ordered.HIGHEST_PRECEDENCE}.
 * <p>If no other resolvers are configured,
 * {@link ViewResolverComposite#resolveViewName(String, Locale)} returns null in order
 * to allow other potential {@link ViewResolver} beans to resolve views.
 * @since 4.1
 */
@Bean
public ViewResolver mvcViewResolver(
    @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
    //视图解析器注册中心
    ViewResolverRegistry registry =
        new ViewResolverRegistry(contentNegotiationManager, this.applicationContext);
    /**
     * 此处调用configureViewResolvers(registry)方法,
     * 就会带着WebMvcConfigurerComposite中所有的配置类对象都调用一次
     * configureViewResolvers(registry)方法,向这个注册中心中添加用户自定义的
     * 视图解析器
     */
    configureViewResolvers(registry);

    //用户未在配置类中注册视图解析器
    if (registry.getViewResolvers().isEmpty() && this.applicationContext != null) {
        //获取容器中所有视图解析器的beanName
        String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this.applicationContext, ViewResolver.class, true, false);
        //只有一个,说明就是当前这个视图解析器mvcViewResolver
        if (names.length == 1) {
            //添加一个默认的视图解析器InternalResourceViewResolver
            registry.getViewResolvers().add(new InternalResourceViewResolver());
        }
    }

    //组合模式,多个视图解析器先后解析一个视图,使用第一个能解析的视图解析器解析
    ViewResolverComposite composite = new ViewResolverComposite();
    composite.setOrder(registry.getOrder());
    composite.setViewResolvers(registry.getViewResolvers());
    if (this.applicationContext != null) {
        composite.setApplicationContext(this.applicationContext);
    }
    if (this.servletContext != null) {
        composite.setServletContext(this.servletContext);
    }
    return composite;
}

有两种配置视图解析器的方式

  • 直接放入容器中,可以自动被识别为视图解析器
  • 通过WebMvcConfigurer配置类的configureViewResolvers(registry)方法注册用户自定义的视图解析器

需要注意的是:两种不能同时使用,否则第一种的就不会生效了

3.3.22 注册HandlerMappingIntrospector

@Bean
@Lazy
public HandlerMappingIntrospector mvcHandlerMappingIntrospector() {
    return new HandlerMappingIntrospector();
}

这个暂时还不知道有什么用途,跳过

4 总结一下注册的组件

使用@EnableWebMvc<mvc:annotation-driven>一共会向容器中注册20个组件,分别如下所示

  1. requestMappingHandlerMapping->RequestMappingHandlerMapping
  2. mvcPathMatcher->PathMatcher
  3. mvcUrlPathHelper->UrlPathHelper
  4. mvcContentNegotiationManager->ContentNegotiationManager
  5. viewControllerHandlerMapping->SimpleUrlHandlerMapping
  6. beanNameHandlerMapping->BeanNameUrlHandlerMapping
  7. routerFunctionMapping->RouterFunctionMapping
  8. resourceHandlerMapping->SimpleUrlHandlerMapping
  9. mvcResourceUrlProvider->ResourceUrlProvider
  10. defaultServletHandlerMapping->SimpleUrlHandlerMapping
  11. requestMappingHandlerAdapter->RequestMappingHandlerAdapter
  12. handlerFunctionAdapter->HandlerFunctionAdapter
  13. mvcConversionService->FormattingConversionService
  14. mvcValidator->Validator
  15. mvcUriComponentsContributor->CompositeUriComponentsContributor
  16. httpRequestHandlerAdapter->HttpRequestHandlerAdapter
  17. simpleControllerHandlerAdapter->SimpleControllerHandlerAdapter
  18. handlerExceptionResolver->HandlerExceptionResolverComposite
  19. mvcViewResolver->ViewResolverComposite
  20. mvcHandlerMappingIntrospector->HandlerMappingIntrospector

5 NoOpValidator

该类是WebMvcConfigurationSupport的嵌套类,表示不使用校验功能,可以看到,它的实现为空

private static final class NoOpValidator implements Validator {

   @Override
   public boolean supports(Class<?> clazz) {
      return false;
   }

   @Override
   public void validate(@Nullable Object target, Errors errors) {
   }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值