目次
1. maven-assembly-plugin プラグインの簡単な使用
3. ブート プロジェクトを war パッケージの展開に変換する方法
背景: 以前のプロジェクトでは、複数のブート プロジェクトのパッケージを圧縮して個別に展開するためにアセンブリが使用されていましたが、今回はリソースが削減され、一部のユーザーはそれらを war パッケージにパッケージ化して Web コンテナーで実行する必要があります。
この記事の内容は次の 3 点です。
- maven-assembly-plugin プラグインの簡単な使用
- アセンブリ プラグインを使用して、パラメーターに従ってさまざまな展開パッケージを作成する
- ブート プロジェクトを war パッケージに変換して Web コンテナーにデプロイする方法
1. maven-assembly-plugin プラグインの簡単な使用
1. アセンブリとは
簡単に言えば、maven-assembly-plugin は、作成するパッケージのタイプ、パッケージに含まれるコンテンツなど、パッケージングを支援するために使用されます。
2. 一般的な maven プラグイン
Maven プラグインは、ライフサイクルの特定の段階で実行されるタスクです。プラグインは機能を完成させます。以下にいくつかの一般的なプラグインを紹介します。
パッケージ化には、さまざまなプラグイン オプションがあります。最も一般的なのは次の 3 つです。
プラグイン | 関数 |
---|---|
maven-jar-plugin |
プロジェクト jar の作成に使用される Maven デフォルト パッケージ プラグイン |
maven-shade-plugin |
実行可能パッケージ、実行可能 (脂肪) jar を作成するために使用されます。 |
maven-assembly-plugin |
Apache プロジェクトのパッケージ方法など、カスタマイズされたパッケージ方法をサポートする |
3. 使い方は?
アセンブリを使用するには、次の構成を pom.xml ファイルに追加する必要があります。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<configuration>
<!--包的名称-->
<finalName>BootDemo</finalName>
<!--打包的名称是否拼接assembly.id-->
<appendAssemblyId>true</appendAssemblyId>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<!-- 添加此项后,可直接使用mvn package | mvn install -->
<!-- 不添加此项,需使用mvn package assembly:single|mvn package assembly:assembly -->
<executions>
<execution>
<!--名字任意 -->
<id>make-assembly</id>
<!-- 绑定到package生命周期阶段上 -->
<phase>package</phase>
<goals>
<!-- 只运行一次 -->
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
次に、対応するパスに assembly.xml ファイルを追加します。内容は次のとおりです。
<?xml version="1.0" encoding="UTF-8" ?>
<assembly>
<!--id 标识符,添加到生成文件名称的后缀符-->
<id>assembly_id</id>
<!--支持的打包格式有zip、tar、tar.gz (or tgz)、tar.bz2 (or tbz2)、jar、dir、war,可以同时指定多个打包格式-->
<formats>
<format>zip</format>
</formats>
<!--默认为true。指定打的包是否包含打包层目录(比如finalName是prefix,当值为true,所有文件被放在包内的prefix目录下,否则直接放在包的根目录下-->
<includeBaseDirectory>true</includeBaseDirectory>
<!--定制工程依赖 jar 包的打包方式-->
<dependencySets>
<dependencySet>
<!--指定包依赖目录,该目录是相对于根目录-->
<outputDirectory>lib</outputDirectory>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
<!--定制工程下其它文件的打包方式-->
<fileSets>
<fileSet>
<!--原文件目录-->
<directory>src/main/bin</directory>
<!--打包的目录-->
<outputDirectory>/bin</outputDirectory>
<includes>
<include>*.sh</include>
</includes>
<!--打包文件权限-->
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>/conf</outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
</fileSets>
</assembly>
テスト手順の構造:
上記の構成の後、渡すことができます
mvn package assembly:assembly または mvn clean install コマンドで zip パッケージを印刷
2. 組み立てによる各種パッケージの作り方
実際には、複数の assemblyy.xmll を追加することで実現されます。具体的な変更は次のとおりです。
pom.xml ファイルは、次の構成を追加します。
<!--配置不同的打包配置,如果不指定参数。默认一最后的profile配置为准,即,如下配置,默认打zip-->
<profiles>
<!--打war包的配置-->
<profile>
<id>war</id>
<activation>
<activeByDefault>true</activeByDefault>
<property>
<name>type</name>
<value>war</value>
</property>
</activation>
<properties>
<type>war</type>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>src/main/assembly/assembly_war.xml</descriptors>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<!--打zip包的配置-->
<profile>
<id>zip</id>
<activation>
<activeByDefault>true</activeByDefault>
<property>
<name>type</name>
<value>zip</value>
</property>
</activation>
<properties>
<type>zip</type>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>src/main/assembly/assembly.xml</descriptors>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
別の assembly_war.xml を追加します
<?xml version="1.0" encoding="UTF-8" ?>
<assembly>
<id>assembly</id>
<formats>
<format>war</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>{basedir}/target/classes</directory>
<fileMode>0755</fileMode>
<outputDirectory>WEB-INF/classes</outputDirectory>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>WEB-INF/lib</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>
</dependencySet>
</dependencySets>
</assembly>
このようにして、 mvn clean install -P war|zip を使用してさまざまなパッケージを作成できます
3. ブート プロジェクトを war パッケージの展開に変換する方法
ブート プロジェクトがスタートアップ クラスを自動的に生成し、スタートアップ クラスを介して開始することがわかっています。
@SpringBootApplication
public class BootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(BootdemoApplication.class, args);
}
}
SpringApplication.run メソッドを追跡すると、コア メソッドは org.springframework.boot.SpringApplication #run (java.lang.String...)であることがわかりました。
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
//根据webEnvironment的值来决定创建何种类型的ApplicationContext对象
//如果是web环境,则创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
//否则创建org.springframework.context.annotation.AnnotationConfigApplicationContext
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//主要是调用所有初始化类的 initialize 方法
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//初始化 Spring 容器
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
//调用 ApplicationRunner 或者 CommandLineRunner 的运行方法
this.callRunners(context, applicationArguments);
} catch (Throwable var12) {
this.handleRunFailure(context, var12, listeners);
throw new IllegalStateException(var12);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
return context;
} catch (Throwable var11) {
this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var11);
}
}
問題は、war デプロイメントに変換した後、このプロセスを Web コンテナーから開始する方法です。実際、ブートはすでに考慮されています
、 boot は、次のように変更された SpringBootServletInitializer クラスを提供します。
@SpringBootApplication
public class BootdemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(BootdemoApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(BootdemoApplication.class);
}
}
スタートアップ クラスを継承できない場合 (たとえば、スタートアップ クラスに既に親クラスがある場合)、SpringBootServletInitializer を継承する新しいクラスを作成し、configure を書き換えることができます。
public class ProjectServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(BootdemoApplication.class);
}
}
ソースコードをたどると、最終的に、Web コンテナーの起動時に org.springframework.boot.SpringApplication #run (java.lang.String...)を呼び出していることがわかりました。
一般的な原則は次のとおりです。
1. spring-boot の SpringBootServletInitializer クラスは、spring-web の WebApplicationInitializer インターフェースを実装します。
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
protected Log logger;
private boolean registerErrorPageFilter = true;
public SpringBootServletInitializer() {
}
protected final void setRegisterErrorPageFilter(boolean registerErrorPageFilter) {
this.registerErrorPageFilter = registerErrorPageFilter;
}
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setAttribute("logging.register-shutdown-hook", false);
this.logger = LogFactory.getLog(this.getClass());
//重点方法,这里最终会调用到SpringApplication#run
WebApplicationContext rootApplicationContext = this.createRootApplicationContext(servletContext);
if (rootApplicationContext != null) {
servletContext.addListener(new SpringBootServletInitializer.SpringBootContextLoaderListener(rootApplicationContext, servletContext));
} else {
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
}
}
//...
}
2. WebApplicationInitializer クラスと同じディレクトリに SpringServletContainerInitializer クラスがあり、javax.servlet-api の ServletContainerInitializer インターフェースを実装し、注釈 @HandlesTypes を介して onStartup メソッドの最初のパラメーターに WebApplicationInitializer クラスを渡します。
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public SpringServletContainerInitializer() {
}
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
List<WebApplicationInitializer> initializers = Collections.emptyList();
Iterator var4;
if (webAppInitializerClasses != null) {
initializers = new ArrayList(webAppInitializerClasses.size());
var4 = webAppInitializerClasses.iterator();
while(var4.hasNext()) {
Class<?> waiClass = (Class)var4.next();
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
((List)initializers).add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());
} catch (Throwable var7) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
}
}
}
}
if (((List)initializers).isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
} else {
servletContext.log(((List)initializers).size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort((List)initializers);
var4 = ((List)initializers).iterator();
while(var4.hasNext()) {
WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
//重点
initializer.onStartup(servletContext);
}
}
}
}
ServletContainerInitializer は Servlet 3.0 の新しいインターフェースです。これは主に、サーブレットやフィルターの登録など、Web コンテナーの開始時にサードパーティ コンポーネントの初期化作業を提供するために使用されます。
ServletContainerInitializer を使用するには、各フレームワークで、対応する jar パッケージの META-INF/services ディレクトリに javax.servlet.ServletContainerInitializer という名前のファイルを作成する必要があります. ファイルの内容は、特定の ServletContainerInitializer 実装クラス (つまり、SPI メカニズム) を指定します.次に、Web コンテナが起動すると、このイニシャライザが実行され、コンポーネントでいくつかの初期化作業が行われます。
このように、ブート プロジェクトは、アセンブリ プラグインを介して war パッケージにパッケージ化され、Web コンテナーにデプロイされます~