• SpringBoot对于SpringMVC的支持


    创建项目

    请添加图片描述

    版本说明这里使用的 SpringBoot 2.0.0.Release

    SpringBoot对于SpringMVC的支持

      在之前的开发中很多场景下使用的是基于xml配置文件或者是Java配置类的方式来进行SpringMVC的配置。一般来讲,初始的步骤如下所示

    • 1、初始化SpringMVC的DispatcherServlet
    • 2、搭建转码过滤器,保证客户端请求进行正确的转码
    • 3、搭建视图解析器(View Resolver),告诉Spring从什么地方查找视图,以及这些视图使用什么语言编写等
    • 4、配置静态资源的位置
    • 5、配置所支持的地域以及资源bundle
    • 6、配置multipart解析器,保证文件上传能够正常工作
    • 7、将Tomcat或者Jetty包含能够在Web服务器上运行应用
    • 8、建立错误页面

      当然不止是上面这些工作。如下图所示,为SpringMVC核心的处理流程。

    核心处理流
    请添加图片描述

    功能流图
    请添加图片描述

    1.1 分发器和multipart配置

      首先在默认的配置文件中加入如下的一行代码,表示已debug模式运行SpringBoot的应用。

    debug=true
    
    • 1

    配置完成之后会看到控制台会打印出debug信息。

    DispatcherServletAutoConfiguration matched:
          - @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet' (OnClassCondition)
          - found 'session' scope (OnWebApplicationCondition)
    
       DispatcherServletAutoConfiguration.DispatcherServletConfiguration matched:
          - @ConditionalOnClass found required class 'javax.servlet.ServletRegistration' (OnClassCondition)
          - Default DispatcherServlet did not find dispatcher servlet beans (DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition)
    
       DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration matched:
          - @ConditionalOnClass found required class 'javax.servlet.ServletRegistration' (OnClassCondition)
          - DispatcherServlet Registration did not find servlet registration bean (DispatcherServletAutoConfiguration.DispatcherServletRegistrationCondition)
    
       DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration#dispatcherServletRegistration matched:
          - @ConditionalOnBean (names: dispatcherServlet; types: org.springframework.web.servlet.DispatcherServlet; SearchStrategy: all) found bean 'dispatcherServlet' (OnBeanCondition)
    
       EmbeddedWebServerFactoryCustomizerAutoConfiguration matched:
          - @ConditionalOnWebApplication (required) found 'session' scope (OnWebApplicationCondition)
    
       EmbeddedWebServerFactoryCustomizerAutoConfiguration.TomcatWebServerFactoryCustomizerConfiguration matched:
          - @ConditionalOnClass found required classes 'org.apache.catalina.startup.Tomcat', 'org.apache.coyote.UpgradeProtocol' (OnClassCondition)
    
       ErrorMvcAutoConfiguration matched:
          - @ConditionalOnClass found required classes 'javax.servlet.Servlet', 'org.springframework.web.servlet.DispatcherServlet' (OnClassCondition)
          - found 'session' scope (OnWebApplicationCondition)
    
       ErrorMvcAutoConfiguration#basicErrorController matched:
          - @ConditionalOnMissingBean (types: org.springframework.boot.web.servlet.error.ErrorController; SearchStrategy: current) did not find any beans (OnBeanCondition)
    
       ErrorMvcAutoConfiguration#errorAttributes matched:
          - @ConditionalOnMissingBean (types: org.springframework.boot.web.servlet.error.ErrorAttributes; SearchStrategy: current) did not find any beans (OnBeanCondition)
    
       ErrorMvcAutoConfiguration.DefaultErrorViewResolverConfiguration#conventionErrorViewResolver matched:
          - @ConditionalOnBean (types: org.springframework.web.servlet.DispatcherServlet; SearchStrategy: all) found bean 'dispatcherServlet'; @ConditionalOnMissingBean (types: org.springframework.boot.autoconfigure.web.servlet.error.DefaultErrorViewResolver; SearchStrategy: all) did not find any beans (OnBeanCondition)
    
       ErrorMvcAutoConfiguration.WhitelabelErrorViewConfiguration matched:
          - @ConditionalOnProperty (server.error.whitelabel.enabled) matched (OnPropertyCondition)
          - ErrorTemplate Missing did not find error template view (ErrorMvcAutoConfiguration.ErrorTemplateMissingCondition)
    
       ErrorMvcAutoConfiguration.WhitelabelErrorViewConfiguration#beanNameViewResolver matched:
          - @ConditionalOnMissingBean (types: org.springframework.web.servlet.view.BeanNameViewResolver; SearchStrategy: all) did not find any beans (OnBeanCondition)
    
       ErrorMvcAutoConfiguration.WhitelabelErrorViewConfiguration#defaultErrorView matched:
          - @ConditionalOnMissingBean (names: error; SearchStrategy: all) did not find any beans (OnBeanCondition)
    
       GenericCacheConfiguration matched:
          - Cache org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration automatic cache type (CacheCondition)
    
       HttpEncodingAutoConfiguration matched:
          - @ConditionalOnClass found required class 'org.springframework.web.filter.CharacterEncodingFilter' (OnClassCondition)
          - found 'session' scope (OnWebApplicationCondition)
          - @ConditionalOnProperty (spring.http.encoding.enabled) matched (OnPropertyCondition)
    
       HttpEncodingAutoConfiguration#characterEncodingFilter matched:
          - @ConditionalOnMissingBean (types: org.springframework.web.filter.CharacterEncodingFilter; SearchStrategy: all) did not find any beans (OnBeanCondition)
    
       HttpMessageConvertersAutoConfiguration matched:
          - @ConditionalOnClass found required class 'org.springframework.http.converter.HttpMessageConverter' (OnClassCondition)
    
       HttpMessageConvertersAutoConfiguration#messageConverters matched:
          - @ConditionalOnMissingBean (types: org.springframework.boot.autoconfigure.http.HttpMessageConverters; SearchStrategy: all) did not find any beans (OnBeanCondition)
    
       HttpMessageConvertersAutoConfiguration.StringHttpMessageConverterConfiguration matched:
          - @ConditionalOnClass found required class 'org.springframework.http.converter.StringHttpMessageConverter' (OnClassCondition)
          
      MultipartAutoConfiguration matched:
          - @ConditionalOnClass found required classes 'javax.servlet.Servlet', 'org.springframework.web.multipart.support.StandardServletMultipartResolver', 'javax.servlet.MultipartConfigElement' (OnClassCondition)
          - found 'session' scope (OnWebApplicationCondition)
          - @ConditionalOnProperty (spring.servlet.multipart.enabled) matched (OnPropertyCondition)
    
       MultipartAutoConfiguration#multipartConfigElement matched:
          - @ConditionalOnMissingBean (types: javax.servlet.MultipartConfigElement,org.springframework.web.multipart.commons.CommonsMultipartResolver; SearchStrategy: all) did not find any beans (OnBeanCondition)
    
       MultipartAutoConfiguration#multipartResolver matched:
          - @ConditionalOnMissingBean (types: org.springframework.web.multipart.MultipartResolver; SearchStrategy: all) did not find any beans (OnBeanCondition)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

      下面就来分析一下DispatcherServletAutoConfiguration配置类

    DispatcherServletAutoConfiguration

    
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Configuration
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass(DispatcherServlet.class)
    @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
    @EnableConfigurationProperties(ServerProperties.class)
    public class DispatcherServletAutoConfiguration {
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    首先会发现它是一个 @Configuration 注解标注的类,说明这是一个典型的Spring配置类。在这个配置类上标注了一个注解 @EnableConfigurationProperties(ServerProperties.class),这个注解表示使用了SpringBoot自动配置原理,对于配置文件与配置类进行了映射,在这里看到向这个配置中注入的自动配置类的 ServerProperties 这里就是在配置文件中以server开头的所有配置。例如在配置文件可以进行端口的设置,访问路径配置

    server.port=8080
    server.servlet.context-path=/hello
    
    • 1
    • 2

    在它的内部有一个静态内部类DispatcherServletConfiguration,这个内部类也是作为一个配置类存在,这里需要注意一个注解 @Conditional 这个注解表示满足条件之后才会向容器中注入对应的组件。这里会看到如果容器中有 DefaultDispatcherServletCondition 这个类才会进行处理。也就是说在这里需要一个默认的DispatcherServlet才会生效

    @Configuration
    @Conditional(DefaultDispatcherServletCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties(WebMvcProperties.class)
    protected static class DispatcherServletConfiguration {
    
    	private final WebMvcProperties webMvcProperties;
    
    	public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {
    		this.webMvcProperties = webMvcProperties;
    	}
    
    	@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    	public DispatcherServlet dispatcherServlet() {
    		DispatcherServlet dispatcherServlet = new DispatcherServlet();
    		dispatcherServlet.setDispatchOptionsRequest(
    			this.webMvcProperties.isDispatchOptionsRequest());
    			dispatcherServlet.setDispatchTraceRequest(
    			this.webMvcProperties.isDispatchTraceRequest());
    			dispatcherServlet.setThrowExceptionIfNoHandlerFound(
    		    this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
    		return dispatcherServlet;
    	}
    
    	@Bean
    	@ConditionalOnBean(MultipartResolver.class)
    	@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
    	public MultipartResolver multipartResolver(MultipartResolver resolver) {
    		// Detect if the user has created a MultipartResolver but named it incorrectly
    		return resolver;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    第一步首先来看看对于 DefaultDispatcherServletCondition 进行分析

    @Order(Ordered.LOWEST_PRECEDENCE - 10)
    private static class DefaultDispatcherServletCondition extends SpringBootCondition {
    
            //继承父类实现获取匹配结果方法
            /**
            * 两个参数
            * 第一个参数ConditionContext 进行操作的上下文
            * 第二个参数AnnotatedTypeMetadata 注解类型的元数据
            */
            
    	@Override
    	public ConditionOutcome getMatchOutcome(ConditionContext context,
    			AnnotatedTypeMetadata metadata) {
    		//定义默认的DispatcherServlet信息	
    		ConditionMessage.Builder message = ConditionMessage
    				.forCondition("Default DispatcherServlet");
    		//从配合beanFactory中进行遍历		
    		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    		
    		List<String> dispatchServletBeans = Arrays.asList(beanFactory
    			.getBeanNamesForType(DispatcherServlet.class, false, false));
    		//找到dispatcherServlet
    		if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
    			return ConditionOutcome.noMatch(message.found("dispatcher servlet bean")
    				 .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
    		}
    		if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
    			return ConditionOutcome
    				 .noMatch(message.found("non dispatcher servlet bean")
    				     .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
    		}
    		if (dispatchServletBeans.isEmpty()) {
    			return ConditionOutcome
    				 .match(message.didNotFind("dispatcher servlet beans").atAll());
    		}
    		return ConditionOutcome.match(message
    				.found("dispatcher servlet bean", "dispatcher servlet beans")
    				.items(Style.QUOTE, dispatchServletBeans)
    				.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    我们知道在使用@Bean注解的时候如果没有指定name属性默认会以方法名为容器中Bean的ID,在上面这类中对容器中的DispatchServlet进行匹配的时候需要满足下面这个Bean的条件。而这个bean的配置正是可以在webMvcProperteis中进行配置的。

    @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    public DispatcherServlet dispatcherServlet() {
    	DispatcherServlet dispatcherServlet = new DispatcherServlet();
    	dispatcherServlet.setDispatchOptionsRequest(
    			this.webMvcProperties.isDispatchOptionsRequest());
    			dispatcherServlet.setDispatchTraceRequest(
    			this.webMvcProperties.isDispatchTraceRequest());
    			dispatcherServlet.setThrowExceptionIfNoHandlerFound(
    			this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
    	return dispatcherServlet;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在其内部还有另外一个内部类DispatcherServletRegistrationConfiguration

    @Configuration
    @Conditional(DispatcherServletRegistrationCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties(WebMvcProperties.class)
    @Import(DispatcherServletConfiguration.class)
    protected static class DispatcherServletRegistrationConfiguration {
    
    	private final WebMvcProperties webMvcProperties;
    
    	private final MultipartConfigElement multipartConfig;
    
    	public DispatcherServletRegistrationConfiguration(WebMvcProperties webMvcProperties,
    		ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
    		this.webMvcProperties = webMvcProperties;
    		this.multipartConfig = multipartConfigProvider.getIfAvailable();
    	}
    
    	@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
    	@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    	public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
    		DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
    					this.webMvcProperties.getServlet().getPath());
    		registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
    			registration.setLoadOnStartup(this.webMvcProperties.getServlet().getLoadOnStartup());
    		if (this.multipartConfig != null) {
    			registration.setMultipartConfig(this.multipartConfig);
    		}
    		return registration;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    	@Order(Ordered.LOWEST_PRECEDENCE - 10)
    	private static class DefaultDispatcherServletCondition extends SpringBootCondition {
    
    		@Override
    		public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
    			ConditionMessage.Builder message = ConditionMessage.forCondition("Default DispatcherServlet");
    			ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    			List<String> dispatchServletBeans = Arrays
    					.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
    			if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
    				return ConditionOutcome
    						.noMatch(message.found("dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
    			}
    			if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
    				return ConditionOutcome.noMatch(
    						message.found("non dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
    			}
    			if (dispatchServletBeans.isEmpty()) {
    				return ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll());
    			}
    			return ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans")
    					.items(Style.QUOTE, dispatchServletBeans)
    					.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
    		}
    
    	}
    
    	@Order(Ordered.LOWEST_PRECEDENCE - 10)
    	private static class DispatcherServletRegistrationCondition extends SpringBootCondition {
    
    		@Override
    		public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
    			ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    			ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);
    			if (!outcome.isMatch()) {
    				return outcome;
    			}
    			return checkServletRegistration(beanFactory);
    		}
    
    		private ConditionOutcome checkDefaultDispatcherName(ConfigurableListableBeanFactory beanFactory) {
    			List<String> servlets = Arrays
    					.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
    			boolean containsDispatcherBean = beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
    			if (containsDispatcherBean && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
    				return ConditionOutcome.noMatch(
    						startMessage().found("non dispatcher servlet").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
    			}
    			return ConditionOutcome.match();
    		}
    
    		private ConditionOutcome checkServletRegistration(ConfigurableListableBeanFactory beanFactory) {
    			ConditionMessage.Builder message = startMessage();
    			List<String> registrations = Arrays
    					.asList(beanFactory.getBeanNamesForType(ServletRegistrationBean.class, false, false));
    			boolean containsDispatcherRegistrationBean = beanFactory
    					.containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
    			if (registrations.isEmpty()) {
    				if (containsDispatcherRegistrationBean) {
    					return ConditionOutcome.noMatch(message.found("non servlet registration bean")
    							.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
    				}
    				return ConditionOutcome.match(message.didNotFind("servlet registration bean").atAll());
    			}
    			if (registrations.contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) {
    				return ConditionOutcome.noMatch(message.found("servlet registration bean")
    						.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
    			}
    			if (containsDispatcherRegistrationBean) {
    				return ConditionOutcome.noMatch(message.found("non servlet registration bean")
    						.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
    			}
    			return ConditionOutcome.match(message.found("servlet registration beans").items(Style.QUOTE, registrations)
    					.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
    		}
    
    		private ConditionMessage.Builder startMessage() {
    			return ConditionMessage.forCondition("DispatcherServlet Registration");
    		}
    
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    在Spring中往往会通过 @Order 注解来声明优先级,可以看到上面的内容对于两个静态内部类都做了是优先级的配置。在其中还有一个比较重要的注解值得关注 @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) 对于这些注解来说都是为了进一步的优化配置顺序细节处理。

  • 相关阅读:
    八大排序之快速排序
    Wi-Fi7将带来前所未有的快捷、稳定的互联网,更快的传输速度
    Swin Transformer 论文精读,并解析其模型结构
    redisson有几种分布式算法
    Kafka生产者
    【深度学习实验】卷积神经网络(五):深度卷积神经网络经典模型——VGG网络(卷积层、池化层、全连接层)
    gulp入门1-安装
    R语言APRIORI关联规则、K-MEANS均值聚类分析中药专利复方治疗用药规律网络可视化...
    前 Twitter CEO 炮轰 Web 3,马斯克也来“掺和一脚”
    OpenAI Sora视频模型技术原理报告解读
  • 原文地址:https://blog.csdn.net/nihui123/article/details/136225287