第 4 章:从 Spring Framework 到 Spring Boot

06-18 1366阅读

通过前面几个章节的介绍,相信大家已经对 Spring Framework 有了一个基本的认识,相比早期那些没有 Spring Framework 加持的项目而言,它让生产力产生了质的飞跃。但人们的追求是无止境的,这也驱动着技术的发展。开发者认为 Spring 还可以更好,于是 Spring Boot 诞生了。本章我们将一起了解一下 Spring Boot 的基础知识,还有它的两个重要功能——起步依赖与自动配置。

4.1 Spring Boot 基础知识

Spring Framework 提供了 IoC 容器、AOP、MVC 等众多功能,让开发者可以从烦琐的工作中抽离出来,只关注在自己的业务逻辑上。Perl 语言发明人 Larry Wal 说过一句名言:“懒惰,是程序员的第一大美德。” 当我们得到了一样东西,总会想着去追求更好的。而这个更好的东西就是 Spring Boot。有了 Spring Framework,为什么还需要搞出一个 Spring Boot?Spring Boot 又包含哪些东西呢?本节的内容将会回答这些问题。

第 4 章:从 Spring Framework 到 Spring Boot

4.1.1 为什么需要 Spring Boot

随着时间的推移,什么是烦琐的工作,这个定义也在发生变化。原先的参照物是 EJB 1. x 和 EJB 2. x,是徒手开发的 JSP 甚至是 CGI 程序;现在,创建一个基于 Spring 的项目本身变成了一件麻烦事——无论使用 Maven 还是 Gradle,要管理清楚这一堆依赖,避免出现冲突,已经是一场灾难了,我们永远都不知道哪个包里的同名类会带来什么“惊喜”。好不容易搞定了依赖,Spring Framework 的配置又该让人抓狂了,等到 Bean 的自动扫描和自动织入稍稍安抚了一下大家几近奔溃的内心,一大堆与业务逻辑没有太多关系的“模板”配置又“补了一刀”。当这些东西耗费的心智和开发业务逻辑相当,甚至超过业务逻辑时,开发者就该做点什么了。

就在广大开发者们快要接受这个事实,打算认命的时候,Spring 团队推出了一款代码生成器,它就是 Spring Roo 项目,官方介绍它是新一代的 Java 快速应用开发工具,在几分钟内就能构建一个完整的 Java 应用。但现实情况是大家不太买账,Spring Roo 一直都没能成为主流,截至本书写作之时,它的最新版本还是末次修改停留在 2017 年的 2.0.0 版本。虽然 Spring Roo 能帮忙生成各种代码和配置,但它们的数量并未减少。后来在笔者与 Spring 团队的 Josh Long 的一次交流过程中,他一语道破了真相,大意是:“如果一个东西可以生成出来,那为什么还要生成它呢?”

另一方面,Spring Framework 虽然解决了开发和测试的问题,但在整个系统的生命周期中,上线后的运维也占据了很大的比重,怎么样让系统具备更好的可运维性也是个重要的任务。怎么配置、怎么监控、怎么部署,都是要考虑的事情。

出于这些原因,Spring Boot 横空出世了,它解决了上面说到的各种痛点,再一次将生产力提升了一个台阶。正如 Spring Boot 项目首页上写的那样,Spring Boot 可以轻松创建独立的生产级 Spring 应用程序,拿来就能用了。这次,Spring Boot 站到了聚光灯下,成了新的主角。

4.1.2 Spring Boot 的组成部分

Spring Boot 提供了大量的功能,但其本身的核心主要是以下几点:

  • 起步依赖
  • 自动配置
  • Spring Boot Actuator
  • 命令行 CLI

    在实际使用时,最后那项命令行 CLI 用得相对较少,因此本书并不会介绍它。此外,Spring Boot 同时支持 Java 与 Groovy,但在本书中,我们也不会涉及 Groovy 的内容。

    1. 起步依赖

      起步依赖(starter dependency)的目的就是解决 4.1.1 节中提到的依赖管理难题:针对一个功能,需要引入哪些依赖、它们的版本又是什么、互相之间是否存在冲突、它们的间接依赖项之间是否存在冲突……现在我们可以把这些麻烦都交给 Spring Boot 的起步依赖来解决。

      以我们在 1.2 节中创建的 HelloWorld 为例(即代码示例 1-1),我们只需在 Maven 的 POM 文件中引入 org.springframework.boot:spring-boot-starter-web 这个依赖,Spring Boot 就知道我们的项目需要 Web 这个功能,它实际上为我们引入了大量相关的依赖项。通过 mvn dependency:tree 可以查看 Maven 的依赖信息 ,其中就有如下的内容:

      +- org.springframework.boot:spring-boot-starter-web:jar:2.6.3:compile
      |  +- org.springframework.boot:spring-boot-starter:jar:2.6.3:compile
      |  |  +- org.springframework.boot:spring-boot:jar:2.6.3:compile
      |  |  +- org.springframework.boot:spring-boot-autoconfigure:jar:2.6.3:compile
      |  |  +- org.springframework.boot:spring-boot-starter-logging:jar:2.6.3:compile
      |  |  |  +- ch.qos.logback:logback-classic:jar:1.2.10:compile
      |  |  |  |  \- ch.qos.logback:logback-core:jar:1.2.10:compile
      |  |  |  +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.17.1:compile
      |  |  |  |  \- org.apache.logging.log4j:log4j-api:jar:2.17.1:compile
      |  |  |  \- org.slf4j:jul-to-slf4j:jar:1.7.33:compile
      |  |  +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
      |  |  \- org.yaml:snakeyaml:jar:1.29:compile
      |  +- org.springframework.boot:spring-boot-starter-json:jar:2.6.3:compile
      |  |  +- com.fasterxml.jackson.core:jackson-databind:jar:2.13.1:compile
      |  |  |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.13.1:compile
      |  |  |  \- com.fasterxml.jackson.core:jackson-core:jar:2.13.1:compile
      |  |  +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.13.1:compile
      |  |  +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.13.1:compile
      |  |  \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.13.1:compile
      |  +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.6.3:compile
      |  |  +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.56:compile
      |  |  +- org.apache.tomcat.embed:tomcat-embed-el:jar:9.0.56:compile
      |  |  \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.56:compile
      |  +- org.springframework:spring-web:jar:5.3.15:compile
      |  |  \- org.springframework:spring-beans:jar:5.3.15:compile
      |  \- org.springframework:spring-webmvc:jar:5.3.15:compile
      |     +- org.springframework:spring-aop:jar:5.3.15:compile
      |     +- org.springframework:spring-context:jar:5.3.15:compile
      |     \- org.springframework:spring-expression:jar:5.3.15:compile
      

      可以看到,起步依赖是以功能为单位来组织依赖的。要实现某个功能,一共需要哪些依赖,我们自己不清楚,但 Spring Boot 知道。我们会在 4.2 节详细讲解起步依赖。

    2. 自动配置

      在没有使用 Spring Boot 时,对于一个 Web 项目,我们需要配置 DispatcherServlet 来处理请求,需要配置 Jackson JSON 来处理 JSON 的序列化,需要配置 Log4j2 或者 Logback 来打印日志……而在 1.2 节的 HelloWorld 例子中,我们并没有配置这些东西,Spring Boot 自己完成了所有的配置,我们只需要编写 REST 接口的逻辑就好了。

      这就是 Spring Boot 的自动配置,它能根据 CLASSPATH 中存在的类判断出引入了哪些依赖,并为这些依赖提供常规的默认配置,以此来消除模板化的配置。与此同时,Spring Boot 仍然给我们留下了很大的自由度,可以对配置进行各种定制,甚至能够排除自动配置。我们会在 4.3 节详细讲解自动配置。

    3. Spring Boot Actuator

      如果说前两项的目的是简化 Spring 项目的开发,那 Spring Boot Actuator 的目的则是提供一系列在生产环境运行时所需的特性,帮助大家监控并管理应用程序。通过 HTTP 端点或者 JMX,Spring Boot Actuator 可以实现健康检查、度量收集、审计、配置管理等功能。我们会在 5.1 节和 5.2 节详细讲解 Spring Boot Actuator。

    4.1.3 解析 Spring Boot 工程

    一个使用了 Spring Boot 的项目工程,本质上来说和只使用 Spring Framework 的工程是一样的,如果使用 Maven 来管理,那它就是个标准的 Maven 工程,大概的结构就像下面这样。

    |-pom.xml
    |-src
      |-main
        |-java
        |-resources
      |-test
        |-java
        |-resources
    

    具体内容如下:

    • pom.xml 中管理了整个项目的依赖和构建相关的信息;
    • src/main 中是生产的 Java 代码和相关资源文件;
    • src/test 中是测试的 Java 代码和相关资源文件。

      如果通过 Spring Initializr 来生成工程,它还会为我们生成用来启动项目的启动类,比如 HelloWorld 中的 Application 类,以及测试用的 ApplicationTest 类(这是个空的 JUnit 测试类)。其中 Application 类上加了 @SpringBootApplication 注解,表示这是应用的主类,在打包成可执行 Jar 包后,运行 Jar 包时会去调用这个主类的 main() 方法。

      这里需要展开说明一下 POM 文件的内容,分为以下几个部分:

      • 工程自身的 GroupId、ArtifactId 与 Version 等内容定义;
      • 工程继承的 org.springframework.boot:spring-boot-starter-parent 定义;
      • 依赖项定义;
      • 构建相关的配置定义。

        org.springframework.boot:spring-boot-starter-parent 又继承了 org.springframework.boot:spring-boot-dependencies,它通过 定义了大量的依赖项,有了 的加持,在我们自己的工程中,只需要在 中写入依赖项的 和 就好了,无须指定版本,有冲突的依赖项也在 中排除了,无须重复排除。

        在 中的 org.springframework.boot:spring-boot-maven-plugin 在打包时能够生成可执行 Jar 包,它也是在 org.springframework.boot:spring-boot-starter-parent 中定义的。

        在一些特殊情况下,我们的工程无法直接继承 org.springframework.boot:spring-boot-starter-parent,这时就可能失去 Spring Boot 的很多便利之处。为此,我们需要自己在 pom.xml 中做些额外的工作。

        首先,增加 ,导入 org.springframework.boot:spring-boot-dependencies 中的依赖项,这样就能利用其中定义的依赖了:

            
                
                    org.springframework.boot
                    spring-boot-dependencies
                    2.6.3
                    pom
                    import
                
            
        
        

        接着,在 中增加 org.springframework.boot:spring-boot-maven-plugin,这样打包时就能用上 Spring Boot 的插件,打出可执行的 Jar 包:

            
                
                    org.springframework.boot
                    spring-boot-maven-plugin
                    2.6.3
                    
                        
                            
                                repackage
                            
                        
                    
                
            
        
        

        通过上述修改,我们就能在不继承 org.springframework.boot:spring-boot-starter-parent 的情况下继续让 Spring Boot 替我们管理依赖并构建可执行 Jar 包了。

        4.2 起步依赖

        在自己管理依赖时,我们要为工程引入 Web 相关的支持,需要配置一堆依赖,但我们常常会搞不清楚哪些是必需的,哪些是多余的,最后只能不管三七二十一从某个现在能跑的工程里胡乱复制一通。

        有了 Spring Boot,情况就不一样了。Spring Boot 按照功能划分了很多起步依赖,大家只需要知道自己要什么功能,比如要实现 Web 功能、需要 JPA 支持等,具体引入什么依赖、分别是什么版本,都可以交给起步依赖来管理。

        此外,管理依赖时不仅要避免出现 GroupId 和 ArtifactId 相同但 Version 不同的依赖,还要注意同一个依赖项因为版本升级替换了 GroupId 或 ArtifactId 的情况。对于前者 Maven 会仅保留一个依赖,但它未必是你想要的那个,而对于后者则更糟糕,Maven 会认为这是两个不同的依赖,它们都会被保留下来。但用了 Spring Boot 的起步依赖之后,此类问题就能得到缓解,同一版本的 Spring Boot 中的各个起步依赖所引入的依赖不会产生冲突,因为官方对这些依赖进行了严格的测试。

        所以说起步依赖是帮助大家摆脱依赖管理困局的一大利器,这节就让我们来了解一下 Spring Boot 都提供了哪些起步依赖,它背后的实现原理又是什么样的。

        4.2.1 Spring Boot 内置的起步依赖

        Spring Boot 官方的起步依赖都遵循一样的命名规范,即都以 spring-boot-starter- 开头,其他第三方的起步依赖都应该 避免使用这个前缀,以免引起混淆。

        Spring Boot 内置了超过 50 个不同的起步依赖,表 4-1 罗列了其中 10 个常用的起步依赖。

        表 4-1 一些常用的 Spring Boot 起步依赖

        名称描述
        spring-boot-starterSpring Boot 的核心功能,比如自动配置、配置加载等
        spring-boot-starter-actuatorSpring Boot 提供的各种生产级特性
        spring-boot-starter-aopSpring AOP 相关支持
        spring-boot-starter-data-jpaSpring Data JPA 相关支持,默认使用 Hibernate 作为 JPA 实现
        spring-boot-starter-data-redisSpring Data Redis 相关支持,默认使用 Lettuce 作为 Redis 客户端
        spring-boot-starter-jdbcSpring 的 JDBC 支持
        spring-boot-starter-logging日志依赖,默认使用 Logback
        spring-boot-starter-securitySpring Security 相关支持
        spring-boot-starter-test在 Spring 项目中进行测试所需的相关库
        spring-boot-starter-web构建 Web 项目所需的各种依赖,默认使用 Tomcat 作为内嵌容器

        后续大家还会接触到很多起步依赖,比如 Spring Cloud 的各种组件,也有第三方的,比如 MyBatis 的 mybatis-spring-boot-starter 和 Druid 的 druid-spring-boot-starter。

        在引入了起步依赖后,如果我们希望修改某些依赖的版本,如何操作呢?可以在 Maven 的 中指定对应依赖的版本。通常这种情况是想要升级某些依赖、修复安全漏洞或使用新功能,但 Spring Boot 的依赖并未升级。例如,想要指定 Jackson 的版本来升级 Jackson Databind,就可以像下面这样:

            2.11.0
        
        

        具体的属性可以在 org.springframework.boot:spring-boot-dependencies 的 pom.xml 中寻找。当然,也可以再彻底一些,在项目 POM 文件的 中直接引入自己所需要的依赖,同时,在引入的起步依赖中排除刚才你所加的依赖。

        Spring Boot 本身也提供了一些可以互相替换的起步依赖,例如,Log4j2 可以代替 Logback,Jetty 和 Netty 可以代替 Tomcat,如代码示例 4-1 所示。

        代码示例 4-1 用 Log4j2 代替 Logback

            
                org.springframework.boot
                spring-boot-starter-web
                
                    
                        org.springframework.boot
                        spring-boot-starter-logging
                    
                
            
            
                org.springframework.boot
                spring-boot-starter-log4j2
            
        
        

        4.2.2 起步依赖的实现原理

        如果熟悉 Maven,那么相信大家已经猜到了,起步依赖背后使用的其实就是 Maven 的传递依赖机制 。看似只添加了一个依赖,但实际上通过传递依赖,我们已经引入了一堆的依赖。

        我们可以在 Maven 的 中统一定义依赖的信息,比如版本、排除的传递依赖项等,随后在 中添加这个依赖时就不用再重复配置这些信息了。起步依赖与其中定义的依赖项都是通过这种方式定义的,所以使用了起步依赖后就不用再考虑版本和应该排除哪些东西了。

        以 2.3.0.RELEASE 版本的 org.springframework.boot:spring-boot-starter-web 为例,它的 POM 文件分为如下三部分:

        • 起步依赖本身的描述信息
        • 导入依赖管理项
        • 具体依赖项

          在 中用 import 的方式导入 org.springframework.boot:spring-boot-dependencies 里配置的依赖信息,具体如下所示:

              
                  
                      org.springframework.boot
                      spring-boot-dependencies
                      2.3.0.RELEASE
                      pom
                      import
                  
              
          
          

          随后,在 中配置具体要引入的依赖,而这些依赖所间接依赖的内容也会被传递进来,具体如下所示:

              
                  org.springframework.boot
                  spring-boot-starter
                  2.3.0.RELEASE
                  compile
              
              
                  org.springframework.boot
                  spring-boot-starter-json
                  2.3.0.RELEASE
                  compile
              
              
                  org.springframework.boot
                  spring-boot-starter-tomcat
                  2.3.0.RELEASE
                  compile
              
              
                  org.springframework
                  spring-web
                  compile
              
              
                  org.springframework
                  spring-webmvc
                  compile
              
          
          

          而到了后面的版本,Spring Boot Starter 的内容变得更直接了,以 2.6.3 版本的 org.springframework.boot:spring-boot-starter-web 为例,其中去掉了 的部分,所有依赖的版本信息直接硬编码写死在了 里。这两种方式对使用 Spring Boot 的开发者而言,在使用体验上并没有什么差异,所以我们不用在意这些细节。

          4.3 自动配置

          Spring Boot 可以根据 CLASSPATH、配置项等条件自动进行常规配置,省去了我们自己动手把一模一样的配置复制来复制去的麻烦。既然框架能猜到你想这么配,那它自己就能帮你搞定,如果它的配置不是我们想要的,再做些手动配置就好了。

          我们已经在代码示例 1-1 中看到过 @SpringBootApplication 注解了,查看这个注解,可以发现它上面添加了 @EnableAutoConfiguration,它可以开启自动配置功能。这两个注解上都有 exclude 属性,我们可以在其中排除一些不想启用的自动配置类。如果不想启用自动配置功能,也可以在配置文件中配置 spring.boot.enableautoconfiguration=false,关闭该功能。

          4.3.1 自动配置的实现原理

          自动配置类其实就是添加了 @Configuration 的普通 Java 配置类,它利用 Spring Framework 4.0 加入的条件注解 @Conditional 来实现“根据特定条件启用相关配置类”,注解中传入的 Condition 类就是不同条件的判断逻辑。Spring Boot 内置了很多条件注解,表 4-2 中列举了 org.springframework.boot.autoconfigure.condition 包中的条件注解。

          表 4-2 Spring Boot 内置的条件注解

          条件注解生效条件
          @ConditionalOnBean存在特定名称、特定类型、特定泛型参数或带有特定注解的 Bean
          @ConditionalOnMissingBean与前者相反,不存在特定 Bean
          @ConditionalOnClass存在特定的类
          @ConditionalOnMissingClass与前者相反,不存在特定类
          @ConditionalOnCloudPlatform运行在特定的云平台上,截至 2.6.3 版本,代表云平台的枚举类支持无云平台、CloudFoundry、Heroku、SAP、Kubernetes 和 Azure,可以通过 spring.main.cloud-platform 配置强制使用的云平台
          @ConditionalOnExpression指定的 SpEL 表达式为真
          @ConditionalOnJava运行在满足条件的 Java 上,可以比指定版本新,也可以比指定版本旧
          @ConditionalOnJndi指定的 JNDI 位置必须存在一个,如没有指定,则需要存在 InitialContext
          @ConditionalOnProperty属性值满足特定条件,比如给定的属性值都不能为 false
          @ConditionalOnResource存在特定资源
          @ConditionalOnSingleCandidate当前上下文中,特定类型的 Bean 有且仅有一个
          @ConditionalOnWarDeployment应用程序是通过传统的 War 方式部署的,而非内嵌容器
          @ConditionalOnWebApplication应用程序是一个 Web 应用程序
          @ConditionalOnNotWebApplication与前者相反,应用程序不是一个 Web 应用程序

          以 @ConditionalOnClass 注解为例,它的定义如下所示, @Target 指明该注解可用于类型和方法定义, @Rentention 指明注解的信息在运行时也能获取到,而其中最关键的就是 OnClassCondition 条件类,里面是具体的条件计算逻辑:

           @Target({ ElementType.TYPE, ElementType.METHOD })
           @Retention(RetentionPolicy.RUNTIME)
           @Documented
           @Conditional(OnClassCondition.class)
           public @interface ConditionalOnClass {
               Class[] value() default {};
               String[] name() default {};
           }
          

          了解了条件注解后,再来看看它们是如何与配置类结合使用的。以后续章节中会用到的 JdbcTemplateAutoConfiguration 为例,它的完整类定义代码如下所示:

            @Configuration(proxyBeanMethods = false)
            @ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
            @ConditionalOnSingleCandidate(DataSource.class)
            @AutoConfigureAfter(DataSourceAutoConfiguration.class)
            @EnableConfigurationProperties(JdbcProperties.class)
            @Import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class })
            public class JdbcTemplateAutoConfiguration {}
          

          可以看到这个配置类的生效条件是存在 DataSource 和 JdbcTemplate 类,且在上下文中只能有一个 DataSource。此外,这个自动配置需要在 DataSourceAutoConfiguration 之后再配置(可以用 @AutoConfigureBefore、 @AutoConfigureAfter 和 @AutoConfigureOrder 来控制自动配置的顺序)。这个配置类还会同时导入 JdbcTemplateConfiguration 和 NamedParameterJdbcTemplateConfiguration 里的配置。

          茶歇时间:通过 ImportSelector 选择性导入配置

          普通的配置类需要被扫描到才能生效,可是自动配置类并不在我们项目的扫描路径中,它们又是怎么被加载上来的呢?

          秘密在于 @EnableAutoConfiguration 上的 @Import(AutoConfigurationImportSelector.class),其中的 AutoConfigurationImportSelector 类是 ImportSelector 的实现,这个接口的作用就是根据特定条件决定可以导入哪些配置类,接口中的 selectImports() 方法返回的就是可以导入的配置类名。

          AutoConfigurationImportSelector 通过 SpringFactoriesLoader 来加载 /META-INF/spring.factories 里配置的自动配置类列表,所用的键是 org.springframework.boot.autoconfigure.EnableAutoConfiguration,值是以逗号分隔的自动配置类全限定类名(包含了完整包名与类名)清单。

          所以,只要在我们的类上增加 @SpringBootApplication 或者 @EnableAutoConfiguration 后,Spring Boot 就会自动替我们加载所有的自动配置类。

          自动配置固然帮我们做了很多事,降低了配置的复杂度,但总有些情况我们会想要强制禁用某些自动配置,这时就需要做以下处理:

          • 在配置文件中使用 spring.autoconfigure.exclude 配置项,它的值是要排除的自动配置类的全限定类名;
          • 在 @SpringBootApplication 注解中添加 exclude 配置,它的值是要排除的自动配置类。

            4.3.2 配置项加载机制详解

            如果自动配置的东西不满足我们的需要,我们可以自己动手进行配置,但在动手之前,可以先了解下 Spring Boot 的自动配置是否有给我们留下什么“开关参数”,用来定制配置内容。以 AOP 的配置为例,它就可以通过 spring.aop.proxy-target-class 属性来做微调:

                @Configuration(proxyBeanMethods = false)
                @EnableAspectJAutoProxy(proxyTargetClass = false)
                @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
                                       matchIfMissing = false)
                static class JdkDynamicAutoProxyConfiguration {}
                @Configuration(proxyBeanMethods = false)
                @EnableAspectJAutoProxy(proxyTargetClass = true)
                @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
                                       matchIfMissing = true)
                static class CglibAutoProxyConfiguration {}
            

            在 2.3.1 节中,我们了解过了 Spring Framework 的 PropertySource 抽象机制,Spring Boot 将它又向前推进了一大步。

            1. Spring Boot 的属性加载优先级

              Spring Boot 有 18 种方式来加载属性,且存在覆盖关系,本节根据优先级列出其中的一部分:

              (1) 测试类上的 @TestPropertySource 注解;

              (2) 测试类上的 @SpringBootTest 注解中的 properties 属性,还有些其他 @...Test 注解也有该属性;

              (3) 命令行参数(在 5.3 节中会讨论如何获取命令行参数);

              (4) java:comp/env 中的 JNDI 属性;

              (5) System.getProperties() 中获取到的系统属性;

              (6) 操作系统环境变量;

              (7) RandomValuePropertySource 提供的 random.* 属性(比如 $、 $、 $ 和 $);

              (8) 应用配置文件(有好几个地方可以配置,下面会详细说明);

              (9) 配置类上的 @PropertySource 注解。

              如果存在同名的属性,越往前的位置优先级越高,例如 my.prop 出现在命令行上,又出现在配置文件里,那最终会使用命令行里的值。

            2. Spring Boot 的配置文件

              Spring Boot 还为我们提供了一套配置文件,默认以 application 作为主文件名,支持 Properties 格式(文件以 .properties 结尾)和 YAML 格式(文件以 .yml 结尾)。Spring Boot 会按如下优先级加载属性(以 .properties 文件为例, .yml 文件的顺序是一样的):

              (1) 打包后的 Jar 包以外的 application-.properties;

              (2) 打包后的 Jar 包以外的 application.properties;

              (3) Jar 包内部的 application-.properties;

              (4) Jar 包内部的 application.properties。

              可以看到 Jar 包外部的文件比内部的优先级高,特定 Profile 的文件比公共的文件优先级高。

              在 Spring Boot 2.4.0 之前,上述第 2 和第 3 个文件的优先级顺序是反的,所有 application-.properties 文件的顺序都要高于 application.properties,无论是否在 Jar 包外。从 2.4.0 开始,调整为 Jar 包外部的文件优先级更高。可以设置 spring.config.use-legacy-processing=true 来开启兼容逻辑,Spring Boot 3.0 里会移除这个开关。

              Spring Boot 会在如下几处位置寻找 application.properties 文件,并将其中的内容添加到 Spring 的 Environment 中:

              • 当前目录的 /config 子目录;
              • 当前目录;
              • CLASSPATH 中的 /config 目录;
              • CLASSPATH 根目录。

            如果我们不想用 application 来做主文件名,可以通过 spring.config.name 来改变默认值。通过下面的方式可以将 application.properties 改为 spring.properties:

            ▸ java -jar foo.jar --spring.config.name=spring
            

            还可以通过 spring.config.location 来修改查找配置文件的路径,默认是下面这样的,用逗号分隔,越靠后的优先级越高:

            classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/
            

            如果同时存在 .propertries 文件和 .yml 文件,那么后者中配置的属性优先级更高。

            1. 类型安全的配置属性

              通常,我们会在类中用 @Value("${}") 注解来访问属性,或者在 XML 文件中使用 ${} 占位符。在配置中,可能会有大量的属性需要一一对应到类的成员变量上,Spring Boot 提供了一种结构化且类型安全的方式来处理配置属性(configuration properties)——使用 @ConfigurationProperties 注解。

              下面的代码是 DataSourceProperties 类的一部分,这是一个典型的配置属性类(当然,它也是一个 POJO),Spring Boot 会把环境中以 spring.datasource 打头的属性都绑定到类的成员变量上,并且完成对应的类型转换。例如, spring.datasource.url 就会绑定到 url 上。

                  @ConfigurationProperties(prefix = "spring.datasource")
                  public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
                      private ClassLoader classLoader;
                      private String name;
                      private boolean generateUniqueName = true;
                      private Class

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]