Maven 编译及打包

[TOC]

Maven 编译及打包

预备知识点

Build Lifecycle、Phase与Goal

  • Build Lifecycle即Maven中项目管理的生命周期,Maven内置了三种生命周期:default,clean,site
  • Phase即生命周期中的各个阶段;以default为例,其生命周期分为validate,compile,test,package,verify,install,deploy等几个阶段,如package即表示生命周期中的打包阶段。
  • Goal即各个Phase所定义的执行步骤;这些具体的执行步骤通常由Maven插件提供,使用时的命令格式通常为plugin:goal

综上所述,一个完整的生命周期Build Lifecycle由一个或多个阶段Phase组成,一个阶段Phase由零个或多个步骤Goal组成;同一个步骤Goal可以从属于零个或多个阶段Phase;一个插件可以同时提供多个Goal,可以将其理解为一个插件根据不同参数以提供不同的能力。

BaseBuild、ProjectBuild、ProfileBuild

  • ProjectBuild即为POM文件中的build节点,对应Maven项目的构建配置管理
  • ProfileBuild即针对不同环境设置的项目构建配置管
  • BaseBuild即build节点与profiles/profile/build节点的一系列共有属性

配置说明

BaseBuild即POM文件中build节点与profiles/profile/build节点的一系列共有属性

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  ...
  <!-- "Project Build" contains more elements than just the BaseBuild set -->
  <build>...</build>
 
  <profiles>
    <profile>
      <!-- "Profile Build" contains a subset of "Project Build"s elements -->
      <build>...</build>
    </profile>
  </profiles>
</project>

基础属性

<build>
  <defaultGoal>install</defaultGoal>
  <directory>${basedir}/target</directory>
  <finalName>${artifactId}-${version}</finalName>
  <filters>
    <filter>filters/filter1.properties</filter>
  </filters>
  ...
</build>
  • defaultGoal,默认构建目标;这里的目标可以是goal(构建指令如jar),也可以是phase(生命周期阶段如install)。
  • directory,输出文件目录;默认为${basedir}/target,若使用相对路径,则当前目录为POM文件所在目录。
  • finalName,输出文件名;注意此处并不代表最终的完整的文件名,其也依赖于插件的执行行为;
  • filters,环境变量列表;其指定的properties文件按行定义环境变量,如name=value,对应的环境变量只能在build/resources节点中使用;在Maven中默认的filter文件路径为${basedir}/src/main/filters/

资源文件

<build>
    ...
    <resources>
      <resource>
        <targetPath>${project.build.outputDirectory}/conf</targetPath>
        <filtering>false</filtering>
        <directory>${basedir}/src/main/resources/conf</directory>
        <includes>
          <include>configuration.xml</include>
        </includes>
        <excludes>
          <exclude>**/*.properties</exclude>
        </excludes>
      </resource>
    </resources>
    <testResources>
      ...
    </testResources>
    ...
</build>
  • targetPath,当前资源文件最终输出目录。
  • filtering,表示当前资源文件配置节点下,是否启用build/filters下的环境变量。
  • directory,资源文件目录,可使用相对路径。
  • includes,在当前directory下需要包含的文件列表。
  • excludes,在当前directory下需要排除的文件列表。
  • testResources,配置项同resources,只不过是用于生命周期中的test阶段。

插件管理

<build>
    ...
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.6</version>
        <extensions>false</extensions>
        <inherited>true</inherited>
        <configuration>
          <items combine.children="append">
            <!-- combine.children="merge" is the default -->
            <item>child-1</item>
          </items>
          <properties combine.self="override">
            <!-- combine.self="merge" is the default -->
            <childKey>child</childKey>
          </properties>
        </configuration>
        <dependencies>...</dependencies>
        <executions>
          <execution>
            <id>echodir</id>
            <goals>
              <goal>run</goal>
            </goals>
            <phase>verify</phase>
            <inherited>false</inherited>
            <configuration>
              <tasks>
                <echo>Build Dir: ${project.build.directory}</echo>
              </tasks>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
    
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <version>2.6</version>
          <executions>
            <execution>
              <id>pre-process-classes</id>
              <phase>compile</phase>
              <goals>
                <goal>jar</goal>
              </goals>
              <configuration>
                <classifier>pre-process</classifier>
              </configuration>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </pluginManagement>
    ...
</build>
  • groupId,artifactId,version,插件坐标项
  • extensions
  • inherited,作为父级POM文件是否向下传递该插件配置
  • configuration,插件自身的配置属性,依插件的不同而不同

configuration的继承规则有三种:merge,append,override,默认为merge

继承规则可以针对每项configuration进行独立设置

configuration的merge继承规则为:异项合并,同项覆盖

  • dependencies,插件的依赖包,可以针对一些特殊情况进行针对性的配置,如依赖包版本或者exclusions
  • executions,针对不同goal进行的独立配置集

Maven-Resources-Plugin

该插件支持三个goal命令:resources:resources、resources:testResources、resources:copy-resources

分别用于复制main\resources、test\resources以及自定义资源目录

Maven-Jar-Plugin

该插件主要用于生命周期的package阶段,其配置项基于Apache Maven Archiver,具体可参考资源链接。

<archive>
  <addMavenDescriptor/>
  <compress/>
  <forced/>
  <index/>
  <manifest>
    <addClasspath/>
    <addDefaultEntries/>
    <addDefaultImplementationEntries/>
    <addDefaultSpecificationEntries/>
    <addBuildEnvironmentEntries/>
    <addExtensions/>
    <classpathLayoutType/>
    <classpathPrefix/>
    <customClasspathLayout/>
    <mainClass/>
    <packageName/>
    <useUniqueVersions/>
  </manifest>
  <manifestEntries>
    <key>value</key>
  </manifestEntries>
  <manifestFile/>
  <manifestSections>
    <manifestSection>
      <name/>
      <manifestEntries>
        <key>value</key>
      </manifestEntries>
    <manifestSection/>
  </manifestSections>
  <pomPropertiesFile/>
</archive>
  • mainfest\addClasspath,是否添加classpath路径,默认false
  • mainfest\classpathPrefix,在所有的classpath中添加指定前缀
  • mainfest\mainClass,程序入口即main函数所在类
  • manifestEntries,需要添加在classpath中的值,否则会提示引用异常或查找不到配置文件

Maven Assembly Plugin

Assembly Plugin用于将编译后的包进行打包组装,便于发行使用,其配置项主要基于assembly descriptor,具体可参考资源链接。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.2-beta-5</version>
    <!-- The configuration of the plugin -->
    <configuration>
        <finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName>
        <!-- Specifies the configuration file of the assembly plugin -->
        <descriptors>
            <descriptor>src/main/resources/package.xml</descriptor>
        </descriptors>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<assembly>
    <id>bin</id>
    <formats>
        <format>zip</format>
    </formats>
    <dependencySets>
        <dependencySet>
            <!--
               不使用项目的artifact,第三方jar不要解压,打包进zip文件的lib目录
           -->
            <outputDirectory>lib</outputDirectory>
            <unpack>false</unpack>
        </dependencySet>
    </dependencySets>
    <fileSets>
        <!-- 把项目相关的说明文件,打包进zip文件的根目录 -->
        <fileSet>
            <directory>${project.basedir}</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>README*</include>
                <include>LICENSE*</include>
                <include>NOTICE*</include>
            </includes>
        </fileSet>

        <!-- 把项目的配置文件,打包进zip文件的config目录 -->
        <fileSet>
            <directory>src/main/resources/conf</directory>
            <outputDirectory>conf</outputDirectory>
            <filtered>true</filtered>
            <includes>
                <include>server.properties</include>
                <include>server-router.json</include>
                <include>log4j.properties</include>
            </includes>
        </fileSet>

        <!-- 把项目的脚本文件目录( src/main/scripts )中的启动脚本文件,打包进zip文件的跟目录 -->
        <fileSet>
            <directory>${project.build.scriptSourceDirectory}</directory>
            <outputDirectory></outputDirectory>
            <includes>
                <include>startup.*</include>
            </includes>
        </fileSet>

        <!-- 把项目自己编译出来的jar文件,打包进zip文件的根目录 -->
        <fileSet>
            <directory>${project.build.directory}</directory>
            <outputDirectory></outputDirectory>
            <includes>
                <include>*.jar</include>
            </includes>
        </fileSet>
    </fileSets>
</assembly>
  • formats
"zip" - Creates a ZIP file format
"tar" - Creates a TAR format
"tar.gz" or "tgz" - Creates a gzip'd TAR format
"tar.bz2" or "tbz2" - Creates a bzip'd TAR format
"tar.snappy" - Creates a snappy'd TAR format
"tar.xz" or "txz" - Creates a xz'd TAR format
"jar" - Creates a JAR format
"dir" - Creates an exploded directory format
"war" - Creates a WAR format
  • dependencySets\dependencySet\outputDirectory
  • dependencySets\dependencySet\unpack,仅支持解压 jar, zip, tar.gz, tar.bz
  • fileSets\fileSet\directory,设置当前配置对应搜索目录
  • fileSets\fileSet\outputDirectory,设置当前配置对应输出目录
  • fileSets\fileSet\includes,需要包含的文件,支持通配符
  • fileSets\fileSet\excludes,需要排除的文件,支持通配符,同匹配条件excludes具有更高的优先级

路径配置

<build>
    ...
    <sourceDirectory>${basedir}/src/main/java</sourceDirectory>
    <scriptSourceDirectory>${basedir}/src/main/scripts</scriptSourceDirectory>
    <testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>
    <outputDirectory>${basedir}/target/classes</outputDirectory>
    <testOutputDirectory>${basedir}/target/test-classes</testOutputDirectory>
    ...
</build>

路径设置仅从属于Project Build节点,在ProfileBuild节点中无法使用

扩展配置

<extensions>
    <extension>
        <groupId>org.apache.maven.wagon</groupId>
        <artifactId>wagon-ftp</artifactId>
        <version>1.0-alpha-3</version>
    </extension>
</extensions>

扩展配置指定的依赖包将在build过程中被加载

参考资源

Maven项目管理生命周期

Maven内置生命周期

Packaging对应的default生命周期插件列表

Maven插件开发手册

Maven插件列表

Maven-Resources-Plugin

Maven-Jar-Plugin

Maven-Jar-Plugin Configuration

常见Q&A

分析以下Maven命令的执行生命周期

mvn clean dependency:copy-dependencies package

先执行生命周期clean,再执行gola,dependency插件的copy-dependencies命令,最后执行phase package


解释在如下配置中二个complie分别代表什么含义

<build>
        <defaultGoal>compile</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.7</version>
                <configuration>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
                <executions>
                    <execution>
                        <phase>compile</phase>
                    </execution>
                </executions>
            </plugin>
        </plugins>
<build>

defaultGoal表示默认的maven build行为compile
execution表示插件在对应生命周期阶段的配置


判断在下方示例POM文件中,最终文件是否会被输出至targetPath

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <build>
    ...
    <resources>
      <resource>
        <targetPath>META-INF/plexus</targetPath>
        <filtering>false</filtering>
        <directory>${basedir}/src/main/plexus</directory>
        <includes>
          <include>*.*</include>
        </includes>
        <excludes>
          <exclude>*.*</exclude>
        </excludes>
      </resource>
    </resources>
    <testResources>
      ...
    </testResources>
    ...
  </build>
</project>

excludes优先级高于includes,故不会


Build节点中executions配置项的设计理念及作用

extensions可以用于提供插件的具体实现,而插件只需要提供统一接口


build/plugin节点与build/pluginManagement节点的异同

类似dependencies/dependencyManagement的关系,前者用于自身且后代直接继承,后者仅用于预声明后代可能会使用到的依赖项,后代使用时必须至少指明对应的groupId与artifactId


debug时无法找到对应的资源文件

首先需要了解java可直接执行编译后的class文件,如

java hello_world

需要注意的是,如果hello_world.java的类文件声明了package com.group.artifact,则执行命令时需要在com目录下并对应指定完整类名:java com.group.artifact.hello_world.class

当源文件引用了其他依赖库时可使用-classpath参数配置依赖项的路径,否则会提示classNotFoundException

java -classpath "/Users/joker/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar:/Users/joker/.m2/repository/io/netty/netty-all/4.1.6.Final/netty-all-4.1.6.Final.jar" hello_world

当idea执行debug时,实际是以target/classes下的编译后class文件为目标进行执行,那么默认条件下其可自动搜寻到target/classes根目录下的文件;若资源文件的最终结构包含多层,则需要手动指定对应的classpath目录:

image

操作路径:idea project structure -> modules -> dependencies -> add -> jar or libary -> choose directory -> classes

选择对应的生命周期为compile后,再次执行debug时将可直接在console中看到对应的java命令-classpath参数包含了对应的目录


公共包发布后无法download resource

原因:执行发布deploy时未生成对应的javadoc及源码,可在公共包项目的pom文件中进行插件配置。

<build>
    <plugins>
        <!--配置生成Javadoc包-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-javadoc-plugin</artifactId>
            <configuration>
                <encoding>UTF-8</encoding>
                <aggregate>true</aggregate>
                <charset>UTF-8</charset>
                <docencoding>UTF-8</docencoding>
            </configuration>
            <executions>
                <execution>
                    <id>attach-javadocs</id>
                    <goals>
                        <goal>jar</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <!--配置生成源码包-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-source-plugin</artifactId>
            <executions>
                <execution>
                    <id>attach-sources</id>
                    <goals>
                        <goal>jar</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
    <pluginManagement>
        <plugins>
            <plugin>
                <artifactId>maven-source-plugin</artifactId>
                <version>2.2.1</version>
            </plugin>
            <plugin>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>2.10.1</version>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容