抢先一步
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 实例。你应该能够通过打开浏览器并访问 http://localhost: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
目录。
如果应用服务器尚未运行,请启动它,然后你应该能够通过访问 http://localhost: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 用户组)直播活动!该活动面向全球,并具有互动性,所以请带上你的问题、评论和反馈。