用 Maven 管理项目文件周期的技巧 java
您以为本身懂 Java 编程?事实是,大多数开发人员都只领会到了 Java 平台的皮毛,所学也只够应付工做。在本系列 中,Java 技术深度挖掘 Java 平台的核心功能,揭示一些不为人知的事实,帮助您解决最棘手的编程困难。 apache
Maven 是为 Java™ 开发人员提供的一个极为优秀的构建工具,您也可使用它来管理您的项目生命周期。做为一个生命周期管理工具,Maven 是基于阶段操做的,而不像 Ant 是基于 “任务” 构建的。Maven 完成项目生命周期的全部阶段,包括验证、代码生成、编译、测试、打包、集成测试、安装、部署、以及项目网站建立和部署。 编程
为了更好地理解 Maven 和传统构建工具的不一样,咱们来看看构建一个 JAR 文件和一个 EAR 文件的过程。使用 Ant,您可能须要定义专有任务来组装每一个工件。另外一方面,Maven 能够为您完成大部分工做:您只须要告诉它是一个 JAR 文件仍是一个 EAR 文件,而后指示它来完成 “打包” 过程。Maven 将会找到所需的资源,而后构建文件。 api
在本文的 参考资料 部分,您将发现大量介绍 Maven 的入门教程。本文的 5 个技巧目的是帮助您解决即将出现的一些问题:使用 Maven 管理您的应用程序的生命周期时,将会出现的编程场景。 tomcat
使用 Maven 构建一个 JAR 文件比较容易:只要定义项目包装为 “jar”,而后执行包装生命周期阶段便可。可是定义一个可执行 JAR 文件却比较麻烦。采起如下步骤能够更高效: 服务器
您能够手工进行这些操做,或者要想更高效,您可使用两个 Maven 插件帮助您完成:maven-jar-plugin 和 maven-dependency-plugin。
maven-jar-plugin 能够作不少事情,但在这里,咱们只对使用它来修改默认 MANIFEST.MF 文件的内容感兴趣。在您的 POM 文件的插件部分添加清单 1 所示代码:
<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 文件的内容。包括 3 个元素:
当您使用这 3 个元素配置好了 MANIFEST.MF 文件以后,下一步是将全部的依赖项复制到 lib 文件夹。为此,使用 maven-dependency-plugin,如清单 2 所示:
<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 目录下的 lib 目录(project-home/target/lib)。
将您的依赖项和修改的 MANIFEST.MF 放在适当的位置后,您就能够用一个简单的命令启动应用程序:
java -jar jarfilename.jar
虽然 maven-jar-plugin 容许您修改 MANIFEST.MF 文件的共有部分,但有时候您须要一个更个性化的 MANIFEST.MF。解决方案是双重的:
例如,考虑一个包含 Java 代理的 JAR 文件。要运行一个 Java 代理,须要定义 Premain-Class 和设置许可。清单 3 展现了这样一个 MANIFEST.MF 文件的内容:
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 所示:
<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 2 已确立了它做为一种最流行和最易使用的开源 Java 生命周期管理工具的地位。Maven 3,2010 年 9 月升级为 alpha 5,带来一些期待已久的改进。在 参考资料部分寻找 Maven 的新功能。
这是一个颇有趣的示例,由于它既定义了一个 Premain-Class — 容许 JAR 文件做为一个 Java 代理运行,也有一个 mainClass — 容许它做为一个可执行的 JAR 文件运行。在这个特殊的例子中,我使用 OpenAPM(我已构建的一个代码跟踪工具)来定义将被 Java 代理和一个用户界面记录的代码跟踪。简而言之,这个示例展现一个显式清单文件与动态修改相结合的力量。
Maven 一个最有用的功能是它支持依赖项管理:您只须要定义您应用程序依赖的库,Maven 找到它们、下载它们、而后使用它们编译您的代码。
必要时,您须要知道具体依赖项的来源 — 这样您就能够找到同一个 JAR 文件的不一样版本的区别和矛盾。这种状况下,您将须要防止将一个版本的 JAR 文件包含在您的构建中,可是首先您须要定位保存 JAR 的依赖项。
一旦您知道下列命令,那么定位依赖项每每是至关容易的:
mvn dependency:tree
dependency:tree 参数显示您的全部直接依赖项,而后显示全部子依赖项(以及它们的子依赖项,等等)。例如,清单 5 节选自个人一个依赖项所须要的客户端库:
[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 参数为我节省了不少调试时间,我但愿对您也一样有帮助。
多数重大项目至少有一个核心环境,由开发相关的任务、质量保证(QA)、集成和生产组成。管理全部这些环境的挑战是配置您的构建,这必须链接到正确的数据库中,执行正确的脚本集、并为每一个环境部署正确的工件。使用 Maven 配置文件让您完成这些任务,而无需为每一个环境分别创建明确指令。
关键在于环境配置文件和面向任务的配置文件的合并。每一个环境配置文件定义其特定的位置、脚本和服务器。所以,在个人 pox.xml 文件中,我将定义面向任务的配置文件 “deploywar”,如清单 6 所示:
<profiles> <profile> <id>deploywar</id> <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> </profile> </profiles>
这个配置文件(经过 ID “deploywar” 区别)执行 tomcat-deployer-plugin,被配置来链接一个特定主机和端口,以及指定用户名和密码证书。全部这些信息使用变量来定义,好比 ${deploymentmanagerRestHost}。这些变量在个人 profiles.xml 文件中定义,如清单 7 所示:
<!-- Defines the development deployment information --> <profile> <id>dev</id> <activation> <property> <name>env</name> <value>dev</value> </property> </activation> <properties> <deploymentManagerRestHost>10.50.50.52</deploymentManagerRestHost> <deploymentManagerRestPort>58090</deploymentManagerRestPort> <deploymentManagerRestUsername>myusername</deploymentManagerRestUsername> <deploymentManagerRestPassword>mypassword</deploymentManagerRestPassword> </properties> </profile> <!-- Defines the QA deployment information --> <profile> <id>qa</id> <activation> <property> <name>env</name> <value>qa</value> </property> </activation> <properties> <deploymentManagerRestHost>10.50.50.50</deploymentManagerRestHost> <deploymentManagerRestPort>58090</deploymentManagerRestPort> <deploymentManagerRestUsername> myotherusername </deploymentManagerRestUsername> <deploymentManagerRestPassword> myotherpassword </deploymentManagerRestPassword> </properties> </profile>
在 清单 7 的 profiles.xml 文件中,我定义了两个配置文件,并根据 env (环境)属性的值激活它们。若是 env 属性被设置为 dev,则使用开发部署信息。若是 env 属性被设置为 qa,那么将使用 QA 部署信息,等等。
这是部署文件的命令:
mvn -Pdeploywar -Denv=dev clean install
-Pdeploywar 标记通知要明确包含 deploywar 配置文件。-Denv=dev 语句建立一个名为 env 的系统属性,并将其值设为 dev,这激活了开发配置。传递 -Denv=qa 将激活 QA 配置。
Maven 有十几个预构建插件供您使用,可是有时候您只想找到本身须要的插件,构建一个定制的 Maven 插件比较容易:
例如,清单 8 显示了一个定制插件(为了部署 Tomcat)的相关部分:
package net.fpic.maven.plugins; import java.io.File; import java.util.StringTokenizer; import net.fpic.tomcatservice64.TomcatDeploymentServerClient; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import com.javasrc.server.embedded.CommandRequest; import com.javasrc.server.embedded.CommandResponse; import com.javasrc.server.embedded.credentials.Credentials; import com.javasrc.server.embedded.credentials.UsernamePasswordCredentials; import com.javasrc.util.FileUtils; /** * Goal that deploys a web application to Tomcat * * @goal deploy * @phase install */ public class TomcatDeployerMojo extends AbstractMojo { /** * The host name or IP address of the deployment server * * @parameter alias="host" expression="${deploy.host}" @required */ private String serverHost; /** * The port of the deployment server * * @parameter alias="port" expression="${deploy.port}" default-value="58020" */ 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" expression="${deploy.username}" */ private String username; /** * The password for the specified username * * @parameter alias="password" expression="${deploy.password}" */ private String password; /** * The name of the source artifact to deploy, such as target/pos.war * * @parameter alias="artifactSource" expression=${deploy.artifactSource}" * @required */ 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" * expression=${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" ); } ... } }
在这个类的头部,@goal 注释指定 MOJO 执行的目标,而 @phase 指出目标执行的阶段。除了一个映射到一个有真实值的系统属性的表达式以外,每一个公布的属性有一个 @phase 注释,经过将被执行的参数指定别名。若是属性有一个 @required 注释,那么它是必须的。若是它有一个default-value,那么若是没有指定的话,将使用这个值。在 execute() 方法中,您能够调用 getLog() 来访问 Maven 记录器,根据记录级别,它将输出具体消息到标准输出设备。若是插件发生故障,抛出一个 MojoExecutionException 将致使构建失败。
您可使用 Maven 只进行构建,可是最好的 Maven 是一个项目生命周期管理工具。本文介绍了 5 个你们不多了解的特性,能够帮助您更高效地使用 Maven。在 参考资料 部分获取 Maven 的更多信息。