Maven学习补充笔记

Maven可以称之为当前项目构建的事实上的标准了,基本上做的项目都是用Maven构建的。一直在使用Maven,但是没有较为全面地了解过,这篇博客主要记录之前使用过程中被忽略的部分,Maven的使用之前写过,传送门:Maven的学习

Maven配置与安装

Maven修改配置文件的最佳实践

Maven的配置文件,位于Maven安装目录/conf/settings.xml 之前都是直接在这个文件中修改Maven配置,实际上这里的配置文件是全局配置,对于当前操作系统所有的用户生效,整台机器上所有用户都会直接受到该配置的影响。最佳实践是我们将这个配置文件拷贝放在 ~/.m2/settings.xml,(~ 表示C盘/user/用户目录)这是用户范围的设置,可以在不同用户之间形成不同配置,而且互不影响,不推荐直接修改Maven安装目录下的文件,包括全局settings.xml

这样的配置还有一个好处就是,方便Maven升级,直接修改conf目录下的settings.xml,当升级Maven版本后还需要记得修改新的settings.xml,如果使用 ~/.m2目录下的settings.xml就不需要了

验证Maven是否安装成功

当解压Maven,配置环境变量,修改settings.xml之后推荐使用如下命令验证

mvn help:system

这个命令通过使用Maven的help插件输出当前系统的信息,如果没有help插件会要求联网从仓库中下载,所以推荐将这个命令作为安装Maven后的第一个命令用于验证。mvn -v命令可以验证Maven是否安装成功,环境变量是否安装成功,但是对于settings.xml的修改没法验证

修改Maven compiler编译的Java版本

通常Maven编译插件支持的版本较低,同时推荐显式指定Java编译版本,可以参看如下配置

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

命令行使用Archetype生成项目骨架

这里记录下项目骨架命令

mvn archetype:generate

接下来会要求输入项目骨架编号

接下来输入groupId,artifactId,version以及包名package即可

打包一个可运行的jar

默认打包生成jar是不能直接运行的,因为带有main方法的类信息不会添加到manifest中(打开jar文件中的META/MANIFEST.MF文件无法看到Main-Class),为了生成可执行的jar文件,需要借助maven-shade-plugin,配置如下

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>Main方法所在类的全路径</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

这样打出来的jar是带有Main-Class信息的可执行jar,使用java -jar即可运行

依赖

排除依赖

因为依赖传递的问题,可能因为某些原因传递过来的版本不稳定或者想要指定某个版本,可以使用<exclusions>标签排除某些传递的jar,这也是解决jar冲突的一种方案

 <dependency>
    <groupId>com.lynu</groupId>
    <artifactId>project-a</artifactId>
    <version>1.0.0</version>
    <exclusions>
        <exclusion>
            <groupId>com.lynu</groupId>
            <artifactId>project-b</artifactId>
        </exclusion>
    </exclusions>
</dependency>

这样在当前项目中使用project-a的时候,就把其所依赖的project-b给排除了

exclusions可以有多个exclusion元素排除多个依赖,声明exclusion元素只需要groupIdartifactId即可,因为只需要groupIdartifactId就可以唯一定位依赖

可选依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

dependency元素有一个optional属性,该属性可以指定这个依赖是可选依赖,可选依赖的jar只对当前项目可以使用,如果当前项目作为其他项目的依赖jar,则其他项目不能通过传递得到这个可选依赖,如果需要使用就要显式声明

仓库

配置远程仓库

Maven中仓库只分为本地仓库和远程仓库,远程仓库包括:中央仓库,私服以及其他公共仓库

本地仓库默认会在~/.m2/respository目录下,该目录一般位于c盘,如果需要自定义的话可以编辑settings.xml文件设置localRepository元素的值即可。而配置远程仓库,可以在项目的POM中配置

<repositories>
    <repository>
        <id>jboss</id>
        <name>JBoss Repository</name>
        <url>http://repository.jboss.com/maven2/</url>
        <releases>
            <!--是否下载releases发布版构件-->
            <enabled>true</enabled>
            <!--更新频率-->
            <updatePolicy>daily</updatePolicy>
            <!--检查校验文件的策略-->
            <checksumPolicy>ignore</checksumPolicy>
        </releases>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
        <!--仓库文件布局, default表示使用的是Maven2及Maven3的默认布局, 而不是Maven1的布局-->
        <layout>default</layout>
    </repository>
</repositories>

updatePolicy:配置Maven从远程仓库检查更新的频率。默认值daily-每天,其他可用值 never-从不检查更新 always-每次构建时检查更新 interval:X-每隔X分钟检查更新(X为整数)
checksumPolicy:配置Maven检查校验文件的策略。Maven在下载构建或者部署构建到仓库中时,会校验对应的文件,如果校验失败,就会采用配置的策略。默认值warn-校验失败输出警告信息,其他可选值 fail-校验失败停止构建 ignore-完全忽略校验和错误

将jar部署到远程仓库都在Maven的学习中有记录

远程仓库的认证

如果远程仓库需要用户名,密码任何认证,就需要配置认证信息了。认证信息与配置远程仓库信息不同,仓库信息可以直接配置到POM文件中,但是认证信息必须配置到settings.xml文件中。这是因为POM文件会被提交到SVN/GIT代码仓库中,而settings.xml在本机,出于安全性考虑

 <servers>
    <server>
      <id>my-repo</id>
      <username>repo-user</username>
      <password>repo-pwd</password>
    </server>
 </servers>

id必须与POM中远程仓库的id完全一致,正是这个id将认证信息与仓库配置联系在一起

一些仓库搜索服务

直接去中央仓库中找需要的jar不太明智,可以使用一些带有搜索服务的Maven服务,例如MVNrepository

继承与聚合

Maven的继承与聚合,实际上是两个不同的概念,聚合:将多个module模块聚集在一起形成一个统一的项目;继承:作为父工程定义一些属性供子工程复用。实际开发中经常将这个两个概念统一在父工程中。父工程只包含一个type为pom的POM.xml文件, 对子工程进行依赖与插件管理。记录一个通用的父工程pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>xxx</groupId>
    <artifactId>xxx</artifactId>
    <packaging>pom</packaging>
	<name>XXX Project</name>
    <version>1.0-SNAPSHOT</version>
	
    <!--定义自定义属性-->
    <properties></properties>
	
    <!--聚合其他模块-->
    <modules>
        <module>xxx</module>
    </modules>
	
    <!--依赖管理,并不会真正引入依赖-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId></groupId>
                <artifactId></artifactId>
                <version></version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!--插件管理,并不会真正引入插件-->
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId></groupId>
                    <artifactId></artifactId>
                    <version></version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

</project>

在父工程进行依赖管理和插件管理,更多的是在限定使用的版本,而且也没有真实引入插件和依赖,真正引入插件和依赖的地方还是在子工程中,而且子工程中也可以引入不同版本的依赖/插件,或者引入父工程中没有Manager的依赖/插件

当在其他项目中需要使用父工程依赖/插件一样的配置,可以使用import范围依赖将这一个配置导入

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>存在依赖/插件管理的groupId</groupId>
			<artifactId>存在依赖/插件管理的artifactId</artifactId>
			<version>存在依赖/插件管理的version</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

子工程pom.xml文件中使用parent标签引入父工程坐标形成继承

<parent>
        <groupId></groupId>
        <artifactId></artifactId>
        <version></version>
        <!--relativePath用于指定父工程的POM.xml文件位置 相对路径 默认位置../pom.xml 如果路径不同使用该标签修改-->
        <!--<relativePath></relativePath>-->
</parent>

只构建指定模块

但项目比较庞大,而且模块众多,一次构建所有模块费时费力,这个时候可以指定所需模块,利用Maven的参数即可,因为模块之间存在依赖关系,构建时必须存在先后顺序,这种关系在Maven中称之为反应堆

  • -am:--also make 同时构建所指定的模块及依赖模块
  • -amd:--also-make-dependents 同时构建依赖于该模块的模块
  • -pl:--projects 构建指定的模块,模块之间使用逗号分隔
  • -rf:--resume-from 在反应堆构建顺序基础上指定从哪个模块开始构建

以上几个参数可以组合使用,例如:编译xxx模块,并先构建其依赖的模块(不然xxx模块无法使用)

mvn clean package -pl xxx -am

超级POM

所有pom.xml文件都默认继承一个超级POM文件,这个文件中指定了一些常用插件的版本,项目源码,配置文件,测试代码/配置文件都路径,项目构建路径等信息,这样的方式是Maven约定优于配置理念的提现,这个超级POM文件位于:$MAVEN_PATH/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路径下

所有的约定都在这里配置的,如果不想遵循约定(强烈不推荐),例如约定源码目录是:src/main/java,我们希望改为src/java,就需要在项目的pom.xml中的build元素中修改

<build>
    <sourceDirectory>src/java</sourceDirectory>
<build>

测试

跳过测试

测试作为项目构建中很重要的一步,理论上不应该跳过测试,只有测试通过之后的才会进行打包,安装等后续操作,这样也保证了项目的稳定。但是运行测试代码需要较长耗时,而且有些错误的单元测试没有得到修正,所以我们希望可以跳过测试。跳过方法两种

  1. 通过命令行参数skipTests
mvn package -DskipTests

这个指令可以跳过测试运行,但是测试代码还是会进行编译,同样会消耗大量的时间

mvn package -Dmaven.test.skip=true

这样就可以连测试代码编译过程也跳过了

通过命令行参数的方式是一种临时配置,只在当前执行中生效

  1. 通过Maven插件参数配置
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.5</version>
    <configuration>
        <skipTests>true</skipTests>
    </configuration>
</plugin>

这样的配置等同于mvn package -DskipTests,不同在于通过插件的配置让项目长时间跳过测试

同样的,想要跳过测试代码的编译过程,如下配置

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.1</version>
    <configuration>
        <skip>true</skip>
    </configuration>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.5</version>
    <configuration>
        <skipTests>true</skipTests>
    </configuration>
</plugin>

同样对应命令mvn package -Dmaven.test.skip=true

指定要运行的测试用例

maven-surefire-plugin提供了test参数让Maven用户能够在命令行指定要运行的测试用例

mvn test -Dtest=MyTest

可以使用*星号匹配零个或多个字符,如果指定多个测试用例,可以使用,逗号分隔

test参数必须匹配一个或多个测试类,如果没有就会报错,这时可以设置-DfailIfNoTests=false告诉maven-surefire-plugin即使没有任何测试也不要报错

mvn test -Dtest -DfailIfNoTests=false

包含与排除测试用例

默认情况下,在测试目录下所有以Test结尾的类都会当作测试用例,我们一般遵循这个规范就可,但是有的测试类是以Tests结尾的,这时就需要我们主动包含这样的测试用例

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.5</version>
    <configuration>
        <includes>**/*Tests.java</includes>
    </configuration>
</plugin>

**表示任意路径,一个星号*匹配0个或多个字符

如果需要排除一些测试用例,可以使用excludes元素

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
    <configuration>
        <excludes>**/*TempDemoTest</excludes>
    </configuration>
</plugin>

打包测试代码

一般来说测试代码是不会打包的,但是如果需要的话,也是可以办到的,通过配置maven-jar-plugin

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.2</version>
    <executions>
        <execution>
            <goals>
                <goal>test-jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

这样在Maven的生命周期package阶段就会对测试代码打包,形成类似xxx-tests.jar文件,但这个文件部署到仓库中之后,就可以通过依赖声明了

<dependency>
    <groupId>com.test</groupId>
    <artifactId>myTest</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <type>test-jar</type>
    <scop>test</scop>
</dependency>

与一般的以来不同的是,这里使用了特殊的元素type,而去测试包只能使用特殊的test-jar类型

web容器插件

配置jetty插件

<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>7.6.16.v20140903</version>
    <configuration>
        <scanIntervalSeconds>10</scanIntervalSeconds>
        <webAppConfig>
            <contextPath>/test</contextPath>
        </webAppConfig>
    </configuration>
</plugin>

scanIntervalSeconds表示扫描项目变更的时间间隔,这里配置的是每隔10秒,默认值是0,表示不扫描,配置这个值可以实现类似热部署的功能
contextPath 表示项目部署之后的context path 例如这里:http://hostname:port/test/访问应用

在启动之前可以对setting.xml文件进行一个小修改(可选)。因为默认情况下,只有org.apache.maven.pluginsorg.codehaus.mojo两个groupId的插件才可以支持简化命令行调用

<setting>
  <pluginGroup>
    <pluginGroup>org.mortbay.jetty</pluginGroup>
  </pluginGroup>
</setting>

现在可以在命令行中使用如下命令启动jetty-maven-plugin

mvn jetty:run

jetty 默认监听本地8080端口,如果使用其他端口,可以添加jetty.port参数

mvn jetty:run -Djetty.port=9090

配置tomcat插件

<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
        <port>8080</port>
    </configuration>
</plugin>

运行tomcat7插件的命令如下

mvn tomcat7:run

因为这个插件有很长时间没有更新了,所以不是很推荐使用,不如直接部署到tomcat容器中,还可以挑选指定的tomcat版本

Maven属性

Maven的属性有6类,这些属性就相当于Maven中的变量。分别为

  • 内置属性:主要有两个内置属性, ${basedir}表示项目根目录,即包含pom.xml文件所在的目录;${version}表示项目的版本号
  • POM属性:可以适用该类属性应用POM文件中对应元素的值。例如${project.artifactId}对应<project><artifactId>元素的值。常用的POM属性有
    • ${project.build.sourceDirectory}: 项目的主源码目录,默认为src/main/java
    • ${project.build.testSourceDirsctory}:项目的测试源码目录,默认为src/test/java
    • ${project.directory}:项目构建输出目录,默认为target
    • ${project.outputDirectory}:项目主代码输出目录,默认为target/classes
    • ${project.testOutputDirectory}:项目测试代码编译输出目录,默认为target/test-classes
    • ${project.groupId}:项目的GroupId
    • ${project.artifactId}:项目的ArtifactId
    • ${project.version}:项目的version, 与${version}等价
    • ${project.build.finalName}:项目打包输出文件的名称,默认为${project.artifactId}-${project.version}
  • 自定义属性,这个应该是最为常见的,用户可以在POM的<projects>元素下自定义Maven属性,例如
<project>
 <properties>
  <my.prop>hello</my.prop>
 </properties>
</project>

然后在POM中使用${my.prop}的时候就会被自动替换为hello

  • Settings属性:用户可以在settings.开头的属性引用settins.xml文件中XML元素的值,比较常用的${settings.localRepository}指向用户本地仓库地址
  • Java系统属性:所有Java系统属性都可以被Maven属性引用,例如${user.home}指向了用户目录,用户可以使用mvn help:system参看所有的Java系统属性
  • 环境变量属性:所有环境变量都可以使用以env.开头的Maven属性引用。例如${env.JAVA_HOME}指向了JAVA——HOME环境变量的值,用户可以使用mvn help:system参看所有的环境变量

利用这些属性可以更好地简化POM的配置,例如在多模块项目中,模块之间以来比较常见,这些模块之间一般都是相同的groupId和version,因此就可以如下利用POM属性配置

<dependency>
    <groupId>${project.groupId}</groupId>
    <artifactId>dao</artifactId>
    <version>${project.version}</version>
</dependency>
<dependency>
    <groupId>${project.groupId}</groupId>
    <artifactId>util</artifactId>
    <version>${project.version}</version>
</dependency>
posted @ 2020-07-19 01:01  OverZeal  阅读(274)  评论(0编辑  收藏  举报