首页 > 技术文章 > maven实践--你所需要了解的maven

small-k 2018-01-06 17:46 原文

基本概念

是什么?

Maven是一个强大的Java项目构建工具。

什么是构建工具?

构建工具是将软件项目构建相关的过程自动化的工具。构建一个软件项目通常包含以下一个或多个过程:

  1. 生成源码(如果项目使用自动生成源码);
  2. 从源码生成项目文档;
  3. 编译源码;
  4. 运行单元测试
  5. 将编译后的代码打包成JAR文件或者ZIP文件;
  6. 将打包好的代码安装到服务器、仓库或者其它的地方;
  7. 叙述项目间的依赖关系

有些项目可能需要更多的过程才能完成构建,这些过程一般也可以整合到构建工具中,因此它们也可以实现自动化。

自动化构建过程的好处是将手动构建过程中犯错的风险降到最低。而且,自动构建工具通常要比手动执行同样的构建过程要快。

核心概念:POM文件(POM project object mode项目对象模型)

POM文件是以XML文件的形式表述项目的资源,如源码、测试代码、依赖(用到的外部Jar包)等。

pom主要组成部分:
image

简单运行

当你安装好了Maven,并且在项目的根目录下创建了POM文件,可以在项目上运行Maven了。

mvn clean install

该命令首先执行clean构建周期,删除Maven输出目录中已编译的类文件,然后执行install构建阶段。

maven深入实践

构建生命周期

Maven中内置了三个构建生命周期:default,clean和site。default生命周期处理工程的部署,clean生命周期处理工程的清理,而site生命周期则负责创建工程的站点文档。

packaging直接影响Maven的构建生命周期。

在Maven 3中,其可用的打包类型如下:

  1. jar,默认类型
  2. war
  3. ejb
  4. ear
  5. rar
  6. par
  7. pom
  8. maven-plugin

如果想一次打包多个项目,可采用聚合类型pom,它通常由一个父模块和若干个子模块构成。

<packaging>pom</packaging>

<modules>
    <module>zy-service-consumer</module>
    <module>zy-eureka-server</module>
    <module>zy-zipkin-service</module>
    <module>zy-hystrix-dashboard</module>
</modules>

构建生命周期是由阶段组成的

三个构建生命周期都是由一系列不同的构建阶段组成,每一个构建阶段代表了生命周期的一个阶段。

以default生命周期为例,它是由以下的阶段组成

  • validate – 验证该项目是否正确,所有必要的信息都是可用的
  • compile – 编译工程源码
  • test – 使用一个合适的单元测试框架测试编译的源代码。这些测试的代码不会被打包或部署到项目中
  • package – 将编译的代码打包成它发布的格式,例如JAR
  • integration-test – 如果必要的话,该命令会将工程处理并部署在一个集成测试运行的环境中
  • verify – 运行任何检查以验证该包是否有效,是否符合质量标准
  • install – 将工程打包安装到本地仓库中,以便本地其他项目可以进行依赖
  • deploy – 在集成或发布环境中,将最终工程打包复制到远程仓库中,用于与其他开发人员和项目共享
mvn deploy
它不仅执行指定的构建阶段,而且会执行指定构建阶段之前的每一个阶段。因此,执行

构建阶段使用的构建插件

mvn install


[INFO] ------------------------------------------------------------------------
[INFO] Building zy-zeromq 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ zy-zeromq ---
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ zy-zeromq ---
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ zy-zeromq ---
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ zy-zeromq ---
[INFO] 
[INFO] --- maven-surefire-plugin:2.18.1:test (default-test) @ zy-zeromq ---
[INFO] 
[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ zy-zeromq ---
[INFO] 
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ zy-zeromq ---

(查看完整的生命周期阶段列表,请参考生命周期参考

仓库

仓库只分为两类:本地仓库和远程仓库。

当Maven根据坐标寻找构建的时候,首先会查看本地仓库,如果本地仓库存在此构件,则直接使用;如果本地仓库不存在此构件,或者需要查看是否有更新的构件版本,Maven就会到远程仓库查找,发现需要的构件后,将其下载到本地仓库再使用。如果本地仓库和远程仓库都不存在需要的构件,则报错。

image

私服

私服的好处:节约外网的带宽,加速Maven的构建,方便部署第三方构件,稳定等

image

部署到远程仓库

将本项目jar包上传到私服、远程仓库,供项目组或其他人使用。配置如下:

pom.xml
<distributionManagement>
    <repository>
    	<id>thirdparty</id>
    	<name>3rd party</name>
    	<url>http://192.168.100.103:8081/nexus/content/repositories/thirdparty/</url>
    </repository>
</distributionManagement>

settigns.xml配置认证
<servers>
    <server>
        <id>thirdparty</id>
        <username>repo-user</username>
        <password>repo-psw</password>
    </server>
</servers>


mvn命令:
本地仓库 的更新
mvn  clean package install 
远程 仓库 的更新
mvn clean package deploy

如果编译出问题时,可以执行如下命令
mvn clean install -e -U
-e详细异常,-U强制更新 

为了加速下载,可以配置一些镜像mirror。

依赖

依赖传递性

  1. 子项目可以继承父项目的依赖
  2. 项目A依赖项目B,项目A-->项目B,项目B中的依赖也会被加入到项目A中。

由于依赖的传递性,导致项目中存在多个同名的jar包,存在jar包冲突等。maven提供的以下机制来解决这些问题:

依赖调解原则

过从Maven 2.0.9开始,POM中依赖声明的顺序决定了哪个版本会被使用,也叫作”第一声明原则”。

依赖管理

在多模块应用中,可能多个子项目会有共同的依赖。

在顶层的pom中指定依赖项的版本,以便让所有的子项目使用同一版本。dependency management只是依赖的一个声明,子项目需显式引用,但不用指定版本号。

父项目pom
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>


子项目pom,需显式引用依赖,但不用指定版本号。
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

依赖范围

  • compile 默认的scope,表示 dependency 都可以在生命周期中使用。而且,这些dependencies 会传递到依赖的项目中。适用于所有阶段,会随着项目一起发布
  • provided 跟compile相似,但是表明了dependency 由JDK或者容器提供,例如Servlet AP和一些Java EE APIs。这个scope 只能作用在编译和测试时,同时没有传递性。
<dependency>
    <groupId>org.apache.storm</groupId>
    <artifactId>storm-core</artifactId>
    <version>1.1.1</version>
    <scope>provided</scope>
</dependency>
  • runtime 表示dependency不作用在编译时,但会作用在运行和测试时,如JDBC驱动,适用运行和测试阶段。
  • system 跟provided 相似,但是在系统中要以外部JAR包的形式提供,maven不会在repository查找它。
  • import(Maven2.0.9及以上)
    import范围只适用于pom文件中的部分。表明指定的POM必须使用部分的依赖。因为依赖已经被替换,所以使用import范围的依赖并不影响依赖传递。
  • test 范围表明使用此依赖范围的依赖,只在编译测试代码和运行测试的时候需要,应用的正常运行不需要此类依赖。

依赖排除

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  • 小技巧:一般在编译时类找不到,但是实际存在jar冲突情况时。在idea中安装Maven Helper插件,根据jar名称搜索,定位到具体的依赖,在依赖中排除冲突jar包。

可选依赖

可选的依赖工作原理

Project-A -> Project-B
上面的图意味着项目A依赖于项目B,当A在它的POM文件中把B声明为一个可选的依赖,他们的关系依然没有改变。仅仅就像一次正常的构建,在这次构建中,项目B将会被添加进classpath。

Project-X -> Project-A
但是当一个其他的项目(项目X)在它的POM文件中声明项目A为一个依赖,这个可选的依赖就发挥作用了。你将会注意到项目X的classpath不会包含项目B:为了把B包含进项目X的classpath,你需要在你的POM文件中直接声明。

例子:

有一个名为X2的项目,这个项目和hibernate有一些类似的功能,支持许多数据库驱动/依赖,比如说MySQL,postgre,oracle等。为了构建X2,所有的这些依赖都是必须的,但是对于你的项目来说却不是必须的,所以对于X2把这些依赖声明为可选的是非常实用的,不论什么时候当你在POM文件中把X2声明为一个直接依赖的时候,所有被X2支持的驱动不会自动的被包含进你的项目的classpath,你需要直接声明你将要使用的数据库的依赖/驱动。

自定义打包maven-assembly-plugin

在运行mapreduce/storm时,一般是以jar包方式运行,每个jar包中依赖的jar的包,可以采用maven-assembly-plugin。

<build>
    <plugins>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <appendAssemblyId>false</appendAssemblyId>
                <descriptors>
                    <descriptor>assembly.xml</descriptor>
                </descriptors>
                <finalName>${project.artifactId}-${project.version}</finalName>
                <attach>false</attach>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase><!-- 绑定到package生命周期阶段上 -->    
                    <goals>
                        <goal>single</goal><!-- 只运行一次 --> 
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

assembly.xml

<!--是否生成和项目名相同的根目录-->
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
	<fileSet>
		<directory>${project.basedir}/target/classes</directory>
		<outputDirectory>.</outputDirectory>
	</fileSet>
	<!--<fileSet>
		<directory>${project.basedir}/src/main/resources</directory>
		<outputDirectory>./</outputDirectory>
	</fileSet>-->
</fileSets>
<dependencySets>
	<dependencySet>
		<!--是否把本项目添加到依赖文件夹下-->
		<useProjectArtifact>false</useProjectArtifact>
		<outputDirectory>/lib</outputDirectory>
		<!--<excludes>
			<exclude>org.apache.storm:storm-core</exclude>
		</excludes>-->
	</dependencySet>
</dependencySets>

其他常见配置方法

<configuration>
    <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
    ...
</configuration>
这种方式打出的包是fat jar,也就是把依赖的jar包全部解压成class文件后,再与自己的代码打成一个jar包。

maven assembly

命令

  1. mvn clean 清除目标目录中的生成结果
  2. mvn package 依据项目生成 jar 文件,打包之前会进行编译,测试
  3. mvn install在本地 Repository 中安装 jar
跳过测试类 : -Dmaven.test.skip=true
下载jar包源码: -DdownloadSource=true
下载javadocs: -DdownloadJavadocs=true

Archrtype

原型(Archetype)是一个Maven工程模板工具包。

maven-archetype-quickstart用得比较多。

maven vs gradle

  • maven和gradle的比较与使用
    总的来说,gradle相比于maven有很打的灵活性,目前gradle的使用者越来越多。而由于maven之前处于主流,并且在某些方面maven较gradle还有一些优点,所以maven和gradle并存。在github上大多数优秀的开源项目会同时提供maven和gradle两种添加依赖的方式。

参考文献


tips:本文属于自己学习和实践过程的记录,很多图和文字都粘贴自网上文章,没有注明引用请包涵!如有任何问题请留言或邮件通知,我会及时回复。

推荐阅读