Java Maven开发完整配置模板详细分析

2021年3月8日16:33:56 发表评论 976 次浏览

本文概述

前言

Maven开发完整配置

像之前分析Gradle构建配置一样,本文的目的旨在分析Gradle配置,并得到一个普遍的基本配置模板。构建工具几乎是所有项目开发都要用到的,而构建工具一般都带有一个配置文件,构建工具的最基本任务包括:

  • 编译源文件文件。
  • 处理资源文件
  • 打包文件(字节码文件和资源文件)。

额外的任务包括:

  • 清理构建生成文件。
  • 项目依赖管理
  • 运行单元测试。
  • 安装项目。
  • 部署项目。

所以,构建工具的输入为一堆项目文件,输出为可运行文件及其相关资源文件。对于Maven也是一样的,Maven的任务由各种插件完成(Maven的插件由多个目标组成),而Gradle的任务由各种Task完成,对比来看,Gradle会比Maven配置更为自由一些。

Maven安装或更新

Maven的安装非常简单,简要流程如下:

  • 从官网 HYPERLINK "https://maven.apache.org/download.html" https://maven.apache.org/download.html下载相关的安装包。
  • 解压安装包到指定文件夹。
  • 将Maven安装目录的bin目录配置到环境变量中。
  • 在命令行中输入mvn -v查看maven版本信息,正常显示则表示安装成功。

Maven的安装也类似以上流程,但是这里有两个问题:仓库配置和镜像配置,首先进入Maven安装目录的conf目录中,打开settings.xml:

1、配置本地仓库:搜索localRepository,在下面添加配置:

<localRepository>E:\maven-repository</localRepository>

2、配置镜像地址:搜索mirrors,在其中添加镜像地址配置:

<mirror>
      <id>alimaven</id>
      <name>aliyun maven</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>central</mirrorOf>        
    </mirror>

如果是更新Maven,在使用新版Maven时,也要注意新版Maven是否跟随了旧版本的相关配置。

Maven的生命周期、插件、目标和阶段

如果我们要执行指定的构建任务,需要使用到Maven插件(plugin),Maven的插件由一系列的目标(goal)组成。在Maven中有构建生命周期的概念,构建的每个单元又称为阶段(phase),阶段实际上是映射到底层目标的(一个阶段可能对应有一个或多个目标)。执行某个构建阶段,可能会预先执行一些依赖的阶段(类似Gradle中的任务依赖),但是仅执行一个目标不会存在依赖的问。例如执行mvn compile,在执行compile编译阶段之前,会执行多个阶段,所有执行的阶段如下:

  • validate
  • generate-sources
  • process-sources
  • generate-resources
  • process-resources
  • compile

但是如果执行mvn jar:jar目标,则只会执行该目标,其它的都不会执行。

执行maven的命令一般形式为:mvn 阶段/目标,例如一个常见的清理阶段是mvn clean。

虽然maven官方说一个插件由不同的目标组成,但是似乎将插件看做一个任务集合更形象(配合Gradle),所以如果要完成某些特殊的任务,一个方法是使用第三方插件,或者自己开发插件,或者简单的任务由maven自带插件完成。

使用基于maven-archetype-quickstart模板生成的Maven项目一般带有一些默认的构建生命周期阶段,你可以打开在IDEA的右边面板,右键执行这些构建阶段。如果你展开“Plugins“,你可以看到Maven配置的所有插件,对每个插件逐个展开可以看到每个插件的不同构建目标(一个插件由多个目标组成),如下图:

IDEA查看Maven插件及其构建阶段

由上图你还可以看到一些Maven目标的特殊记法,例如compiler:testCompile,前面是插件名称,后面是插件中的目标。Maven中的阶段和目标可以并列执行,例如:

mvn clean dependency:copy-dependencies package

clean和package是maven的构建阶段,而copy-dependencies是dependency中的一个目标。

综上总结如下:

  • 一个构件生命周期lifecycle由不同的阶段phase构成,这些阶段有前后依赖的关系,执行一个阶段可能会预先执行一些依赖的阶段。
  • 一个构建插件中可能会有一个或多个目标goal,不同或相同的目标会被映射的到一个阶段中,不被映射的目标仍然可用被单独执行,执行任意一个目标不会影响其它目标。
  • 一个目标是一个单独的子功能,目标一般被映射到一个阶段中,阶段属于构建生命周期。

例如,下面的例子是将插件display-maven-plugin中的一个目标time映射到阶段process-test-resources中,这样当执行到process-test-resources阶段的时候,就会去执行time目标。

<plugin>
   <groupId>com.mycompany.example</groupId>
   <artifactId>display-maven-plugin</artifactId>
   <version>1.0</version>
   <executions>
     <execution>
       <phase>process-test-resources</phase>
       <goals>
         <goal>time</goal>
       </goals>
     </execution>
   </executions>
 </plugin>

Maven中的Archetype

在IDEA中创建一个Maven项目一般都可以选择一个Archetype,Archetype是原型的意思,Maven中的Archetype是一个项目模板工具包,用于生成特定配置的Maven项目。

例如maven-archetype-quickstart可以生成一个普通的Java Maven项目,maven-archetype-webapp用于生成一个Maven Web项目。

Archetype由maven的archetype插件支持,而archetype:generate只是其中的一个目标,用于生成指定格式的Java项目。

由此可见,Maven中的大部分工作都依赖于插件完成。

Maven资源过滤

Maven的资源过滤功能用于访问maven项目中定义的变量或属性,根据属性的来源分类,其使用方式分别如下:

  • 来自POM:pom.xml是一个XML文件,maven将每个xml元素作为一个对象来处理。例如pom.xml的根元素是<project>,所以我们可以把project作为一个对象来使用,而${project.name}表示访问project元素中的name元素的值。
  • 来自maven的配置文件settings.xml,该文件也是一个XML文件,使用方式类似pom.xml,例如${settings.localRepository}。
  • 来自自定义filter文件src/main/filters/filter.properties:在这里定义的变量都可以使用,但是需要在pom.xml中配置:在<build>下添加filters配置。
  • 来自pom.xml中<properties>定义的属性。
  • 在资源文件中访问以上属性需要在pom.xml中配置:在<build>下添加resources配置,并将filtering设置为true。
  • Java系统属性:Java系统属性或Java命令行-D参数指定的属性。

Maven多项目配置

假设项目app有两个模块:my-app和my-webapp,多项目配置如下:

首先在父项目app中添加子模块配置,module中的值为项目文件目录(有可能需要使用相对路径):

<modules>
    <module>my-app</module>
    <module>my-webapp</module>
  </modules>

接着在每个子模块中添加父项目配置,有可能需要使用relativePath指定pom.xml文件:

<parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>app</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

子模块之间可以互相依赖,例如my-webapp依赖my-app的依赖配置如下:

...
  <dependencies>
    <dependency>
      <groupId>com.mycompany.app</groupId>
      <artifactId>my-app</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    ...
  </dependencies>

Maven多项目继承和聚合配置方式如下:

  • 在每个子POM中指定它们的父POM。
  • 将父POM的打包类型设置为“pom”。
  • 在父POM中指定其模块(子POM)的目录。

Maven单项目完整配置

下面是Maven单项目的一些基本配置,不算完整,但可以借用,或在此基础上添加,配置来源于github的配置:

<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 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example.maven-samples</groupId>
  <artifactId>single-module-project</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>A Single Maven Module</name>
  <description>Sample single module Maven project with a working, deployable site.</description>
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
  </properties>

  <distributionManagement>
    <site>
      <id>site-server</id>
      <name>Test Project Site</name>
      <url>file:///tmp/single-module-site</url>
    </site>
  </distributionManagement>

  <build>
    <finalName>${project.artifactId}</finalName>

    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>

      <plugin>
        <artifactId>maven-release-plugin</artifactId>
        <version>2.2.1</version>
      </plugin>

      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.5</version>
      </plugin>

      <plugin>
        <artifactId>maven-site-plugin</artifactId>
        <version>3.0</version>
        <configuration>
          <reportPlugins>
            <plugin>
              <artifactId>maven-checkstyle-plugin</artifactId>
              <version>2.8</version>
            </plugin>

            <plugin>
              <artifactId>maven-javadoc-plugin</artifactId>
              <version>2.8</version>
            </plugin>

            <plugin>
              <artifactId>maven-jxr-plugin</artifactId>
              <version>2.3</version>
            </plugin>

            <plugin>
              <artifactId>maven-pmd-plugin</artifactId>
              <version>2.6</version>
            </plugin>

            <plugin>
              <artifactId>maven-project-info-reports-plugin</artifactId>
              <version>2.4</version>
            </plugin>

            <plugin>
              <artifactId>maven-surefire-report-plugin</artifactId>
              <version>2.11</version>
            </plugin>

            <plugin>
              <groupId>org.codehaus.mojo</groupId>
              <artifactId>cobertura-maven-plugin</artifactId>
              <version>2.5.1</version>
            </plugin>

            <plugin>
              <groupId>org.codehaus.mojo</groupId>
              <artifactId>findbugs-maven-plugin</artifactId>
              <version>2.3.3</version>
            </plugin>

            <plugin>
              <groupId>org.codehaus.mojo</groupId>
              <artifactId>taglist-maven-plugin</artifactId>
              <version>2.4</version>
            </plugin>
          </reportPlugins>
        </configuration>
      </plugin>

      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.11</version>
      </plugin>

      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>8.0.0.M1</version>
      </plugin>
    </plugins>
  </build>

  <dependencies>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
    </dependency>

    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.2</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit-dep</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-core</artifactId>
      <version>1.2.1</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-library</artifactId>
      <version>1.2.1</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>1.8.5</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <scm>
    <connection>scm:git:git@github.com:gabrielf/maven-samples.git</connection>
    <developerConnection>scm:git:git@github.com:gabrielf/maven-samples.git</developerConnection>
    <tag>HEAD</tag>
    <url>http://github.com/gabrielf/maven-samples</url>
  </scm>

  <prerequisites>
    <maven>3.0.3</maven>
  </prerequisites>

</project>

Maven JNI项目配置(Linux和Windows)

JNI项目的主要处理是:

  • 根据native方法生成头文件,然后编写C/C++代码。
  • 编译C/C++代码,这部分需要使用Linux和Windows对应的编译器,或结合相关的构建工具。
  • 编译代码生成动态库或静态库。
  • 库文件打包。

首先是项目结果,推荐的方式是在项目根目录创建clib,用于存放编译好的库文件。源码文件结果如下:

  -src
--main/c
--main/java

Java编译插件配置,用于确保编译第一步进行:

  <plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <executions>
    <execution>
      <phase>validate</phase>
      <goals>
        <goal>compile</goal>
      </goals>
    </execution>
  </executions>
</plugin>

头文件生成配置,要注意的是,这里的文件名和类名不是动态的,你需要手动指定:

  <plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>native-maven-plugin</artifactId>
  <executions>
    <execution>
      <id>javah</id>
      <phase>generate-resources</phase>
      <goals>
        <goal>javah</goal>
      </goals>
      <configuration>
        <!-- 头文件输出目录 ${basedir}为maven内置变量-->
        <javahOutputDirectory>${basedir}/src/main/c</javahOutputDirectory>
        <javahOutputFileName>learn_jni.h</javahOutputFileName>
        <!-- 需要编译成头文件的class,必须是全限定类名 -->
        <javahClassNames>
          <javahClassName>learn.jni.Sample01</javahClassName>
        </javahClassNames>
      </configuration>
    </execution>
  </executions>
</plugin>

对于编译JNI代码,你应该使用对应平台的编译器进行编译,因为我们的JNI程序是在特定平台上运行的。如果编译成静态库,Linux下是.so,Windows下是.dll。

打包库文件,其配置如下:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <executions>
      <execution>
        <id>copy-clib</id>
        <phase>compile</phase>
        <goals>
          <goal>copy-resources</goal>
        </goals>
        <configuration>
          <resources>
            <resource>
              <directory>clib</directory>
            </resource>
          </resources>
          <outputDirectory>${project.build.directory}/classes</outputDirectory>
        </configuration>
      </execution>
    </executions>
  </plugin>

对于最终项目打包,可以使用以下配置:

  <plugin>
  <artifactId>maven-assembly-plugin</artifactId>
  <version>3.1.0</version>
  <configuration>
    <descriptors>
      <descriptor>assembly.xml</descriptor>
    </descriptors>
  </configuration>
  <executions>
    <execution>
      <id>package</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
    </execution>
  </executions>
</plugin>

其中,我们需要在项目根目录创建assembly.xml文件,内容如下:

  <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>bin</id>
<formats>
<format>tar.gz</format>
<format>dir</format>
</formats>

<dependencySets>
<dependencySet>
    <outputDirectory>/lib</outputDirectory>
    <useProjectArtifact>false</useProjectArtifact>
</dependencySet>
</dependencySets>

<fileSets>
<fileSet>
    <directory>${project.build.directory}/classes</directory>
    <outputDirectory>/classes</outputDirectory>
</fileSet>
</fileSets>

<files>
<file>
    <source>bin/start.sh</source>
    <outputDirectory>/</outputDirectory>
    <fileMode>0755</fileMode>
</file>
</files>
</assembly>

总结

好了,先到这里了。整体来说,相对于Gradle,Maven的配置没有那么复杂,主要是理解Maven的一些核心概念,例如依赖管理、插件、目标和生命周期等,最重要的则是插件,因为很多东西都可以使用插件来完成,本身也不难。

本文的单项目和多项目配置会放到github上,Gradle的完整配置也放到github,也不算完整吧,就是可以适配多数项目,以后遇到特殊的配置再逐渐添加进去,这样可以免去配置的很多麻烦,节省开发时间。

木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: