抢先一步
VMware 提供培训和认证,助你加速前进。
了解更多Spring Boot 1.0 RC4 刚刚发布,1.0 正式版也不会太远了,并且还有各种酷炫的新特性即将到来!
关于这方面我收到很多问题,其中一个就是关于 Boot 应用的部署策略。Spring Boot 构建在 Spring 之上,可以在 Spring 能运行的任何地方运行。它享有 Spring 的可移植性。Spring Boot 让开发者首先专注于应用开发,并消除了过度关注应用生命周期其他方面的必要,包括部署和管理。
它的目标是开箱即用,达到生产就绪状态。作为其中的一部分,Spring Boot 默认情况下会做一些与众不同的事情,这对于一些人来说可能起初会感到陌生。在这篇文章中,我希望简要介绍一些部署 Spring Boot 应用的常见策略。在深入探讨之前,我将简要介绍一下它和一些示例代码。您可以随意跳过本节,从嵌入式 Web 服务器部署部分开始阅读。
如果你还没用过 Spring Boot,快试试吧!有很多方法可以开始,包括 start.spring.io 上的 Spring Initializr webservice,以及——如果你正在使用 Spring Tool Suite——一个更熟悉的、集成的向导,它最终会调用相同的 webservice。我通常通过勾选 Actuator 和 Web 复选框开始,然后选择生成一个 Maven Project。这将给你两个 starter 类,Application.java 和 ApplicationTests.java,以及一个随时可用的 Maven pom.xml 文件。
这是解压后的 starter 项目
➜ pwd
/Users/jlong/Downloads/starter
➜ starter tree
.
├── pom.xml
└── src
├── main
│ └── java
│ └── demo
│ └── Application.java
└── test
└── java
└── demo
└── ApplicationTests.java
7 directories, 3 files
➜ starter
Maven 构建依赖于 Spring Boot starter 依赖。这些依赖是带有主张的。它们带来了已知的、随时可用的技术栈,这些技术栈与你面前的任务对齐,而不是你可能使用的技术栈:换句话说,如果你想构建一个 web 应用,那么只需简单地依赖 Spring Boot starter web 依赖,就像这样
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Maven 构建从其父级 pom 继承要使用的依赖版本信息,这也由 Spring Boot 提供。你无需担心协调常见的 Spring 项目版本和第三方依赖。
生成的 Java 类是样板代码(这就是为什么它们是生成的!)。你通常不会更改类本身,尽管你可以。到本篇博客结束时,你将掌握部署 Spring Boot 应用的常用方法。这(希望如此!)是你在 Spring Boot 中遇到的唯一样板代码。这是 Spring Boot 提供的 Application.java 类
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
为了在文章中演示,我们将添加一个 RESTful Spring MVC 控制器。这是修改后的 Application.java 代码页面,包含一个 Spring MVC REST 控制器,当请求访问 /hello/World 时,它会响应 "Hello, World"
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@RestController
class GreetingController {
@RequestMapping("/hello/{name}")
String hello(@PathVariable String name) {
return "Hello, " + name + "!";
}
}
开箱即用,Spring Boot 使用 public static void main 入口点为你启动一个嵌入式 web 服务器。
如果你使用 Spring Boot Initializr 提供的 Maven 构建 (mvn clean install),你会得到一个 fat jar。这个 jar 很方便,因为它包含了所有其他依赖以及像你的 web 服务器这样的东西都在归档文件内部。你可以把这个 .jar 文件给任何人,他们就可以轻松运行你的整个 Spring 应用:无需构建工具,无需设置,无需 web 服务器配置等:只需运行 java -jar ...your.jar。
当你运行应用时,Spring Boot 默认会检测到你有一个 Spring MVC 控制器,并启动一个嵌入式的 Apache Tomcat 7 实例。你应该能够通过打开浏览器并访问 https://:8080/hello/World 来测试 REST 端点。
嵌入式 Tomcat 有许多配置选项。你可以通过提供一个 EmbeddedServletContainerCustomizer 来轻松地为你的 webservice 启用 HTTPS (SSL/TLS 终止),就像我在这个示例中做的那样。那里描述的模块是一个交钥匙式的 web 应用,它可以在 HTTPS 上运行,只需要一个 SSL/TLS 证书,并嵌入了自己的 web 服务器。运行那个特定的应用非常简单: java -Dspring.profiles.active=production -Dkeystore.file=file:///$PWD/src/main/resources/keystore.p12 -jar target/oauth-1.0.0.BUILD-SNAPSHOT.jar。
这个 EmbeddedServletContainerCustomizer 配置 SPI 让你能够利用独立 Apache Tomcat 实例的大部分显式 XML 配置能力。更小的细节,例如服务器运行在哪个端口上,可以通过命令行(如 --D-style 参数)或通过加载的属性文件来配置(例如,Spring Boot 会自动查找 CLASSPATH 上名为 application.properties 的文件中的任何属性)。因此,要改变 Tomcat 监听的端口,你可以指定 --Dserver.port=8081,让它监听 8081 端口。如果你指定 server.port=0,它将自动查找一个未使用的端口进行监听。
默认情况下,Spring Boot 使用 Tomcat 7。如果你想使用 Tomcat 8,只需说明即可!你只需覆盖 Maven 构建的 tomcat.version 属性,这将触发更高版本 Apache Tomcat 的解析。
<properties>
<tomcat.version>8.0.3</tomcat.version>
</properties>
当然,有些人可能想使用 Jetty 嵌入式 servlet 容器。Jetty 也是一个不错的选择。你可以简单地排除 Spring Boot starter Tomcat 模块,然后引入 Spring Boot Starter Jetty 模块。Spring Boot 将自动转而使用 Jetty。这是我们 Maven 构建中修改后的 dependencies 部分
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
如果你想切换到 Jetty 9,那也很容易。确保你的 Maven 构建中有以下 properties。
<properties>
<java.version>1.7</java.version>
<jetty.version>9.1.0.v20131115</jetty.version>
<servlet-api.version>3.1.0</servlet-api.version>
</properties>
但是,我想你可能在想,“我怎么把它部署到已有的 Tomcat 安装,或者像 WebSphere、WebLogic 或 JBoss 这样的经典 Java EE 应用服务器(其中一些可不便宜!)上呢?” 简单!毕竟它仍然只是 Spring,所以需要的改动非常少。你需要进行三个直观的更改:在 Maven 中从 jar 构建转换为 war 构建:注释掉 pom.xml 文件中 spring-boot-maven-plugin 插件的声明,然后将 Maven 的 packaging 类型更改为 war。最后,在你的应用中添加一个 web 入口点。Spring 使用 Servlet 3 Java 配置为你配置了几乎所有东西。你只需要给它这个机会即可。修改你的 Application 入口点类,如下所示
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
private static Class<Application> applicationClass = Application.class;
}
@RestController
class GreetingController {
@RequestMapping("/hello/{name}")
String hello(@PathVariable String name) {
return "Hello, " + name + "!";
}
}
这个新的基类 - SpringBootServletInitializer - 利用了 Servlet 3 风格的 Java 配置 API,让你可以在代码中描述以前只能在 web.xml 中描述的内容。这类配置类会在应用启动时被发现并调用。这使得 Spring Boot 有机会告诉 web 服务器关于应用的信息,包括各种 Spring 项目通常所需的 Servlet、Filter 和 Listener。
如果你的类与大型应用服务器自带的类发生冲突,你可能会遇到问题。在这种情况下,使用你的构建工具的功能来排除或将相关的 API 设置为 optional。以下是我为了让 starter Spring Boot REST 服务在 JBoss WildFly(之前称为 JBoss AS 的应用服务器)上成功运行而对 Maven 构建所做的更改
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.demo</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<description>Demo project</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<start-class>demo.Application</start-class>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.7</java.version>
</properties>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
然后我能够重新运行构建,并将构建好的 .war 文件 cp 到 $WILDFLY_HOME/standalone/deployments 目录。
如果应用服务器尚未运行,请启动它,然后你应该能够通过访问 https://:8080/$YOURAPP/hello/World 来启动应用。同样,我用 $YOURAPP 替换了你构建的应用名称。
关于部署的故事,如果不涉及当今增长最快的部署目标——云端,那就不完整!当然,当我们谈论云时,最好具体说明:如果你是指直接部署到 Amazon Web Services 或 Google Compute Engine,那么这就和你在自己数据中心的 Linux 服务器上运行应用一样,一切如常。因为,基本上你就是在做同样的事情。
如果你试图将应用部署到平台即服务 (Platform-as-a-service),Spring 备受赞誉的可移植性在这里为你提供了很多选择。部署到 Heroku,特别是采用 fat-jar 方法,是 Heroku 的现状,因为那个平台即服务反正期望你自己带上容器!只需将 java -jar 咒语放在你的 Procfile 中,就可以开始了。
使用 Cloud Foundry,你可以将应用部署为独立应用或一个 .war 风格的 web 应用。构建完应用后(例如,使用 mvn clean install)并安装了 cf 命令行工具,只需按照我在下面回答 cf push 命令的提示即可
➜ cf push --path target/demo-0.0.1-SNAPSHOT.jar
Name> $YOURAPP
Instances> 1
1: 128M
2: 256M
3: 512M
4: 1G
Memory Limit> 256M
Creating $YOURAPP... OK
1: $YOURAPP
2: none
Subdomain> $YOURAPP
1: cfapps.io
2: none
Domain> cfapps.io
Creating route $YOURAPP.cfapps.io... OK
Binding $YOURAPP.cfapps.io to $YOURAPP... OK
Create services for application?> n
Bind other services to application?> n
Save configuration?> y
Saving to manifest.yml... OK
Uploading $YOURAPP... OK
Preparing to start $YOURAPP... OK
-----> Downloaded app package (8.7M)
-----> Java Buildpack source: system
-----> Downloading Open JDK 1.7.0_51 from http://d2vm4m9hl67ira.cloudfront.net/openjdk/lucid/x86_64/openjdk-1.7.0_51.tar.gz (1.4s)
Expanding Open JDK to .java-buildpack/open_jdk (1.3s)
-----> Downloading Spring Auto Reconfiguration 0.8.7 from http://d2vm4m9hl67ira.cloudfront.net/auto-reconfiguration/auto-reconfiguration-0.8.7.jar (0.0s)
-----> Uploading droplet (43M)
Checking status of app '$YOURAPP'...
0 of 1 instances running (1 starting)
0 of 1 instances running (1 starting)
1 of 1 instances running (1 running)
Push successful! App '$YOURAPP' available at http://$YOURAPP.cfapps.io
应用应该已经启动并运行,可以从 http://$YOURAPP.cfapps.io/hello/Cloud%20Foundry 访问,其中 $YOURAPP 再次用作应用名称的占位符。
对于一个小小的 Application 类和对构建文件的一些修改来说,这不算糟糕!
Spring Boot 的目标是默认就达到生产就绪状态。这意味着它开箱即用地提供了实用的默认配置,如有必要可以覆盖。默认情况下,Spring Boot 提供了一个嵌入式 Apache Tomcat 构建。默认情况下,Spring Boot 以最自然的方式为你配置一切,以适应当今的平台,并支持领先的平台即服务。
Spring Boot 提供了许多覆盖配置的机会,包括可配置属性和定制回调。
展望未来,我已经能看到接下来的几篇文章将继续探讨这个话题,包括通过监控和管理工具(JMX 和 JConsole, New Relic 等)对 Spring Boot 应用进行管理,以及安全方面的问题。令人高兴的是,Spring Boot 为所有这些问题提供了解决方案,甚至更多。
Spring Boot 文档正随着其向 1.0 迈进而快速完善,与此同时,还有许多很棒的资源可以继续探索。看看我最喜欢的一个包含各种技巧(tips 'n tricks)的百宝箱页面,即 How To 文档,别忘了查看 Spring IO 指南,其中大部分都基于 Spring Boot!
我很想在线上继续这场讨论。我想知道你在探索 Spring Boot 时还有哪些问题想要解答,所以不要害羞。我将在 2014 年 4 月 9 日进行一场关于 Spring Boot 的虚拟 JUG(Java 用户组)直播活动!该活动面向全球,并具有互动性,所以请带上你的问题、评论和反馈。