973114 发表于 2016-12-30 08:49:30

Apache maven介绍

一 Maven介绍
  Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(ProjectLifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。 当你使用Maven的时候,你用一个明确定义的项目对象模型来描述你的项目,然后 Maven 可以应用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件。
  从Apache maven网站上下载maven并安装,maven目录下/conf/setting.xml中定义的是全局配置信息,里面包含了项目公用的localRepository(私有仓库)位置,pluginGroups,proxies,servers,profiles等属性,xml里文档注释很详细,具体含义可以参见文档,比较重要的有localRepository,profiles等,建议将配置文件放入~/.m2/settings.xml,实现用户范围控制。

<!--localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ~/.m2/repository
<localRepository>/path/to/local/repo</localRepository>
-->
<localRepository>d:/rr</localRepository>

  pom是描述项目对象模型的,每个maven项目必须包含pom.xml,一个简单的pom中包含的信息有:坐标信息、依赖管理、属性值等。
  超级POM定义了一组被所有项目共享的默认设置。它是Maven安装的一部分,可在/usr/local/maven/lib中的maven-xxx.jar文件中找到。如果你看一下这个JAR文件,你会看到在包org.apache.maven.project下看到一个名为pom-4.0.0.xml的文件。从这个文件可以看到默认的属性值和默认执行的插件。

二 Maven 变量

1 Maven提供了三个隐式的变量:

env
  暴露了你操作系统或者shell的环境变量

project
  暴露了POM。你可以使用点标记(.)的路径来引用POM元素的值${project.artifactId},你可能在老的构建中看到使用${pom.xxx}或者仅仅${xxx}来引用POM属性。这些方法已被弃用,我们只应该使用${project.xxx}。所有pom中的元素都可以用 project. 前缀进行引用,以下是部分常用的
  ${project.build.directory } results in the path to your "target" dir, this is the same as ${pom.project.build.directory }
  ${project.build. outputD irectory } results in the path to your "target/classes" dir
  ${project.name } refers to the name of the project.
  ${project.version } refers to the version of the project.
  ${project.build.finalName } refers to the final name of the file created when the built project is packaged

settings
  暴露了Maven settings信息如:
  ${settings.offline}会引用~/.m2/settings.xml文件中offline元素的值
  ${settings.localRepository } refers to the path of the user's local repository.

2 内置属性
  主要有两个常用内置属性:
  ${basedir}表示项目根目录,即包含pom.xml文件的目录
  ${version}表示项目版本

3 Java系统属性
  所有Java系统属性都可以使用Maven属性引用,例如${user.home}指向了用户目录。
  可以通过命令行mvn help:system查看所有的Java系统属性

4 环境变量属性
  所有环境变量都可以使用以env.开头的Maven属性引用。例如${env.JAVA_HOME}指代了JAVA_HOME环境变量的值。也可以通过命令行mvn help:system查看所有环境变量。
  系统的环境变量通过 env. 前缀引用,如:
  ${env.M2_HOME } returns the Maven2 installation path.
  ${java.home } specifies the path to the current JRE_HOME environment use with relative paths to get for example: 
  <jvm>${java.home}../bin/java.exe</jvm>

5 使用系统属性
  Java系统属性,所有可以通过java.lang.System中getProperties()方法访问的属性都被暴露成POM属性。一些系统属性的例子是:hudson,/home/hudson,/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0/jre,和Linux。一个完整的系统属性列表可以在java.lang.System类的Javadoc中找到。

6 自定义属性
  通过pom.xml或者settings.xml中的properties元素设置自己的属性,如:

<project>
...
<properties>
<foo>bar</foo>
</properties>
...
</project>

7 上级工程的变量 
  上级工程的pom中的变量用前缀 ${project.parent } 引用. 上级工程的版本也可以这样引用: ${parent.version }.

二 Maven 依赖管理
  Maven一个强大的功能是它能够帮助管理项目的依赖,一个项目对其他项目或包的依赖范围分为如下几种:



test
当你只有在测试的时候才引用类库的时候,你就要使用测试范围依赖


compile
编译范围(compile)依赖。(默认)如果你的项目在编译,测试,和运行中都依赖于一个类库


provided
当你的开发过程只有在编译和测试时需要一个类库,而该类库在运行的时候由容器提供,那么你就需要使用已提供范围的依赖,例如,如果你开发了一个web应用,你可能在编译classpath中需要可用的Servlet API来编译一个servlet,但是你不会想要在打包好的WAR中包含这个Servlet API;这个Servlet API JAR由你的应用服务器或者servlet容器提供


runtime
runtime依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如,你可能在编译的时候只需要JDBC API JAR,而只有在运行的时候才需要JDBC驱动实现。


system
system范围依赖与provided类似,但是你必须显式的提供一个对于本地系统中JAR文件的路径。这么做是为了允许基于本地对象编译,而这些对象是系统类库的一部分。这样的构件应该是一直可用的,Maven也不会在仓库中去寻找它。如果你将一个依赖范围设置成系统范围,你必须同时提供一个systemPath元素。注意该范围是不推荐使用的(你应该一直尽量去从公共或定制的Maven仓库中引用依赖)。

  可选依赖:

<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.4.1</version>
<optional>true</optional>
</dependency>
  在你将这些依赖声明为可选之后,你就需要在依赖于my-project的项目中显式的引用对应的依赖。例如,如果你正编写一个应用,它依赖于my-project,并且想要使用EHCache实现,你就需要在你项目添加如下的dependency元素,在理想的世界中,你不需要使用可选依赖。你可以将EHCache相关的代码放到yproject-ehcache子模块中,将SwarmCache相关的代码放到my-project-swarmcache子模块中,而非创建一个带有一系列可选依赖的大项目。
  依赖界限

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>[3.8,4.0)</version>
<scope>test</scope>
</dependency>
  传递依赖
  范围如何影响传递性依赖,行表示直接依赖,行与列的交叉就是为某个传递性依赖指定的范围。表中的空格意思是该传递性依赖被忽略)



 
compile
provided
runtime
test


compile
compile
-
runtime
-


provided
provided
provided
provided
-


runtime
runtime
-
runtime
-


test
test
-
test
-


 
 
 
 
 

  如果project-a包含一个对于project-b的测试范围依赖,后者包含一个对于project-c的编译范围依赖。project-c将会是project-a的测试范围传递性依赖。
  排除一个传递性依赖
 

<dependency> <groupId>org.sonatype.mavenbook</groupId> <artifactId>project-a</artifactId> <version>1.0</version> <exclusions> <exclusion> <groupId>org.sonatype.mavenbook</groupId> <artifactId>project-b</artifactId> </exclusion> </exclusions> </dependency>
 
  排除并替换一个传递性依赖

<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.5.ga</version>
<exclusions>
<exclusion>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jta_1.1_spec</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
  当Maven使用一种“最近者胜出”方式解决依赖的时候,它会用到依赖的深度。当使用依赖归类技术(pom类型依赖)的时候,会把依赖推入整个树的更深一层。当在选择用pom归类依赖或者用父POM的dependenctManagement的时候,需要留意这一点。

二 Maven 生命周期



生命周期(lifecyle)
阶段(phrase)


清理(clean)
  pre-clean
  clean
  post-clean




默认(default)(有时候也称为构建)



validate
验证项目是否正确,以及所有为了完整构建必要的信息是否可用


generate-sources
生成所有需要包含在编译过程中的源代码


process-sources
处理源代码,比如过滤一些值


generate-resources
生成所有需要包含在打包过程中的资源文件


process-resources
复制并处理资源文件至目标目录,准备打包


compile
编译项目的源代码


process-classes
后处理编译生成的文件,例如对Java类进行字节码增强(bytecode enhancement)


generate-test-sources
生成所有包含在测试编译过程中的测试源码


process-test-sources
处理测试源码,比如过滤一些值


generate-test-resources
生成测试需要的资源文件


process-test-resources
复制并处理测试资源文件至测试目标目录


test-compile 
编译测试源码至测试目标目录


test
使用合适的单元测试框架运行测试。这些测试应该不需要代码被打包或发布


prepare-package
在真正的打包之前,执行一些准备打包必要的操作。这通常会产生一个包的展开的处理过的版本(将会在Maven 2.1+中实现)


package
将编译好的代码打包成可分发的格式,如JAR,WAR,或者EAR


pre-integration-test
执行一些在集成测试运行之前需要的动作。如建立集成测试需要的环境


integration-test
如果有必要的话,处理包并发布至集成测试可以运行的环境


post-integration-test
执行一些在集成测试运行之后需要的动作。如清理集成测试环境。


verify
执行所有检查,验证包是有效的,符合质量规范


install
安装包至本地仓库,以备本地的其它项目作为依赖使用


deploy 
复制最终的包至远程仓库,共享给其它开发人员和项目(通常和一次正式的发布相关)





站点(site)
  pre-site
  site
  post-site
  site-deploy
  默认绑定到站点生命周期的目标是:
  1. site - site:site
  2. site-deploy -site:deploy
  通过运行如下命令从一个Maven项目生成一个站点:
  mvn site




三 Maven 常用命令
  maven命令都是由插件来实现的,常用的插件和命令见http://maven.apache.org/plugins/index.html
  罗列下常用的命令:
  mvn help:system 打印出所有的Java系统属性和环境变量
  mvn help:describe -Dplugin=xxxgroupId:xxxartifactId -Dfull
  mvn help:describe -Dplugin=exec -Dfull
  help:effective-pom 打印项目的有效POM 有效POM是指合并了所有父POM(包括Super POM)后的XML,当你不确定POM的某些信息从何而来时,就可以查看有效POM
  help:effective-settings 打印项目的有效settings
  mvn help:active-profiles
  mvn help:describe -Dcmd=package
  mvn help:describe -Dcmd=jar:jar
  mvn help:all-profiles 和mvn help:active-profiles 帮助查看项目的Profile
  mvn archetype:generate maven3创建maven项目
  maven2创建普通java项目,最好用稳定版本
  mvn org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-5:generate
  mvn archetype:create -DgroupId=xxx \
  -DartifactId=xxx \
  -DpackageName=xxx \
  -Dversion=1.0
  创建Maven的Web项目:
  mvn archetype:create -DgroupId=packageName -DartifactId=webappName -DarchetypeArtifactId=maven-archetype-webapp
  mvn clean compile 编译 maven3.0之前compile:compile使用javac编译器,从3.0后使用javax.tool.JavaCompiler
  mvn test 运行到 test 阶段为止的所有生命周期阶段
  mvn test -Dmaven.test.failure.ignore=true 忽略测试失败
  mvn install -Dmaven.test.skip=true 跳过单元测试
  mvn clean package 打包
  mvn clean install 打包并上传到maven本地库
  mvn site生成站点信息,在target/site目录下index.html打开可浏览
  直接执行main函数命令
  mvn install
  mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather.Main exec插件不用添加依赖lib包到class路径,它会自动获取依赖包
  mvn jetty:run 使用jetty插件启动项目
  mvn dependency:resolve 打印出已解决依赖的列表
  mvn dependency:tree 项目的整个依赖树
  mvn dependency:analyse帮助你发现对于依赖的直接引用想要查看完整的依赖踪迹,包含那些因为冲突或者其它原因而被拒绝引入的构件,如果你有直接使用到的却未声明的依赖,该目标就会发出警告
  打开 Maven 的调试标记运行:
  mvn install -X
  mvn跟开源测试覆盖率统计工具 合成,比如 
  mvn cobertura:cobertura 
  之后在target/site/cobertura下看到index.html文件
  将依赖的文件安装到本地库
  mvn install:install-file 
  -Dfile=<path-to-file> 
  -DgroupId=<group-id> 
  -DartifactId=<artifact-id> 
  -Dversion=<version> 
  -Dpackaging=<packaging> 
  -DgeneratePom=true

四 Maven 插件介绍
  一个Maven插件是一个单个或者多个目标的集合。Maven插件的例子有一些简单但核心的插件,像Jar插件,它包含了一组创建JAR文件的目标,Compiler插件,它包含了一组编译源代码和测试代码的目标,或者Surefire插件,它包含一组运行单元测试和生成测试报告的目标。而其它的,更有专门的插件包括:Hibernate3插件,用来集成流行的持久化框架Hibernate,JRuby插件,它让你能够让运行ruby称为Maven构建的一部分或者用
  Ruby来编写Maven插件。Maven也提供了自定义插件的能力。一个定制的插件可以用Java编写,或者用一些其它的语言如Ant,Groovy,beanshell和Ruby.
  执行命令mvn help:describe -Dplugin=war -Dfull >> a.log
  文件a.log中内容如下:

Name: Maven WAR Plugin
Description: Builds a Web Application Archive (WAR) file from the project
output and its dependencies.
Group Id: org.apache.maven.plugins
Artifact Id: maven-war-plugin
Version: 2.1.1
Goal Prefix: war
This plugin has 5 goals:
war:exploded
Description: Create an exploded webapp in a specified directory.
Implementation: org.apache.maven.plugin.war.WarExplodedMojo
Language: java
Bound to phase: package
Available parameters:
archive
The archive configuration to use. See Maven Archiver Reference.
archiveClasses (Default: false)
User property: archiveClasses
Whether a JAR file will be created for the classes in the webapp. Using
this optional configuration parameter will make the compiled classes to
be archived into a JAR file and the classes directory will then be
excluded from the webapp.
cacheFile (Default: ${project.build.directory}/war/work/webapp-cache.xml)
Required: true
The file containing the webapp structure cache.
containerConfigXML
User property: maven.war.containerConfigXML
The path to a configuration file for the servlet container. Note that the
file name may be different for different servlet containers. Apache
Tomcat uses a configuration file named context.xml. The file will be
copied to the META-INF directory.
dependentWarExcludes
The comma separated list of tokens to exclude when doing a WAR overlay.
Deprecated. Use &lt;overlay&gt;/&lt;excludes&gt; instead
dependentWarIncludes
The comma separated list of tokens to include when doing a WAR overlay.
Default is '**'
Deprecated. Use &lt;overlay&gt;/&lt;includes&gt; instead
......
......
  文件中描述了插件的坐标,目标(goals),目标参数,目标执行的mojo类,目标默认绑定阶段
  打开其中war目标的mojo类org.apache.maven.plugin.war.WarMojo(在仓库的目录jar中,根据插件的坐标,可以找到jar位置在%本地仓库%/org/apache/maven/plugins/maven-war-plugin)

war:war
Description: Build a WAR file.
Implementation: org.apache.maven.plugin.war.WarMojo
Language: java
Bound to phase: package

package org.apache.maven.plugin.war;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugin.war.util.ClassesPackager;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.jar.ManifestException;
import org.codehaus.plexus.archiver.war.WarArchiver;
import org.codehaus.plexus.util.StringUtils;
public class WarMojo extends AbstractWarMojo
{
private String outputDirectory;
private String warName;
private String classifier;
private String packagingExcludes;
private String packagingIncludes;
private WarArchiver warArchiver;
private MavenProjectHelper projectHelper;
private boolean primaryArtifact;
private boolean failOnMissingWebXml;
private boolean attachClasses;
private String classesClassifier;
public WarMojo()
{
this.primaryArtifact = true;
this.failOnMissingWebXml = true;
this.attachClasses = false;
this.classesClassifier = "classes";
}
public void execute()
throws MojoExecutionException, MojoFailureException
{
File warFile = getTargetWarFile();
try
{
performPackaging(warFile);
}
catch (DependencyResolutionRequiredException e)
{
throw new MojoExecutionException("Error assembling WAR: " + e.getMessage(), e);
}
catch (ManifestException e)
{
throw new MojoExecutionException("Error assembling WAR", e);
}
catch (IOException e)
{
throw new MojoExecutionException("Error assembling WAR", e);
}
catch (ArchiverException e)
{
throw new MojoExecutionException("Error assembling WAR: " + e.getMessage(), e);
}
}
private void performPackaging(File warFile)
throws IOException, ArchiverException, ManifestException, DependencyResolutionRequiredException, MojoExecutionException, MojoFailureException
{
getLog().info("Packaging webapp");
buildExplodedWebapp(getWebappDirectory());
MavenArchiver archiver = new MavenArchiver();
archiver.setArchiver(this.warArchiver);
archiver.setOutputFile(warFile);
getLog().debug("Excluding " + Arrays.asList(getPackagingExcludes()) + " from the generated webapp archive.");
getLog().debug("Including " + Arrays.asList(getPackagingIncludes()) + " in the generated webapp archive.");
this.warArchiver.addDirectory(getWebappDirectory(), getPackagingIncludes(), getPackagingExcludes());
File webXmlFile = new File(getWebappDirectory(), "WEB-INF/web.xml");
if (webXmlFile.exists())
{
this.warArchiver.setWebxml(webXmlFile);
}
if (!(this.failOnMissingWebXml))
{
getLog().debug("Build won't fail if web.xml file is missing.");
this.warArchiver.setIgnoreWebxml(false);
}
archiver.createArchive(getProject(), getArchive());
if (isAttachClasses())
{
ClassesPackager packager = new ClassesPackager();
File classesDirectory = packager.getClassesDirectory(getWebappDirectory());
if (classesDirectory.exists())
{
getLog().info("Packaging classes");
packager.packageClasses(classesDirectory, getTargetClassesFile(), getJarArchiver(), getProject(), getArchive());
this.projectHelper.attachArtifact(getProject(), "jar", getClassesClassifier(), getTargetClassesFile());
}
}
String classifier = this.classifier;
if (classifier != null)
{
this.projectHelper.attachArtifact(getProject(), "war", classifier, warFile);
}
else
{
Artifact artifact = getProject().getArtifact();
if (this.primaryArtifact)
{
artifact.setFile(warFile);
}
else if ((artifact.getFile() == null) || (artifact.getFile().isDirectory()))
{
artifact.setFile(warFile);
}
}
}
protected static File getTargetFile(File basedir, String finalName, String classifier, String type)
{
if (classifier == null)
{
classifier = "";
}
else if ((classifier.trim().length() > 0) && (!(classifier.startsWith("-"))))
{
classifier = "-" + classifier;
}
return new File(basedir, finalName + classifier + "." + type);
}
protected File getTargetWarFile()
{
return getTargetFile(new File(getOutputDirectory()), getWarName(), getClassifier(), "war");
}
protected File getTargetClassesFile()
{
return getTargetFile(new File(getOutputDirectory()), getWarName(), getClassesClassifier(), "jar");
}
public String getClassifier()
{
return this.classifier;
}
public void setClassifier(String classifier)
{
this.classifier = classifier;
}
public String[] getPackagingExcludes()
{
if (StringUtils.isEmpty(this.packagingExcludes))
{
return new String;
}
return StringUtils.split(this.packagingExcludes, ",");
}
public void setPackagingExcludes(String packagingExcludes)
{
this.packagingExcludes = packagingExcludes;
}
public String[] getPackagingIncludes()
{
if (StringUtils.isEmpty(this.packagingIncludes))
{
return { "**" };
}
return StringUtils.split(this.packagingIncludes, ",");
}
public void setPackagingIncludes(String packagingIncludes)
{
this.packagingIncludes = packagingIncludes;
}
public String getOutputDirectory()
{
return this.outputDirectory;
}
public void setOutputDirectory(String outputDirectory)
{
this.outputDirectory = outputDirectory;
}
public String getWarName()
{
return this.warName;
}
public void setWarName(String warName)
{
this.warName = warName;
}
public WarArchiver getWarArchiver()
{
return this.warArchiver;
}
public void setWarArchiver(WarArchiver warArchiver)
{
this.warArchiver = warArchiver;
}
public MavenProjectHelper getProjectHelper()
{
return this.projectHelper;
}
public void setProjectHelper(MavenProjectHelper projectHelper)
{
this.projectHelper = projectHelper;
}
public boolean isPrimaryArtifact()
{
return this.primaryArtifact;
}
public void setPrimaryArtifact(boolean primaryArtifact)
{
this.primaryArtifact = primaryArtifact;
}
public boolean isAttachClasses()
{
return this.attachClasses;
}
public void setAttachClasses(boolean attachClasses)
{
this.attachClasses = attachClasses;
}
public String getClassesClassifier()
{
return this.classesClassifier;
}
public void setClassesClassifier(String classesClassifier)
{
this.classesClassifier = classesClassifier;
}
public boolean isFailOnMissingWebXml()
{
return this.failOnMissingWebXml;
}
public void setFailOnMissingWebXml(boolean failOnMissingWebXml)
{
this.failOnMissingWebXml = failOnMissingWebXml;
}
}
  上面为反编译器显示的jar,注解信息有丢失,war插件的war目标被执行时,WarMojo的execute()方法会被执行,WarMojo类的属性通过注解接收maven传递过来的参数或者默认参数(如果没有传参)。有些Mojo有默认绑定的阶段,当不进行任何设置时,该阶段就会执行这个目标。如compile插件的compile目标默认绑定在default生命周期的compile阶段。
  default生命周期jar打包方式绑定的插件:



generate-resources
plugin:descriptor


process-resources
resources:resources


compile
compile:compile


process-test-resources
resources:testResources


test-compile
compiler:testCompile


test
surefire:test


package
jar:jar


install
install:install


deploy
deploy:deploy

  default生命周期pom打包方式绑定的插件:



package
site:attach-descriptor


install
install:install


deploy
deploy:deploy




  通过编写mojo可以自定义插件,详情见下篇。
页: [1]
查看完整版本: Apache maven介绍