SpringMVC自动配置原理

SpringMVC自动配置原理

官方文档

1.Spring MVC auto-configuration

Spring Boot 自动配置好了SpringMVC,以下是SpringBoot对SpringMVC的默认配置:(WebMvcAutoConfiguration)

1、ContentNegotiatingViewResolver 和 BeanNameViewResolver

  • 自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染)

  • ContentNegotiatingViewResolver:组合所有的视图解析器的;

  • 如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;

public class WebMvcAutoConfiguration {
    @Bean
    @ConditionalOnBean(View.class)
    @ConditionalOnMissingBean
    public BeanNameViewResolver beanNameViewResolver() {
        BeanNameViewResolver resolver = new BeanNameViewResolver();
        resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
        return resolver;
    }

    @Bean
    @ConditionalOnBean(ViewResolver.class)
    @ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
    public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
        ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
        resolver.setContentNegotiationManager(
            beanFactory.getBean(ContentNegotiationManager.class));
        // ContentNegotiatingViewResolver uses all the other view resolvers to locate
        // a view so it should have a high precedence
        resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return resolver;
    }
}

2、支持提供静态资源,包括对webjar的支持,静态资源文件夹路径webjars、静态index.html首页访问和favicon.ico 图像显示。

public class WebMvcAutoConfiguration {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        if (!this.resourceProperties.isAddMappings()) {
            logger.debug("Default resource handling disabled");
            return;
        }
        Integer cachePeriod = this.resourceProperties.getCachePeriod();
        if (!registry.hasMappingForPattern("/webjars/**")) {
            customizeResourceHandlerRegistration(
                registry.addResourceHandler("/webjars/**")
                .addResourceLocations(
                    "classpath:/META-INF/resources/webjars/")
                .setCachePeriod(cachePeriod));
        }
        String staticPathPattern = this.mvcProperties.getStaticPathPattern();
        if (!registry.hasMappingForPattern(staticPathPattern)) {
            customizeResourceHandlerRegistration(
                registry.addResourceHandler(staticPathPattern)
                .addResourceLocations(
                    this.resourceProperties.getStaticLocations())
                .setCachePeriod(cachePeriod));
        }
    }
}

3、自动注册了 ConverterGenericConverterFormatter

  • Converter:转换器; 类型转换使用Converter;

  • Formatter 格式化器; "2017.12.17"格式化成Date

    自己自定义添加的格式化器转换器,我们只需要放在容器中即可

@Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format") //在文件中配置日期格式化的规则
public Formatter<Date> dateFormatter() {
    return new DateFormatter(this.mvcProperties.getDateFormat()); //日期格式化组件
}

4、Http消息转换器HttpMessageConverters

  • HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User---转换为---> Json;

  • HttpMessageConverters 是从容器中确定;获取所有的HttpMessageConverter;

如果你需要添加或自定义转换器,你可以使用Spring Boot的HttpMessageConverters类,如下所示:

import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;

@Configuration
public class MyConfiguration {

	@Bean
	public HttpMessageConverters customConverters() {
		HttpMessageConverter<?> additional = ...
		HttpMessageConverter<?> another = ...
		return new HttpMessageConverters(additional, another);
	}

}

上下文中出现的任何HttpMessageConverter bean都被添加到转换器列表中。您也可以用相同的方法重写默认转换器。

5、定义错误代码生成规则 MessageCodesResolver

6、web数据绑定器 ConfigurableWebBindingInitializer

我们可以配置一个ConfigurableWebBindingInitializer来替换默认的;(添加到容器)

@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
    @Override
    protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
        try {
            return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
        }
        catch (NoSuchBeanDefinitionException ex) {
            return super.getConfigurableWebBindingInitializer();
        }
    }
}

public class ConfigurableWebBindingInitializer implements WebBindingInitializer {
    // 初始化WebDataBinder数据绑定器
    @Override
    public void initBinder(WebDataBinder binder, WebRequest request) {
        binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
        if (this.directFieldAccess) {
            binder.initDirectFieldAccess();
        }
        if (this.messageCodesResolver != null) {
            binder.setMessageCodesResolver(this.messageCodesResolver);
        }
        if (this.bindingErrorProcessor != null) {
            binder.setBindingErrorProcessor(this.bindingErrorProcessor);
        }
        if (this.validator != null && binder.getTarget() != null &&
            this.validator.supports(binder.getTarget().getClass())) {
            binder.setValidator(this.validator);
        }
        if (this.conversionService != null) {
            binder.setConversionService(this.conversionService);
        }
        if (this.propertyEditorRegistrars != null) {
            for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
                propertyEditorRegistrar.registerCustomEditors(binder);
            }
        }
    }
}

在包org.springframework.boot.autoconfigure.web下是web的所有自动场景;

MVC配置官网文档

如果你想保留Spring Boot MVC特性,并且你只想添加额外的MVC配置(如:拦截器,格式化器,视图控制器等),你可以添加你自己的类型为WebMvcConfigurerAdapter@Configuration类,但使用 @EnableWebMvc

如果你想提供RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver的自定义实例,你可以声明一个WebMvcRegistrationsAdapter实例来提供这样的组件。

如果你想完全控制Spring MVC,你可以添加你自己的@Configuration注释与@EnableWebMvc

2.扩展SpringMVC

编写一个@Configuration注解类,并且类型要为WebMvcConfigurer(实现WebMvcConfigurer接口),还 不能 标注@EnableWebMvc注解

既保留了所有的自动配置,也能用我们扩展的配置

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

// 因为类型要求为WebMvcConfigurer,所以我们实现其接口
// 可以使用自定义类扩展MVC的功能
// 如果我们要扩展springmvc,官方推荐这样做
@Configuration
public class MySpringMvcConfig implements WebMvcConfigurer {
    // 视图跳转
    @Override
    public void addViewControllers(ViewControllerRegistry registry){
        // 浏览器发送/temp请求跳转到test视图页面
        registry.addViewController("/temp").setViewName("test");
    }
}

各种配置都是这么扩展 在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置

原理:

​ 1)WebMvcAutoConfiguration是SpringMVC的自动配置类

​ 2)在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)

public class WebMvcAutoConfiguration {
    @Configuration
	@Import(EnableWebMvcConfiguration.class)
	@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
	public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {

    }
}

@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {

}

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    
    //从容器中获取所有的WebMvcConfigurer
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
}

class WebMvcConfigurerComposite implements WebMvcConfigurer {

	private final List<WebMvcConfigurer> delegates = new ArrayList<WebMvcConfigurer>();
    
    // 以视图解析为例:将所有的WebMvcConfigurer相关配置都来一起调用
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addViewControllers(registry);
        }
    }
}

3)容器中所有的WebMvcConfigurer都会一起起作用;

4)我们的配置类也会被调用;

效果:SpringMVC的自动配置和我们的扩展配置都会起作用;

3.全面接管SpringMVC

使用@EnableWebMvc注解,SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了

我们需要在配置类中添加@EnableWebMvc即可

//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
       // super.addViewControllers(registry);
        //浏览器发送 /hguo 请求来到 视图名success页面
        registry.addViewController("/hguo").setViewName("success");
    }
}

原理:

为什么@EnableWebMvc自动配置就失效了;

1)查看注解@EnableWebMvc可以看到@Import(DelegatingWebMvcConfiguration.class)引入了这个组件

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

2)DelegatingWebMvcConfiguration

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {}

3)而在WebMvcAutoConfiguration自动配置类中有注解@ConditionalOnMissingBean对容器中组件进行判断

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
		WebMvcConfigurerAdapter.class })

//@ConditionalOnMissingBean判断容器中没有WebMvcConfigurationSupport这个组件的时候,这个自动配置类才生效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

4)@EnableWebMvcWebMvcConfigurationSupport组件导入进来;

5)导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能;

4.如何修改SpringBoot的默认配置

模式:

​ 1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;

​ 2)、在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置

​ 3)、在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置

posted @ 2022-09-12 18:15  Lz_蚂蚱  阅读(102)  评论(0)    收藏  举报