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[0];
    }

    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可以自定义插件,详情见下篇。

猜你喜欢

转载自xxh02.iteye.com/blog/2165183