Maven in Action

新公司大家都在用maven,老东家用的是ant,我自己用的是gradle,为了了解工具细节还是需要看一下书的,所以找了这本书过了一个,在这里作个记录。

概念

<?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/maven-v4_0_0.xsd">  
    <modelVersion>4.0.0</modelVersion>         
    <groupId>vicviz</groupId>
    <artifactId>hello-world</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>Maven Hello World Project</name>
</project>
一些名词
名词 作用
project 根元素
modelVersion 当前 POM 模型的版本,对于 Maven2 及 Maven 3 来说,它只能是 4.0.0
groupId 组一般是指项目所在公司或者组织
artifactId 可以理解为组件Id,一般来说,代码都放在groupId.artifactId.{package}下
version 版本信息,SNAPCHOT表明为开发中的不稳定版本
name 非必须的,推荐加上方便称呼

命令

  1. mvn clean:删除target
  2. mvn compile:编译
  3. mvn test:编译以及跑测试,一般标准是使用junit来执行测试用例
  4. mvn package:打包,会在target下生成artifactId-version.jar,如果生成的jar执行时(java -jar **.jar)提示没有主清单属性,有几种方法,推荐maven-shade-plugin
  5. mvn clean package -Dmaven.test.skip=true:不进行测试打包
  6. mvn deploy: 发布到仓库
  7. mvn dependency:copy-dependencies:下载包到当前目录下,有可能是lib也有可能是target里面,会有提示。这样我们就可不用打jar-with-dependency.jar包了。使用时可以配合指定路径的参数一起使用-Djava.ext.dirs={lib包所在的目录},当然,我们也可以使用java的bin/start.sh(或run.sh)文件来统一管理
    可以这样写: java -cp target/validater-1.0-SNAPSHOT.jar -Djava.ext.dirs=/export/home/zhaowei/workspace/rt_compute/validater/target/dependency/ com.jd.search.realtime.validater.Test

    也可以这样写 java -cp ./target/dependency/fastjson-1.2.46.jar:target/validater-1.0-SNAPSHOT.jar com.jd.search.realtime.validater.Test, 注意使用通配符时(linux上),不要加.jar,得写成:java -cp ./target/dependency/*:target/validater-1.0-SNAPSHOT.jar

生命周期

生命周期阶段 描述
validate 验证项目是否正确,并且所有必要的信息可用于完成构建过程
initialize 建立初始化状态,例如设置属性
generate-sources 产生任何的源代码包含在编译阶段
process-sources 处理源代码,例如,过滤器值
generate-resources 包含在包中产生的资源
process-resources 复制和处理资源到目标目录,准备打包阶段
compile 编译该项目的源代码
process-classes 从编译生成的文件提交处理,例如:Java类的字节码增强/优化
generate-test-sources 生成任何测试的源代码包含在编译阶段
process-test-sources 处理测试源代码,例如,过滤器任何值
test-compile 编译测试源代码到测试目标目录
process-test-classes 处理测试代码文件编译生成的文件
test 运行测试使用合适的单元测试框架(JUnit)
prepare-package 执行必要的任何操作的实际打包之前准备一个包
package 提取编译后的代码,并在其分发格式打包,如JAR,WAR或EAR文件
pre-integration-test 完成执行集成测试之前所需操作。例如,设置所需的环境
integration-test 处理并在必要时部署软件包到集成测试可以运行的环境
pre-integration-test 完成集成测试已全部执行后所需操作。例如,清理环境
verify 运行任何检查,验证包是有效的,符合质量审核规定
install 将包安装到本地存储库,它可以用作当地其他项目的依赖
deploy 复制最终的包到远程仓库与其他开发者和项目共享

包冲突解决

包冲突产生的原因和解决

  1. 在依赖传递时同一个Jar包出现了多个不同版本,并选择了错误的版本而导致JVM加载不到需要的类或加载了错误版本的类。

  2. 另一个比较麻烦同样的类出现在了不同的jar包中,例如:GenericKeyedObjectPool.class出现在了某个jedis版本依赖的org.apache.commons:commons-pool2,也出现在了hive3.1.0依赖的commons-pool:commons-pool,如果某个项目同时依赖于hive和jedis,可能就会遇到莫名的异常,提示说GenericKeyedObjectPool执行错误。

包冲突问题排查

  1. 直接在IDE里面搜索类:cmd+shift+o,会看到某个类会在多个路径下
  2. 在应用程序启动时加上JVM参数-verbose:class或者-XX:+TraceClassLoading,日志里会打印出每个类的加载信息,如来自哪个Jar包
  3. mvn dependency:tree可以看到所有的依赖,mvn dependency:tree -Dverbos -Dincludes=commons-pool:commons-pool可以看

包冲突解决

  1. 不同版本的冲突--这类的冲突需要了解一下maven的仲裁机制:
    • 优先按照依赖管理元素中指定的版本声明进行仲裁
    • 若无版本声明,则按照“短路径优先”的原则(Maven2.0)进行仲裁,即选择依赖树中路径最短的版本
    • 若路径长度一致,则按照“第一声明优先”的原则进行仲裁,即选择POM中最先声明的版本

所以解决的办法可以修改dependencyManagement中的声明,也可以加入,另外可以考虑一下升级。如果必须要有两个,则考虑一下下面的maven-shade-plugin。

  1. 同一个类(同package同name)出现在不同的依赖中--直接maven-shade-plugin改名吧
    maven-shade-plugin 在打包时,可以将项目中依赖的 jar 包中的一些类文件打包到项目构建生成的 jar 包中,在打包的时候把类重命名。
<plugin>  
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.4.3</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <relocations>
                <relocation>
                  <pattern>org.apache.commons.pool.impl</pattern>

      <shadedPattern>shade.org.apache.commons.pool.impl</shadedPattern>
                  </relocation>
              </relocations>
            </configuration>
          </execution>
        </executions>
      </plugin>

关于打包

plugin 描述
maven-jar-plugin maven 默认打包插件,用来创建 project jar
maven-shade-plugin 用来打可执行包,executable(fat) jar, 也可以解决类的多版本冲突
maven-assembly-plugin 支持定制化打包方式,例如 apache 项目的打包方式

case

  1. 修改本地仓库
which mvn  
  /Users/viz/Downloads/software/dev/mvn/apache-maven-3.5.0/bin/mvn

  vi /Users/viz/Downloads/software/dev/mvn/apache-maven- 
 3.5.0/conf/settings.xml
修改以下路径就好了
<localRepository>/path/to/local/repo</localRepository>  
Default: ${user.home}/.m2/repository  
  1. 增加远程仓库
在项目的pom.xml中,<project>里面加入
 <repositories>
        <repository>
            <id>company</id>
            <name>company Internal Repository</name>
<url>http://nexus.company.com/nexus/content/groups/public</url>  
        </repository>
    </repositories>
  1. java版本错误
Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project demos: Compilation failure: Compilation failure:  
[ERROR] /Users/viz/workspace/github/demos/src/main/java/vicviz/demos/java8/FutureDemo.java:[12,55] -source 1.6 中不支持 lambda 表达式
[ERROR]   (请使用 -source 8 或更高版本以启用 lambda 表达式)


处理:可以改setting.xml,也可以改pom.xml,后者更好
            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
  1. 通过文件加入本地仓库
    mvn install:install-file -Dfile=path-{version}.jar -DgroupId=com.google.code -DartifactId=kaptcha -Dversion={version} -Dpackaging=jar
  1. 生成文档
    mvn site
  1. 关于快照和版本
版本如果有更新,也不会被拉到。而快照SNAPSHOT加入-U参数会被及时的拉取.
<version>1.0-SNAPSHOT</version>  
mvn clean package -U  
  1. 不进行单元测试的打包
mvn -U -DskipTests clean package  
  1. 关于scope
    • compile:默认范围,编译测试运行都有效
    • provided:在编译和测试时有效,打包的时候,这个依赖的包可能不会打到jar包中
    • runtime:在测试和运行时有效
    • test:只在测试时有效
    • system:在编译和测试时有效,与本机系统关联,可移植性差
comments powered by Disqus