19.2 构建和部署WAR文件

19.2 构建和部署WAR文件

在本书中,我们编写Taco Cloud应用所需的服务时,都是在IDE中运行,或者通过命令行以可执行文件的形式运行。不管是使用哪种方式,都会有一个嵌入式的Tomcat服务器(在Spring WebFlux应用中会是Netty)来为应用的请求提供服务。

在很大程度上,借助Spring Boot的自动配置,我们不需要创建web.xml文件或Servlet initializer类来声明Spring的DispatcherServlet,以实现Spring MVC相关的功能。如果要将应用程序部署到Java应用服务器中,就需要构建一个WAR文件。而且,为了让应用服务器知道如何运行应用程序,我们还需要在WAR文件中包含一个servlet initializer,以扮演web.xml文件的角色并声明DispatcherServlet。

实际上,要将Spring Boot应用构建为WAR文件并不困难。在使用Initializr创建应用的时候,如果选择了WAR方案,其实并没有额外要做的事情了。

Initializr会确保所生成的项目包含servlet initializer类,并且构建文件调整为生成WAR文件。如果你在Initializr中选择了构建为JAR文件(或者只是想知道它们之间的差异是什么),那么可以继续向下阅读。

首先,我们需要有一种配置Spring DispatcherServlet的方式。虽然可以通过web.xml文件来实现,但是Spring Boot的SpringBootServletInitializer使这个过程变得更加简单了。SpringBootServletInitializer是一个能够感知Spring Boot环境的特殊SpringWebApplicationInitializer实现。除了配置Spring的DispatcherServlet之外,SpringBootServletInitializer还会查找Spring应用上下文中所有Filter、Servlet或ServletContextInitializer类型的bean,并将它们绑定到servlet容器中。

要使用SpringBootServletInitializer,我们需要创建一个子类并重写configure()方法来指明Spring配置类。程序清单19.1展现了IngredientServiceServletInitializer,它是SpringBootServletInitializer的子类,我们将会使用它来实现配料服务应用。

程序清单19.1 通过Java启用Spring Web应用
1
2
3
4
5
6
7
8
9
10
11
package tacos.ingredients;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class IngredientServiceServletInitializer
extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder builder) {
return builder.sources(IngredientServiceApplication.class);
}
}

我们可以看到,configure()方法以参数形式得到了一个SpringApplicationBuilder对象,并且将其作为结果返回。在中间的代码中,它调用sources()方法来注册Spring配置类。在本例中,它注册了IngredientServiceApplication类,这个类同时作为(可执行JAR的)引导类和Spring配置类。

虽然配料服务应用还有其他的Spring配置类,但是我们没有必要将它们全部注册到sources()方法中。IngredientServiceApplication类使用了@SpringBootApplication,说明将会启用组件扫描。组件扫描功能会发现其他的配置类并将它们添加进来。

在大多数情况下,SpringBootServletInitializer的子类都是样板式的。它引用了应用的主配置类。除此之外,在构建WAR时,每个应用都是相同的。我们几乎没有必要去修改它。

现在,我们已经编写完servlet initializer类。接下来,必须要对项目的构建文件做一些修改。如果使用Maven进行构建,那么所需的变更非常简单,只需要确保pom.xml中的<packaging>元素设置成war即可:

1
<packaging>war</packaging>

Gradle构建所需的变更也很简单直接,我们需要在build.gradle文件中应用war插件:

1
apply plugin: 'war'

现在,我们就可以构建应用了。如果使用Maven,那么我们可以借助Initializr所使用的Maven包装器来执行package goal:

1
mvnw package

构建成功的话,WAR文件将会出现在target目录下。如果使用Gradle来构建项目,那么可以使用Gradle包装器来执行build任务:

1
$ gradlew build

构建完成之后,我们可以在build/libs目录下找到WAR文件。剩下的事情就是部署应用了。不同应用服务器的部署过程会有所差异,所以请参考应用服务器部署过程的相关文档。

比较有意思的事情是,虽然我们构建了适用于Servlet 3.0(或更高版本)部署的WAR文件,但是这个WAR文件依然可以像可执行JAR文件那样在命令行中执行:

1
$ java -jar target/ingredient-service-0.0.19-SNAPSHOT.war

实际上,使用一个部署制件,我们同时实现了两种部署方案。

将微服务放到应用服务器中?

按照我们的初衷,更大的Taco Cloud应用是由多个微服务应用组成的,而配料服务只是其中之一。但是,在这里,我们所讨论的是将配料服务部署为一个单独的应用,并将其放到应用服务器中。这样做是合理的吗?

微服务通常和其他的应用一样,应该可以独立部署。尽管离开了Taco Cloud应用的上下文之后,配料服务也没有太大的用处,但是没有理由不能将其部署到Tomcat或其他应用服务器中。不过,我们需要注意的是,不要期望它能够具有像部署到云中那样的可扩展性。

虽然WAR文件作为Java部署的主流方案已经有20多年的历史了,但是它们确实是为将应用程序部署到传统Java应用服务器而设计的。按照我们所选择的平台,现代云部署方案并不需要WAR文件,有些甚至都不支持这种格式。随着我们进入云部署的新时代,JAR文件可能是更好的选择。