SpringBoot(16) —— MVC配置原理

在这里插入图片描述


  • 在进行项目编写前,我们还需要知道一个东西,就是SpringBoot对SpringMVC做了哪些自动配置,我们如何定制自己对于springMVC的配置,并如何实现将自己定义的配置加入到springBoot自动配置的识别队列中,即如何对springBoot对springMVC的自动配置进行扩展(后面学习的核心)
  • 只有把这些都搞清楚了,我们在之后使用才会更加得心应手
  • 途径一:源码分析,途径二:官方文档!
  • SpringMVC官方doc.

在这里插入图片描述

springboot为springmvc提供了自动配置,可以很好地与大多数应用程序配合使用
自动配置在Spring的默认设置之上添加了以下功能:
- 包含内容协商视图解析器和Bean名称视图解析器Bean【即springBoot自动配置了视图解析器】
- 对服务静态资源的支持,包括对webjar的支持(本文稍后将介绍)【即springBoot自动配置了怎么获取项目的静态资源】
- 转换器、泛型转换器和格式化程序bean的自动注册
- 对Http消息转换器的支持(本文档后面将介绍)
- 消息代码解析程序的自动注册(将在本文档后面介绍)
- 静态索引.html支持
- 自定义Favicon支持(本文档后面将介绍)【自定义图标】
- 自动使用可配置的Web绑定初始值设定项bean(本文后面将介绍)

如果您想保留Spring Boot MVC功能,并且希望添加其他MVC配置(拦截器、格式化程序、视图控制器和其他功能),
则可以添加自己的@configuration类,类型为WebMvcConfigurer ,但不添加@EnableWebMvc。
如果希望提供RequestMappingHandlerMapping、RequestMappingHandlerAdapter或
ExceptionHandlerExceptionResolver的自定义实例,则可以声明WebMVCregisterationAdapter实例以提供此类组件。
  • 上面的文字中介绍了springBoot对于springMVC究竟进行了哪些自动配置,这些自动配置我们可以使用默认的,也可以在它的基础上进行"扩展/增强"(就是加入我们自定义的配置,和原来的自动配置一起让这一项springMVC的配置效果增强)

  • 参考官方文档,如果我们想要扩展springBoot对于springMVC的自动配置,方法为:我们可以去定义一个类,这个类的定义上要有注解@configuration的标注,并且实现WebMvcConfigurer接口
  • 首先WebMvcConfigurer是个什么东西?是一个接口?抽象类?还是普通类?
    在这里插入图片描述
    其实上图已经可以知道它是一个接口了

在这里插入图片描述

  • 这个接口的作用
    • ①MyMvcConfig被作为配置类加载到spring容器的时候,容器可以识别这个类里面的东西是来增加springMVC的配置的,这样就将这个配置类和其他配置类分开处理了;
    • ②并且实现这个接口,我们就可以使用springBoot官方给我们留的扩展springMVC配置的接口方法,使用这些方法,spring容器进能够区分和识别这个类到底增强了哪些springMVC的配置,并对应的将这些配置增加到自动配置的选项中,这样springBoot项目在进行自动配置的时候就可以获取到自动配置+我们自定义的配置选项了,并将这些设置都生效,这样就实现了springMVC默认自动配置的增强

  • 自定义类MyMvcConfig,使用注解@Configuration进行标注(这样这个类将会在项目启动的时候被视为一个配置类被加载到spring容器中,为spring容器添加配置组件),并实现接口WebMvcConfigurer
    package com.thhh.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    //这个类用于扩展springBoot原生封装的自动配置好的springMVC
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    
    }
    
  • 有了上面这个类,我们就可以在它的内部扩展/增强 springBoot对于springMVC的自动配置设置了

扩展/增强 视图解析器

  • 原来直接使用springMVC的时候我们的视图解析器需要自己手动的去spring容器中装配,需要自己去写前缀和后缀
    在这里插入图片描述
  • 现在使用了springBoot,它有很多的自动配置设置,我们可以查看它的视图解析器的自动配置
  • 首先去WebMvcAutoConfiguration中去找,所有的web相关的自动配置都在这个类里面
    @Bean
    @ConditionalOnMissingBean
    public InternalResourceViewResolver defaultViewResolver() {
    	InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    	resolver.setPrefix(this.mvcProperties.getView().getPrefix());
    	resolver.setSuffix(this.mvcProperties.getView().getSuffix());
    	return resolver;
    }
    
  • 可以发现这个方法在获取默认的视图解析器,并设置了视图解析器的前缀和后缀(使用了mvcProperties类)
  • 点进mvcProperties类中查看前缀和后缀的配置
    		/**
    		 * Spring MVC view prefix.
    		 */
    		private String prefix;
    
    		/**
    		 * Spring MVC view suffix.
    		 */
    		private String suffix;
    
  • 可以发现视图解析器的前后缀并没有写在这个类里面,并且mvcProperties类使用了注解@ConfigurationProperties(prefix = “spring.mvc”),所以我们可以去默认的配置文件中看看默认的前缀后缀是什么
    在这里插入图片描述
    在这里插入图片描述
  • 这说明了springBoot本身就没有为我们设置视图解析器的前后缀,要设置需要我们自己在application配置文件中进行设置
    在这里插入图片描述
  • spring.mvc.view.prefix= 视图前缀
  • spring.mvc.view.suffix= 视图后缀

- 所以如果我们要自己配置一个视图解析器,想让他起作用,就需要将它作为bean装配到spring容器中作为容器的一个组件
- 现在的springBoot自动为我们装配了视图解析器,但是我们还没看过它的原理,下面就来看看springBoot是怎么为我们自动装配的视图解析器,并且自定义一个视图解析器


  • 找到类ContentNegotiatingViewResolver 的源码(为什么是这个类?因为Spring MVC Auto-configuration上来的第一个特点说的就是这个类。类名翻译过来就是 内容协商视图求解器,就是一个视图解析器,所以先从它开刀,下面的代码是这个类定义的简化,把不需要的都删除了)

    public class ContentNegotiatingViewResolver implements ViewResolver
    
  • 可以发现这个类实现了接口ViewResolver(视图解析器),我们把实现了ViewResolver接口的类也看作是一个视图解析器

    public interface ViewResolver {
        @Nullable
        View resolveViewName(String var1, Locale var2) throws Exception;
    }
    
  • 可以发现接口ViewResolver(视图解析器)内部只定义了一个方法resolveViewName()

  • 来看看ContentNegotiatingViewResolver 怎么实现的方法resolveViewName()

        @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;
                }
            }
    
            String mediaTypeInfo = this.logger.isDebugEnabled() && requestedMediaTypes != null ? " given " + requestedMediaTypes.toString() : "";
            if (this.useNotAcceptableStatusCode) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo);
                }
    
                return NOT_ACCEPTABLE_VIEW;
            } else {
                this.logger.debug("View remains unresolved" + mediaTypeInfo);
                return null;
            }
        }
    

    在这里插入图片描述
    在这里插入图片描述

  • 现在我们知道了,springBoot首先会将所有的视图解析器从spring容器中获取出来装入一个list集合返回,然后再选出当前场景下最适合的视图解析器(调用方法getBestView())

  • 为什么是从容器中获取呢?因为只有容器才会存有所有的视图解析器,因为解析器要起作用必须作为spring容器的一个bean,这在学习视图解析器的时候就已经说过了

  • 拓展
    在这里插入图片描述

  • 所以如果我们自己想要定义一个视图解析器,需要写一个类实现接口ViewResolver(视图解析器),然后在有注解@Configuration的类中定义一个@Bean方法,使得在项目启动的时候将我们定义的视图解析器实例装配到spring容器中,容器将自动识别这个视图解析器


  • 注意:我们在增强springMVC默认配置功能的时候的自定义类怎么起作用,需要参考原来使用配置文件的步骤,像上面自定义的一个视图解析器要想起作用,因为原来使用它就是把它作为一个bean装配到spring容器中,所以现在还是按照原来的方法,定义一个标注了@Bean的方法将视图解析器注入spring容器中;在后面不管扩展什么样的功能,我们都需要参考原来spring配置文件是怎么装配它的,然后按照原来的方式进行等效的装配
  • 但是原来有一些东西的装配比较的麻烦,不是像上面视图解析器一样,定义一个@Bean,把类放进容器就能解决的,springBoot也想到了这一点,它基本将所有复杂的装配都封装成了接口WebMvcConfigurer的方法,我们在增强功能的时候,需要参照源码,看看源码是怎么实现的,然后自己定义一个类来照猫画虎实现相同功能,最后就在WebMvcConfigurer的实现类中调用对应增强这个功能的方法,在springBoot项目启动的时候,我们定义的MyMvcConfig类将被作为一个配置类为容器添加组件,而我们定义在定义的@Bean方法和springBoot封装的WebMvcConfigurer接口中的方法都将对应的去增强springMVC的配置
    在这里插入图片描述

  • 直接在刚刚定义的MyMvcConfig中定义一个内部类,方便使用

    //这个类用于扩展springBoot原生封装的自动配置好的springMVC
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    	//自定义的视图解析器
        public static class MyViewResolver implements ViewResolver {
            @Override
            public View resolveViewName(String s, Locale locale) throws Exception {
                //什么都不写
                return null;
            }
        }
    }
    
  • 写一个@Bean方法

    //这个类用于扩展springBoot原生封装的自动配置好的springMVC
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    
        //自定义的视图解析器
        public static class MyViewResolver implements ViewResolver {
            @Override
            public View resolveViewName(String s, Locale locale) throws Exception {
                //什么都不写
                return null;
            }
        }
        @Bean
        public ViewResolver myViewResolver(){
            return new MyViewResolver();
        }
    }
    
  • 这样,当spring容器扫描的时候就会将上面这个视图解析器bean装配到容器中,那么springBoot在加载全部视图解析器进行选择的时候,就会加载到我们定义的这个视图解析器了,至于选哪一个,怎么选还需要参考getBestView()的源码,这里就不展开了,先去验证springBoot项目选取视图解析器的时候会不会加载我们定义的这个视图解析器

  • 测试方法:在学习springMVC的时候我们知道,核心就是一个servlet DispatcherServlet,并且它里面的核心方法为dodispatcher(),所有的请求和响应都是从这个方法进入和返回的,所以我们可以在这个方法上打一个断点,再去前端请求controller提供的接口,然后看看调试信息中返回的视图有没有我们自己定义的那个视图解析器
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 从上面的测试结果我们可以发现:如果你想自定义/扩展视图解析器,你就可以在自定义的" @Configuration class of type WebMvcConfigurer but without @EnableWebMvc"(官方doc上对于自定义扩展mvc类的描述) mvc配置类中定义一个视图解析器      并为它创建一个@Bean方法,使它能够被加载到spring容器中,只要我们扩展的东西被加载到容器当中,springBoot对应的getCandidateXXX()方法就会从容器中加载到它,这样我们自定义/扩展的视图解析器就成为了springBoot执行的时候的一个选项
    - 【推广】既然视图解析器可以这么干,那么其他的springMVC的配置也都可以这么干,推论:如果你想用自定义/扩展一些springBoot项目中springMVC的功能,你只需要自定义一个实现了WebMvcConfigurer 接口并且有@Configuration注解标注的并且没有使用注解@EnableWebMvc标注的springMVC扩展配置类,在这个类中我们只需为我们自定义/扩展的功能的类定义一个@Bean方法,让它被加载到spring容器中,springBoot就会自动的去加载它,使用它(使用还是要根据具体的选择方法来设置选中我们自定义的设置来执行,这里还没涉及到)

  • 上面划掉的部分都是错误的结论,因为学习到后面就会发现,我们自定义的增强功能要加入到springBoot自动配置中起作用,不是只靠定义一个@Bean方法让这个类被装配到容器中就能实现的,具体要增强的功能需要参考原来使用spring配置文件的时候是怎么配置的,如果只是定义了一个bean节点,就可以通过@Bean方法实现功能增强;如果是比较麻烦的装配标签节点,我们一般就是借助WebMvcConfigurer接口封装的方法在将我们定义的增强操作加入到springBoot自动配置中,使其生效
    在这里插入图片描述


小结

  • 这么多的自动配置,原理都是一样的,通过这个WebMVC的自动配置原理分析,我们要学会一种学习方式,通过源码探究,得出结论,这个结论一定是属于自己的,而且一通百通
  • SpringBoot的底层,大量用到了这些设计细节思想,所以,没事需要多阅读源码,得出结论
  • SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的,如果有就用用户配置的,如果没有就用自动配置的(对应application文件中的配置和XXXproperties配置类中成员属性的默认值)
  • 如果有些组件可以存在多个,比如我们的视图解析器,就将用户配置的和自己默认的组合起来!(就像上面自定义视图解析器的测试结果一样,它将自己默认的和用户定义的都加载出来了)
  • 我们要做的就是编写一个注解@Configuration标注的类,并且这个类要实现WebMvcConfigurer接口,还不能标注@EnableWebMvc注解,因为注解@EnableWebMvc表示全面接管springBoot为我们配置的springMVC,即我们的WebMvcAutoConfiguration将不会被加载,这样原来springBoot对于web的自动配置就全部不起作用了,我们需要全部自己手动配置,这显然就失去了springBoot最大的优点"自动配置",所以在开发中我们基本不会使用注解@EnableWebMvc,最多就是增强springBoot对于springMVC的默认配置
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值