将 Spring Cloud 应用从 Spring Boot 1.2 迁移到 1.3

工程 | Dave Syer | 2015年11月25日 | ...

Spring Boot 1.3 中有一些有趣的新特性,这些特性现已在 Brixton 发布分支的 Spring Cloud 中可用。Spring Cloud 的 Angel 发布分支与 Spring Boot 1.3 部分不兼容,因此在升级时有一些重要事项需要注意。本文旨在帮助您了解这些变化并更新现有应用以使用新特性。对于将 Spring 项目的新版本引入现有代码库的一般情况,本文也应有所帮助。

提示:您可以使用 mvn dependency:treegradle dependencies 命令列出项目中的依赖项并检查版本。

依赖管理

如果您使用的是旧版本的 Spring Boot,您的 Maven POM 中可能包含类似以下内容:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.2.7.RELEASE</version>
  <relativePath /> <!-- lookup parent from repository -->
</parent>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.2.7.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

或者,如果您使用 Gradle,则类似以下内容:

buildscript {
	ext {
		springBootVersion = '1.2.7.RELEASE'
	}
	dependencies {
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
	}
}

要升级到 Spring Boot 1.3.0,您只需将上面的 '1.2.7' 更改为 '1.3.0'。目前看来很简单。

提示:要查看包含最新版 Spring Boot 的“典型”Maven POM,您可以使用 curl start.spring.io/pom.xml 命令。要添加 Spring Cloud,您可以附加 -d style=cloud-config-client。可以通过添加 -d bootVersion=1.3.1.BUILD-SNAPSHOT (例如) 来更改 Spring Boot 版本。对于 Gradle,可以使用 build.gradle 代替 pom.xml 来执行同样的操作。

将 Spring Cloud 与 Spring Boot 结合使用

由于 Spring Cloud 构建在 Spring Boot 之上,因此找到能够协同工作的组合可能令人困惑且困难。接下来我们将描述几种升级场景,并展示您在依赖管理方面可以实现的目标。

大型升级

通常,最大的变化在于升级(Spring Boot 1.2 到 1.3,或 Spring Cloud Angel 到 Brixton)。如果您从 Spring Initializr 下载的项目,它将使用 Spring Boot 父 POM

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.2.7.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>

并在 <dependencyManagement> 部分中使用 Spring Cloud BOM

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-parent</artifactId>
      <version>Angel.SR4</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

在 Gradle 中,您将看到类似这样的内容:

buildscript {
    ext {
        springBootVersion = '1.2.7.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 
    }
}
dependencyManagement {
  imports { 
    mavenBom "org.springframework.cloud:spring-cloud-starter-parent:Angel.SR4" 
  }
}

简单地更新 Spring Boot 版本在这两种情况下都不会奏效,因为 Spring Cloud Angel BOM 包含旧版本的 Spring Boot 和 Spring(以及其他一些东西)。因此,我们确实需要同时升级 Spring Boot 和 Spring Cloud。例如在 Maven 中:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.3.0.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-parent</artifactId>
      <version>Brixton.M3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

在 Gradle 中:

buildscript {
    ext {
        springBootVersion = '1.3.0.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 
    }
}
dependencyManagement {
  imports { 
    mavenBom "org.springframework.cloud:spring-cloud-starter-parent:Brixton.M3" 
  }
}

注意:Brixton.M2 及所有早期版本的 Spring Cloud 兼容 Spring Boot 1.3.0.RELEASE。您至少需要 Brixton.M3。

升级 Spring Boot 到 1.3.0 以上版本

假设您想使用 Spring Boot 的 snapshot 版本,或在 1.3.1 发布时升级到该版本,但 Spring Cloud 没有明确依赖于您想要的 Spring Boot 版本的版本。

在 Maven 中,请记住,如果您使用现成的父 POM 之一,它们包含 <dependencyManagement> 并且会优先。考虑到这一点,如果您使用这些父 POM,请务必使用依赖集与您所需版本最接近的父 POM(在此场景中是 Boot 的父 POM)。

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.3.1.BUILD-SNAPSHOT</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-parent</artifactId>
      <version>Brixton.M3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

原则上,Gradle 更简单,因为它没有“父级”的概念。实践中,除非您手动应用依赖管理插件,否则 Spring Boot 插件无法与依赖管理使用不同版本。因此,您需要在 build.gradle 中做一些调整

buildscript {
    ext {
        springBootVersion = '1.3.0.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "io.spring.gradle:dependency-management-plugin:0.5.3.RELEASE"
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 
    }
}

apply plugin: "io.spring.dependency-management"
...

dependencyManagement {
  imports { 
    mavenBom "org.springframework.cloud:spring-cloud-starter-parent:Brixton.M3" 
    mavenBom "org.springframework.bootspring-boot-starter-parent:1.3.1.BUILD-SNAPSHOT" 
  }
}

apply plugin: 'spring-boot' 

规则是您必须 a) 手动导入依赖管理插件,并且在 Spring Boot 插件之前导入,b) 在应用 Spring Boot 插件之前声明 dependencyManagement。完成这些步骤后,您可以在 dependencyManagement 声明中列出依赖项,并且最后一个声明的会生效(与 Maven 相反)。

注意:这种对声明顺序的敏感性是当前版本工具的“特性”。未来版本中可能会有所不同。有关更多详细信息,请参阅 Gradle 工具中的此问题

将 Maven 与自定义父级一起使用

如果您不使用现成的父 POM,则可以自由使用一个包含 <dependencyManagement> 的父 POM,这使得控制更容易。在这种情况下,您需要将 Spring Boot 和 Spring Cloud 都放在 <dependencyManagement> 中,并且顺序很重要:Maven 中第一个声明的生效(Gradle 中是最后一个)。例如,要在 Maven 中使用 Spring Boot 1.3.1.BUILD-SNAPSHOT 和 Spring Cloud Brixton.M3:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.3.1.BUILD-SNAPSHOT</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-parent</artifactId>
      <version>Brixton.M3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

BOM 的顺序

请注意,在 Maven 和 Gradle 中,BOM 的顺序都很重要:如果在顶层(显式声明的依赖项)存在冲突,Maven 中通常是第一个声明的生效(Gradle 中是最后一个)。与 Maven 的一个巨大区别是父级是特殊的:如果它包含 <dependencyManagement>,它总是生效。

要了解特定依赖项版本是否会按您需要的方式解析是很复杂的。这取决于 BOM 的顺序,以及您的依赖项在传递依赖树中的深度。例如,Spring Boot BOM 为 spring-core 声明了显式的(一级)依赖管理,但没有针对任何其他 Spring Framework jar 包(这些是通过引用 Spring Framework BOM 引入的)。规则是第一次声明的生效,但整个树(包括所有 BOM)都会被包含,从顶部逐层搜索。

注意:如果没有 Spring Boot(或 Spring 依赖管理)插件,Gradle 没有这个“最后一个生效”的规则。要使用“原生”Gradle 构建实现同样的效果,通常需要仔细且繁琐地手动修复传递依赖项版本。

进一步操纵依赖项版本

如果您想将某个依赖项的版本提升到 Spring Boot 和 Spring Cloud BOM 中指定的版本之上,事情可能会变得复杂。概括来说,有两种选择:属性和附加 BOM。第一种(属性)适用于现成的父 POM,而另一种不适用。第二种(更多 BOM)仅在您感兴趣的依赖项存在可用 BOM 的情况下才有效,并且仅在传递依赖项与您的需求不冲突的情况下才有效。例如,所有 Spring Cloud 项目都有自己的 BOM,Spring Framework 也是如此,这是一个好的开始。

属性

Spring Boot 父 POM(以及您使用的 Spring Cloud 父 POM,因为它继承自 Spring Boot 父 POM)将其所有依赖项版本提取到 <properties/> 中。因此,您通常只需更改属性值即可。Maven 中的示例:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.3.0.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
  <spring.version>4.2.4.BUILD-SNAPSHOT</spring.version>
</properties>

Gradle 中对应的功能是 ext 属性,例如:

ext['spring.version'] = '4.2.4.BUILD-SNAPSHOT'

附加 BOM

Spring Framework 有自己的 BOM,因此我们可以使用它来管理 Spring 版本。在 Maven 中使用自定义父级(不包含 <dependencyManagement>):

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-framework-bom</artifactId>
      <version>4.2.4.BUILD-SNAPSHOT</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

注意:这个示例实际上适用于 Spring Boot 父 POM(除非它碰巧具有相同的 <spring.version/>),因为 Spring Framework 版本已经被父级固定了。要在使用 Spring Boot 父级的同时使用 Spring Framework snapshot 版本,最好采用属性方法(如上所述)。

在 Gradle 中更简单(因为没有父级来设置冲突的版本):

dependencyManagement {
	imports { 
      mavenBom "org.springframework:spring-framework-bom:4.2.4.BUILD-SNAPSHOT" 
      mavenBom "org.springframework.boot:spring-boot-starter-parent:1.3.0.RELEASE" 
	}
}

结论

依赖管理很困难,但希望我们通过概述升级 Spring Boot 和 Spring Cloud 时的一些常见场景来减轻难度。根据您选择 Maven 还是 Gradle,行为会略有不同,但至少如果您选择 Gradle 并使用 Spring Boot 插件,差异会最小化。归根结底,Spring 项目有不同的发布计划,因此总可能存在冲突,但它们通常会朝着收敛的方向发展,所以如果您等待足够长的时间,情况会趋于平衡。像 Spring Cloud、Spring Boot 和 Spring IO Platform 这样的伞形项目也有助于消除障碍:如果您可以使用其中一个来管理所有依赖项,事情就会简单得多。

Spring Guides 中的示例应用现在都已更新到 Spring Boot 1.3,即使这意味着它们依赖于 Spring Cloud 的一个里程碑版本(这仅适用于 Zuul 代理示例)。许多应用不再需要 Spring Cloud。如果您需要 Spring Cloud 的 GA 版本,现在需要停留在 Spring Boot 1.2 版本。该组合的示例可以从 git 历史记录中获取。

订阅 Spring 新闻邮件

订阅 Spring 新闻邮件保持联系

订阅

快人一步

VMware 提供培训和认证,助力您加速前进。

了解更多

获取支持

Tanzu Spring 通过简单的订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

近期活动

查看 Spring 社区的所有近期活动。

查看全部