Maven是Java™开发人员的出色构建工具,您也可以使用它来管理项目的生命周期。 作为生命周期管理工具,Maven跨阶段运行,而不是Ant风格的构建“任务”。 Maven处理项目生命周期的所有阶段,包括验证,代码生成,编译,测试,打包,集成测试,验证,安装,部署以及项目站点的创建和部署。
要了解Maven与传统构建工具之间的区别,请考虑构建JAR文件和EAR文件的过程。 使用Ant,您需要定义特定的任务来组装每个工件。 另一方面,Maven可以为您完成大部分工作:您只需告诉它项目是JAR还是EAR文件,然后指示它处理“打包”阶段即可。 Maven将找到所需的资源并构建文件。
您会发现许多入门指南,这些入门指南包括在本文的“ 相关主题”部分中列出的一些指南。 这里的五个技巧旨在帮助您了解接下来的内容:使用Maven管理应用程序生命周期时出现的编程方案。
1.可执行的JAR文件
关于本系列
所以您认为您了解Java编程吗? 事实是,大多数开发人员从头开始学习Java平台,仅学习足够的知识即可完成工作。 在这个正在进行的系列文章中,Java技术专家深入探讨了Java平台的核心功能,并提出了一些技巧和窍门,这些技巧和窍门甚至可以帮助您解决最棘手的编程挑战。
使用Maven构建JAR文件非常容易:只需将项目包装定义为“ jar”,然后执行包装生命周期阶段即可。 但是定义可执行的JAR文件更加棘手。 有效地执行此操作涉及以下步骤:
在您的JAR的MANIFEST.MF文件中定义一个main类,该main类定义了可执行类。 (MANIFEST.MF是打包应用程序时Maven生成的文件。) 查找项目依赖的所有库。 将那些库包括在MANIFEST.MF文件中,以便您的应用程序类可以找到它们。
您可以手动完成所有这些操作,也可以在两个Maven插件( maven-jar-plugin和maven-dependency-plugin的帮助下更有效地进行操作。
maven-jar-plugin
maven-jar-plugin做很多事情,但是在这里我们感兴趣的是使用它来修改默认MANIFEST.MF文件的内容。 在POM文件的插件部分中,添加清单1中所示的代码:
清单1.使用maven-jar-plugin修改MANIFEST.MF
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.mypackage.MyClass</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
所有Maven插件都通过<configuration>元素公开其配置。 在此示例中, maven-jar-plugin修改了其archive属性,特别是存档的manifest属性,该属性控制MANIFEST.MF文件的内容。 它包括三个元素:
addClassPath :将此元素设置为true可以告诉maven-jar-plugin将一个Class-Path元素添加到MANIFEST.MF文件,并在该Class-Path元素中包括所有依赖项 classpathPrefix :如果计划将所有依赖项与要构建的JAR包含在同一目录中,则可以忽略此元素; 否则,请使用classpathPrefix指定所有相关JAR文件的前缀。 在清单1中, classpathPrefix指定所有依赖项应位于相对于归档文件的“ lib ”文件夹中。 mainClass :使用此元素定义当用户使用java -jar命令执行JAR文件时要执行的类的名称。
Maven依赖插件
使用这三个元素配置MANIFEST.MF文件后,下一步是实际将所有依赖项复制到lib文件夹。 为此,您使用了maven-dependency-plugin ,如清单2所示:
清单2.使用maven-dependency-plugin将依赖项复制到lib
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/lib
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
maven-dependency-plugin具有copy-dependencies目标,该目标会将您的依赖关系复制到您选择的目录中。 在此示例中,我将依赖项复制到了build目录( project-home/target/lib )下的lib目录。
有了依赖关系并修改了MANIFEST.MF后,您可以使用简单的命令启动应用程序:
java -jar jarfilename.jar
2.自定义MANIFEST.MF
尽管maven-jar-plugin允许您修改MANIFEST.MF文件的公共部分,但有时您需要更自定义的MANIFEST.MF。 解决方案有两个:
在“模板” MANIFEST.MF文件中定义所有自定义配置。 配置maven-jar-plugin以使用您的MANIFEST.MF文件,并使用任何Maven定制对其进行扩充。
例如,考虑一个包含Java代理的JAR文件。 为了运行Java代理,它需要定义Premain-Class和权限。 清单3显示了这样的MANIFEST.MF文件的内容:
清单3.自定义MANIFEST.MF文件中的Premain-Class定义
Manifest-Version: 1.0
Premain-Class: com.geekcap.openapm.jvm.agent.Agent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true
在清单3中 ,我指定了Premain-Class com.geekcap.openapm.jvm.agent.Agent将被授予重新定义和重新转换类的权限。 接下来,我更新maven-jar-plugin以包括MANIFEST.MF文件,如清单4所示:
清单4.包括Premain-Class
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>
src/main/resources/META-INF/MANIFEST.MF
</manifestFile>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>
com.geekcap.openapm.ui.PerformanceAnalyzer
</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
Maven的3
Maven 2已成为最受欢迎和使用最广泛的开源Java生命周期管理工具之一。 Maven 3于2010年9月晋升为alpha 5,为Maven带来了一些期待已久的变化。 请参阅“ 相关主题”部分,以了解Maven 3的新增功能。
这是一个有趣的示例,因为它都定义了一个Premain-Class ,它允许JAR文件作为Java代理工作,并且具有mainClass ,使它可以作为可执行JAR文件运行。 在这个特定示例中,我使用了OpenAPM (我构建的代码跟踪工具)来定义将由Java代理和用户界面记录的代码跟踪,这将有助于对记录的跟踪进行分析。 简而言之,该示例显示了将显式清单文件与动态修改相结合的强大功能。
3.依赖树
Maven最有用的功能之一是它对依赖项管理的支持:您只需定义应用程序所依赖的库,然后Maven即可找到它们(在本地或中央存储库中),下载它们并使用它们来编译代码。 。
有时,您可能需要了解特定依赖项的来源-例如,如果要在构建中找到同一JAR文件的不同且不兼容的版本。 在这种情况下,您需要阻止将一个版本的JAR文件包含在您的构建中,但是首先您需要找到保存JAR的依赖项。
一旦您知道以下命令,查找依赖关系就变得异常容易:
mvn dependency:tree
dependency:tree参数显示所有直接依赖关系,然后显示所有子依赖关系(及其子依赖关系,依此类推)。 例如,清单5是我的一个依赖项所要求的客户端库的摘录:
清单5.一个Maven依赖树
[INFO] ------------------------------------------------------------------------
[INFO] Building Client library for communicating with the LDE
[INFO] task-segment: [dependency:tree]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:tree {execution: default-cli}]
[INFO] com.lmt.pos:sis-client:jar:2.1.14
[INFO] +- org.codehaus.woodstox:woodstox-core-lgpl:jar:4.0.7:compile
[INFO] | \- org.codehaus.woodstox:stax2-api:jar:3.0.1:compile
[INFO] +- org.easymock:easymockclassextension:jar:2.5.2:test
[INFO] | +- cglib:cglib-nodep:jar:2.2:test
[INFO] | \- org.objenesis:objenesis:jar:1.2:test
您可以在清单5中看到sis-client项目需要woodstox-core-lgpl和easymockclassextension库。 easymockclassextension库又需要cglib-nodep库和objenesis库。 如果我在使用objenesis遇到问题,例如1.2和1.3两个版本,则此依赖关系树将向我显示1.2工件是由easymockclassextension库间接导入的。
dependency:tree参数为我节省了许多调试损坏的构建的时间; 希望对您也一样。
4.针对不同环境的构建
大多数实质性项目至少具有一组核心环境,其中包括与开发,质量保证(QA),集成和生产相关的任务。 管理所有这些环境的挑战在于配置构建,该构建必须连接到正确的数据库,执行正确的脚本集并将所有正确的工件部署到每个环境。 使用Maven配置文件可让您执行所有这些操作,而不必分别为每个环境构建明确的说明。
关键在于将环境配置文件与面向任务的配置文件相结合。 每个环境概要文件都定义了其特定的位置,脚本和服务器。 因此,在我的pom.xml文件中,我将定义面向任务的构建,如清单6所示:
清单6.部署构建
<build>
<plugins>
<plugin>
<groupId>net.fpic</groupId>
<artifactId>tomcat-deployer-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<id>pos</id>
<phase>install</phase>
<goals>
<goal>deploy</goal>
</goals>
<configuration>
<host>${deploymentManagerRestHost}</host>
<port>${deploymentManagerRestPort}</port>
<username>${deploymentManagerRestUsername}</username>
<password>${deploymentManagerRestPassword}</password>
<artifactSource>
address/target/addressservice.war
</artifactSource>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
此构建执行tomcat-deployer-plugin ,该tomcat-deployer-plugin配置为连接到特定的主机,端口以及特定的用户名和密码凭据。 所有这些信息都是使用变量定义的,例如${deploymentmanagerRestHost} 。 这些变量是在每个配置文件中按环境定义的,如清单7所示:
清单7.环境配置文件
<profiles>
<profile>
<id>dev</id>
<properties>
<deploymentManagerRestHost>10.50.50.52</deploymentManagerRestHost>
<deploymentManagerRestPort>58090</deploymentManagerRestPort>
<deploymentManagerRestUsername>myusername</deploymentManagerRestUsername>
<deploymentManagerRestPassword>mypassword</deploymentManagerRestPassword>
</properties>
</profile>
<profile>
<id>qa</id>
<properties>
<deploymentManagerRestHost>10.50.50.50</deploymentManagerRestHost>
<deploymentManagerRestPort>58090</deploymentManagerRestPort>
<deploymentManagerRestUsername>myotherusername</deploymentManagerRestUsername>
<deploymentManagerRestPassword>myotherpassword</deploymentManagerRestPassword>
</properties>
</profile>
</profiles>
部署Maven配置文件
在清单7的概要文件中,我定义了两个概要文件,并根据概要文件名称中的值激活了它们。 如果选择的概要文件是dev那么将使用开发部署信息。 如果选择的概要文件是qa ,那么将使用QA部署信息,依此类推。
这是部署到开发的命令:
mvn -Pdev clean install
–Pdev标志告诉Maven激活开发配置,而传递-Pqa将激活QA配置。
5.自定义Maven插件
Maven提供了数十种预建插件供您使用,但是在某些时候,您可能会发现自己需要自定义插件。 构建自定义Maven插件非常简单:
创建一个新项目,将POM包装设置为“ maven-plugin 。 包括对maven-plugin-plugin的调用,该调用定义了您公开的插件目标。 创建一个Maven插件“ mojo ”类(扩展AbstractMojo的类)。 注释该类以定义将作为配置参数公开的目标和变量。 注释@Mojo是必需的,它控制执行mojo的方式和时间。 实现execute()方法,当您的插件被调用时将被调用。
作为示例,清单8显示了用于部署Tomcat的自定义插件的相关部分:
清单8. TomcatDeployerMojo.java
package net.fpic.maven.plugins;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
/**
* Goal that deploys a web application to Tomcat
*/
@Mojo(name = "deploy", defaultPhase = LifecyclePhase.INSTALL)
public class TomcatDeployerMojo extends AbstractMojo {
/**
* The host name or IP address of the deployment server
*/
@Parameter(alias = "host", property = "deploy.host", required = true)
private String serverHost;
/**
* The port of the deployment server
*/
@Parameter(alias = "port", property = "deploy.port", defaultValue = "58020", required = true)
private String serverPort;
/**
* The username to connect to the deployment manager (if omitted then the plugin
* attempts to deploy the application to the server without credentials)
*/
@Parameter(alias = "username", property = "deploy.username")
private String username;
/**
* The password for the specified username
*/
@Parameter(alias = "password", property = "deploy.password")
private String password;
/**
* The name of the source artifact to deploy, such as target/pos.war
*/
@Parameter(alias = "artifactSource", property = "deploy.artifactSource", required = true)
private String artifactSource;
/**
* The destination name of the artifact to deploy, such as ROOT.war.
* If not present then the
* artifact source name is used (without pathing information)
*/
@Parameter(alias = "artifactDestination", property = "deploy.artifactDestination")
private String artifactDestination;
public void execute() throws MojoExecutionException {
getLog().info("Server Host: " + serverHost +
", Server Port: " + serverPort +
", Artifact Source: " + artifactSource +
", Artifact Destination: " + artifactDestination);
// Validate our fields
if (serverHost == null) {
throw new MojoExecutionException(
"No deployment host specified, deployment is not possible");
}
if (artifactSource == null) {
throw new MojoExecutionException(
"No source artifact is specified, deployment is not possible");
}
...
}
}
在类级别, @Mojo批注值name指定此MOJO执行的目标,而lifecyclePhase指定目标执行的阶段。 除了映射到包含实际值的系统属性的表达式之外,每个公开的属性都有一个@Parameter批注,该批注指定将通过其执行参数的别名。 如果属性设置了required=true ,那么它是必需的。 如果它具有defaultValue ,那么如果未指定该值,则将使用该值。 在execute()方法中,可以调用getLog()来访问Maven记录器,该记录器将根据记录级别将指定的消息输出到标准输出设备。 如果该插件失败,则抛出MojoExecutionException将导致构建失败。
结论
您可以仅将Maven用于构建,但最好的Maven是项目生命周期管理工具。 本文介绍了五个鲜为人知的功能,这些功能可以帮助您更有效地使用Maven。 请参阅“ 相关主题”部分,以了解有关Maven的更多信息。
翻译自: https://www.ibm.com/developerworks/java/library/j-5things13/index.html
相关资源:阿帕奇maven3.3.3