欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

七、SpringBoot之SpringMVC自动配置原理

程序员文章站 2022-04-19 22:32:41
...

SpringMvc自动配置文档

1、Spring MVC 自动配置

Spring Boot 自动配置好了SpringMVC

以下是SpringBoot对SpringMVC的默认配置:都在WebMvcAutoConfiguration.java这个类里

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

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

WebMvcAutoConfiguration.java        
        @Bean //给容器中添加ContentNegotiationManager组件
        @ConditionalOnBean({ViewResolver.class})
        @ConditionalOnMissingBean(
            name = {"viewResolver"},
            value = {ContentNegotiatingViewResolver.class}
        )
        public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
            ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
            resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
            resolver.setOrder(-2147483648);
            return resolver;
        }
    //初始化方法
    protected void initServletContext(ServletContext servletContext) {
        //用BeanFactoryUtils工具类从容器中获取所有的视图解析器,这个解析器就作为它解析的所有解析器
        Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
        ViewResolver viewResolver;
        if (this.viewResolvers == null) {
            this.viewResolvers = new ArrayList(matchingBeans.size());
            Iterator var3 = matchingBeans.iterator();

            while(var3.hasNext()) {
                viewResolver = (ViewResolver)var3.next();
                if (this != viewResolver) {
                    this.viewResolvers.add(viewResolver);
                }
            }
        } else {
            for(int i = 0; i < this.viewResolvers.size(); ++i) {
                viewResolver = (ViewResolver)this.viewResolvers.get(i);
                if (!matchingBeans.contains(viewResolver)) {
                    String name = viewResolver.getClass().getName() + i;
                    this.obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolver, name);
                }
            }
        }

        if (this.viewResolvers.isEmpty()) {
            this.logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the 'viewResolvers' property on the ContentNegotiatingViewResolver");
        }

        AnnotationAwareOrderComparator.sort(this.viewResolvers);
        this.cnmFactoryBean.setServletContext(servletContext);
    }
ContentNegotiatingViewResolver.java
    @Nullable
    //解析视图
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
        Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
        List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
        if (requestedMediaTypes != null) {
            //获取候选视图对象
            List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
            //选择适合的视图对象,然后把这个对象返回
            View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
            if (bestView != null) {
                return bestView;
            }
        }

        if (this.useNotAcceptableStatusCode) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
            }

            return NOT_ACCEPTABLE_VIEW;
        } else {
            this.logger.debug("No acceptable view found; returning null");
            return null;
        }
    }
  //获取候选视图对象
  private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
        List<View> candidateViews = new ArrayList();
        if (this.viewResolvers != null) {
            Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");
            //把所有视图解析器拿来遍历,挨个解析
            Iterator var5 = this.viewResolvers.iterator();

            while(var5.hasNext()) {
                ViewResolver viewResolver = (ViewResolver)var5.next();
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    candidateViews.add(view);
                }

                Iterator var8 = requestedMediaTypes.iterator();

                while(var8.hasNext()) {
                    MediaType requestedMediaType = (MediaType)var8.next();
                    List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
                    Iterator var11 = extensions.iterator();

                    while(var11.hasNext()) {
                        String extension = (String)var11.next();
                        String viewNameWithExtension = viewName + '.' + extension;
                        view = viewResolver.resolveViewName(viewNameWithExtension, locale);
                        if (view != null) {
                            candidateViews.add(view);
                        }
                    }
                }
            }
        }

        if (!CollectionUtils.isEmpty(this.defaultViews)) {
            candidateViews.addAll(this.defaultViews);
        }

        return candidateViews;
    }

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

        @Bean
	public ViewResolver myViewReolver(){
	    return new MyViewResolver();
        }
	private static class MyViewResolver implements ViewResolver{

            @Override
            public View resolveViewName(String viewName, Locale locale) throws Exception {
                return null;
            }
        }
  • 自动配置了静态资源文件夹路径,webjars(前面用过)
  • 自动配置了静态首页访问(index.html)
  • 自动配置来favicon.ico图标访问
  • Converter:转换器;类型转换使用Converter
  • Formatter 格式化器;比如日期格式化转换
        //添加格式化器    
        public void addFormatters(FormatterRegistry registry) {
            //从容器中获取所有的Converter,再遍历
            Iterator var2 = this.getBeansOfType(Converter.class).iterator();

            while(var2.hasNext()) {
                Converter<?, ?> converter = (Converter)var2.next();
                //自己添加的格式化器转换器,我们只需要放在容器中即可
                registry.addConverter(converter);
            }
            //从容器中获取所有的GenericConverter,再遍历

            var2 = this.getBeansOfType(GenericConverter.class).iterator();

            while(var2.hasNext()) {
                GenericConverter converter = (GenericConverter)var2.next();
                registry.addConverter(converter);
            }

            //从容器中获取所有的Formatter,再遍历
            var2 = this.getBeansOfType(Formatter.class).iterator();

            while(var2.hasNext()) {
                Formatter<?> formatter = (Formatter)var2.next();
                registry.addFormatter(formatter);
            }

        }

 

  • ​HttpMessageConverter:SpringMVC用来转换Http请求和响应的;例如User转换成Json;
        //只有一个有参构造器的情况下,每一个参数的值都是要从容器中拿
        //HttpMessageConverters 是从容器中确定,获取所有的HttpMessageConverter;
        //自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中(@Bean,@Component)
        public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
            this.resourceProperties = resourceProperties;
            this.mvcProperties = mvcProperties;
            this.beanFactory = beanFactory;
            this.messageConvertersProvider = messageConvertersProvider;
            this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
        }
  • MessageCodesResolver:定义错误代码生成规则

         public MessageCodesResolver getMessageCodesResolver() {
            //从配置类中拿到一个配置,定义错误代码生成规则的配置
            if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
                DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
                resolver.setMessageCodeFormatter(this.mvcProperties.getMessageCodesResolverFormat());
                return resolver;
            } else {
                return null;
            }
        }
  • ConfigurableWebBindingInitializer:初始化WebDataBinder;例如:请求数据与JavaBean绑定

     protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
            try {
                //从容器中拿到ConfigurableWebBindingInitializer
                //我们可以配置一个ConfigurableWebBindingInitializer添加到容器里来替换默认的;
                return (ConfigurableWebBindingInitializer)this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
            } catch (NoSuchBeanDefinitionException var2) {
                //拿不到调用父类的ConfigurableWebBindingInitializer
                return super.getConfigurableWebBindingInitializer();
            }
        }

web的所有自动场景都在org.springframework.boot.autoconfigure.web这个包里。

2、扩展SpringMVC

  • Spring以前需要写配置文件
    <!--Spring视图映射配置-->
    <mvc:view-controller path="/hello" view-name="success"/>
    <!--定义Spring的拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/hello"/>
            <bean></bean>
        </mvc:interceptor>
    </mvc:interceptors>
  • SpringBoot不用写配置文件,编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc;

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

//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
//WebMvcConfigurerAdapter实现WebMvcConfigurer接口,实现了接口里的方法都是空方法
//自定义配置类想配置哪个属性,就重写对应的方法就好了
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //super.addViewControllers(registry);
        //浏览器发送 /atguigu请求来到success页面
        registry.addViewController("/atguigu").setViewName("success");
    }
}
  • 原理:

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

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

    //WebMvcAutoConfiguration.java里有一个内部类WebMvcAutoConfigurationAdapter
    //WebMvcAutoConfigurationAdapter实现WebMvcConfigurer接口,实现了接口里的方法
    @Configuration
    //在做其他自动配置时会导入@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
    @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
    @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {
        private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);

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

@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {


@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    public DelegatingWebMvcConfiguration() {}

    @Autowired(
        required = false
    )
    //从容器中获取所有的WebMvcConfigurer,然后把WebMvcConfigurer赋值到configurers里
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }

    }

     //其中的一个参考实现,将从容器中获取所有的WebMvcConfigurer相关配置都来一起调用
     //容器中所有的WebMvcConfigurer都会一起起作用;
     //我们自己的配置类也会被调用
     protected void addViewControllers(ViewControllerRegistry registry) {
        this.configurers.addViewControllers(registry);
    }

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

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

3、全面接管SpringMVC

  • @EnableWebMvc

SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了,我们值需要在配置类中添加@EnableWebMvc即可;

//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@EnableWebMvc //全面接管SpringMVC,所有的SpringMVC的自动配置都失效了
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //super.addViewControllers(registry);
        //浏览器发送 /atguigu请求来到success页面
        registry.addViewController("/atguigu").setViewName("success");
    }
}
  • 原理:

[email protected]的核心

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

2.SpringMVC自动配置类表示容器中没有WebMvcConfigurationSupport这个组件的时候,这个自动配置类才生效,@EnableWebMvc将WebMvcConfigurationSupport组件导入进来;所以所有的SpringMVC的自动配置都失效了,只导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能;

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

3.SpringMVC自动配置类表示容器中没有WebMvcConfigurationSupport这个组件的时候,这个自动配置类才生效

@Configuration
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
//容器中没有这个组件的时候,这个自动配置类才生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {

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

模式:

  • SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;
  • 在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
  • ​ 在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置