七、SpringBoot之SpringMVC自动配置原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_29479041/article/details/82981484

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");
    }
}
  • 原理:

1.@EnableWebMvc的核心

@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帮助我们进行定制配置

猜你喜欢

转载自blog.csdn.net/qq_29479041/article/details/82981484