《精通Spring MVC 4》——1.6 幕后的Spring Boot

简介: 一般会通过@Order注解来声明优先等级,可以看到DispatcherServletAutoConfiguration需要优先进行配置; 其中也可以包含一些提示信息,如@AutoConfigureAfter或@AutoConfigureBefore,从而进一步细化配置处理的顺序; 它还支持在特定的条件下启用某项功能。

本节书摘来自异步社区《精通Spring MVC 4》一书中的第1章,第1.6节,作者:【美】Geoffroy Warin著,更多章节内容可以访问云栖社区“异步社区”公众号查看

1.6 幕后的Spring Boot

如果你之前搭建过Spring MVC应用,那么可能已经习惯于编写相关的XML文件或Java注解配置类。

一般来讲,初始的步骤如下所示:

1.初始化Spring MVC的DispatcherServlet;

2.搭建转码过滤器,保证客户端请求进行正确地转码;

3.搭建视图解析器(view resolver),告诉Spring去哪里查找视图,以及它们是使用哪种方言编写的(JSP、Thymeleaf模板等);

4.配置静态资源的位置(CSS、JS);

5.配置所支持的地域以及资源bundle;

6.配置multipart解析器,保证文件上传能够正常工作;

7.将Tomcat或Jetty包含进来,从而能够在Web服务器上运行我们的应用;

8.建立错误页面(如404)。

不过,Spring Boot为我们处理了所有的事情。因为这些配置一般是与应用相关的,所以你可以无限制地将它们进行组合。

在一定程度上来讲,Spring Boot是带有一定倾向性的Spring项目配置器。它基于约定,并且默认会在你的项目中使用这些约定。

1.6.1 分发器和multipart配置
接下来,让我们看一下在幕后到底发生了什么。

我们使用默认生成的Spring Boot配置文件,并将其设置为debug模式。在src/main/resources/ application.properties中添加下面这一行:

debug=true
现在,如果重新启动应用的话,就能看到Spring Boot的自动配置报告。它分为两部分:一部分是匹配上的(positive matches),列出了应用中,所有的自动配置,另一部分是没有匹配上的(negative matches),这部分是应用在启动的时候,需求没有满足的Spring Boot自动配置:

=========================
AUTO-CONFIGURATION REPORT
=========================



Positive matches:
-----------------


  DispatcherServletAutoConfiguration
      - @ConditionalOnClass classes found: org.Springframework.web.
servlet.DispatcherServlet (OnClassCondition)
      - found web application StandardServletEnvironment
(OnWebApplicationCondition)


  EmbeddedServletContainerAutoConfiguration
      - found web application StandardServletEnvironment
(OnWebApplicationCondition)


  ErrorMvcAutoConfiguration
      - @ConditionalOnClass classes found: javax.servlet.Servlet,org.
springframework.web.servlet.DispatcherServlet (OnClassCondition)
      - found web application StandardServletEnvironment
(OnWebApplicationCondition) 


  HttpEncodingAutoConfiguration
      - @ConditionalOnClass classes found: org.springframework.web.
filter.CharacterEncodingFilter (OnClassCondition)
      - matched (OnPropertyCondition)



<Input trimmed>

仔细看一下DispatcherServletAutoConfiguration:

/**
* {@link EnableAutoConfiguration Auto-configuration} for the Spring
* {@link DispatcherServlet}. Should work for a standalone application
where an embedded
* servlet container is already present and also for a deployable
application using
* {@link SpringBootServletInitializer}.
*
* @author Phillip Webb
* @author Dave Syer
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

    /*
    * The bean name for a DispatcherServlet that will be mapped to the
root URL "/"
    */
    public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME =
"dispatcherServlet";

    /*
    * The bean name for a ServletRegistrationBean for the
DispatcherServlet "/"
    */
    public static final String DEFAULT_DISPATCHER_SERVLET_
REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
    @Configuration
    @Conditional(DefaultDispatcherServletCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    protected static class DispatcherServletConfiguration {

        @Autowired
        private ServerProperties server;

        @Autowired(required = false)
        private MultipartConfigElement multipartConfig;

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_
NAME)
        public ServletRegistrationBean dispatcherServletRegistration()
{
            ServletRegistrationBean registration = new
ServletRegistrationBean(
                    dispatcherServlet(), this.server.
getServletMapping());
            registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_
NAME);
            if (this.multipartConfig != null) {
                registration.setMultipartConfig(this.multipartConfig);
            }
            return registration;
        }

        @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;
        }
    }

    @Order(Ordered.LOWEST_PRECEDENCE - 10)
    private static class DefaultDispatcherServletCondition extends
SpringBootCondition {

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext
context,
                AnnotatedTypeMetadata metadata) {
            ConfigurableListableBeanFactory beanFactory = context.
getBeanFactory();
            ConditionOutcome outcome = checkServlets(beanFactory);
            if (!outcome.isMatch()) {
                return outcome;
            }
            return checkServletRegistrations(beanFactory);
        }

    }
}

这是一个典型的Spring Boot配置类。

与其他的Spring配置类相同,它使用了@Configuration注解;
一般会通过@Order注解来声明优先等级,可以看到DispatcherServletAutoConfiguration需要优先进行配置;
其中也可以包含一些提示信息,如@AutoConfigureAfter或@AutoConfigureBefore,从而进一步细化配置处理的顺序;
它还支持在特定的条件下启用某项功能。通过使用@ConditionalOnClass (DispatcherServlet.class)这个特殊的配置,能够确保我们的类路径下包含DispatcherServlet,这能够很好地表明Spring MVC位于类路径中,用户当前希望将其启动起来。
这个文件中还包含了Spring MVC分发器Servlet和multipart解析器的典型配置。整个Spring MVC配置被拆分到了多个文件之中。

另外,值得一提的是,这些bean会遵循特定的规则,以此来检查是否处于激活状态。在@Conditional(DefaultDispatcherServletCondition.class)条件满足的情况下,ServletRegistrationBean函数才会启用,这有些复杂,但是能够检查在你的配置中,是否已经注册了分发器Servlet。

只有在满足@ConditionalOnMissingBean(name=DispatcherServlet.MULTIPART_RESOLVER_ BEAN_NAME)条件的情况下,MultipartResolver函数才会处于激活状态,例如,当我们自己还没有注册的时候。

这意味着Spring Boot仅仅是基于常见的使用场景,帮助我们对应用进行配置。不过,可以在任意的地方覆盖这些默认值,并声明自己的配置。

因此,通过查看DispatcherServletAutoConfiguration,就了解了为什么我们已经拥有了分发器Servlet和multipart解析器。

1.6.2 视图解析器、静态资源以及区域配置
另外一个密切相关的配置是WebMvcAutoConfiguration,它声明了视图解析器、地域解析器(localeresolver)以及静态资源的位置。视图解析器如下所示:

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

  @Value("${spring.view.prefix:}")
  private String prefix = "";

  @Value("${spring.view.suffix:}")
  private String suffix = "";

  @Bean
  @ConditionalOnMissingBean(InternalResourceViewResolver.class)
  public InternalResourceViewResolver defaultViewResolver() {
    InternalResourceViewResolver resolver = new
InternalResourceViewResolver();
    resolver.setPrefix(this.prefix);
    resolver.setSuffix(this.suffix);
    return resolver;
  }
}

视图解析器的配置并没有什么特殊之处,这里真正有意思的是使用了配置属性,从而允许用户对其进行自定义。

它的意思就是说“将会在用户的application.properties文件中查找两个变量,这两个变量的名字是spring.view.prefix和spring.view.suffix”。在配置中只需两行代码就能将视图解析器搭建起来了,这是非常便利的。

为了下一章内容的讲解,你需要牢记这一点,不过,我们现在会继续浏览Spring Boot的代码。

关于静态资源,配置中包含了如下的内容:

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
    "classpath:/META-INF/resources/", "classpath:/resources/",
    "classpath:/static/", "classpath:/public/" };

private static final String[] RESOURCE_LOCATIONS;
static {
  RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length
      + SERVLET_RESOURCE_LOCATIONS.length];
  System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS,
0,
      SERVLET_RESOURCE_LOCATIONS.length);
  System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_
LOCATIONS,
      SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.
length);
}

@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/**")) {
    registry.addResourceHandler("/webjars/**")
        .addResourceLocations("classpath:/META-INF/resources/
webjars/")
        .setCachePeriod(cachePeriod);
}
if (!registry.hasMappingForPattern("/**")) {
  registry.addResourceHandler("/**")
        .addResourceLocations(RESOURCE_LOCATIONS)
        .setCachePeriod(cachePeriod);
  }
}

资源位置的声明有点复杂,但是通过它,我们可以了解到以下两点:

对带有“webjar”前缀的资源访问将会在类路径中解析。这样的话,我们就能使用Mavan中央仓库中预先打包好的JavaScript依赖;
我们的静态资源需要放在类路径中,并且要位于以下4个目录中的任意一个之中,“/META-INF/resources/”“/resources/”“/static/”或“/public/”。
screenshot

WebJars是JAR包格式的客户端JavaScript库,可以通过Maven中央仓库来获取。它们包含了Maven项目文件,这个文件允许定义传递性依赖,能够用于所有基于JVM的应用之中。WebJars是JavaScript包管理器的替代方案,如bower或npm。对于只需要较少JavaScript库的应用来说,这种方案是很棒的。你可以在www.webjars.org站点上看到所有可用的WebJars列表。
在这个文件中,还专门有一部分用来声明地域管理:

@Bean
@ConditionalOnMissingBean(LocaleResolver.class)
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
  return new FixedLocaleResolver(
      StringUtils.parseLocaleString(this.mvcProperties.getLocale()));
}

默认的地域解析器只会处理一个地域,并且允许我们通过spring.mvc.locale配置属性来进行定义。

相关文章
|
26天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
40 0
|
1月前
|
缓存 前端开发 Java
Spring MVC 面试题及答案整理,最新面试题
Spring MVC 面试题及答案整理,最新面试题
88 0
|
1月前
|
SQL JavaScript Java
springboot+springm vc+mybatis实现增删改查案例!
springboot+springm vc+mybatis实现增删改查案例!
25 0
|
1月前
|
SQL Java 数据库连接
挺详细的spring+springmvc+mybatis配置整合|含源代码
挺详细的spring+springmvc+mybatis配置整合|含源代码
40 1
|
14天前
|
前端开发 Java 应用服务中间件
Springboot对MVC、tomcat扩展配置
Springboot对MVC、tomcat扩展配置
|
15天前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
|
5天前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
13 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
|
7天前
|
XML Java C++
【Spring系列】Sping VS Sping Boot区别与联系
【4月更文挑战第2天】Spring系列第一课:Spring Boot 能力介绍及简单实践
29 0
【Spring系列】Sping VS Sping Boot区别与联系
|
11天前
|
数据采集 前端开发 Java
数据塑造:Spring MVC中@ModelAttribute的高级数据预处理技巧
数据塑造:Spring MVC中@ModelAttribute的高级数据预处理技巧
23 3
|
11天前
|
存储 前端开发 Java
会话锦囊:揭示Spring MVC如何巧妙使用@SessionAttributes
会话锦囊:揭示Spring MVC如何巧妙使用@SessionAttributes
13 1