架构师工具箱(二)Maven——解决类冲突、包依赖,NoClassDefFoundError问题定位及解决

写在前面:

  • 你好,欢迎关注!
  • 我热爱技术,热爱分享,热爱生活, 我始终相信:技术是开源的,知识是共享的!
  • 博客里面的内容大部分均为原创,是自己日常的学习记录和总结,便于自己在后面的时间里回顾,当然也是希望可以分享 自己的知识。如果你觉得还可以的话不妨关注一下,我们共同进步!
  • 个人除了分享博客之外,也喜欢看书,写一点日常杂文和心情分享,如果你感兴趣,也可以关注关注!
  • 公众号:傲骄鹿先生

目录

一、解决类冲突、包依赖

三、NoClassDefFoundError问题定位及解决


一、解决类冲突、包依赖

在实际开发中,多模块项目常会使用Maven进行包管理。在poml文件中进行包依赖时,常存在引入一个jar包中默认依赖了其他的jar包的情况。这样很容易导致jar包冲突,从而产生一些诡异问题,如版本问题导致的类、方法找不到等。下面我们将聊聊具体关于依赖冲突产生的原因、排查方式以及解决的方案。

1、类冲突、包依赖的概念

(1)依赖传递的概念

举个简单例子,比如一个多模块项目依赖关系如下图。其中bepe-dal引入了common-lib,当bepe-manager模块中引入bepe-dal时,common-lib这个依赖也会被引入到bepe-manager模块中,这个就是依赖传递。

Maven中jar包冲突原理及解决的方式

(2)依赖冲突的概念

依赖冲突指当模块中引入很多jar包时,如果其中存在着groupId和artifactId 一样,但是version不一样的两个jar包,这就是依赖冲突。

2、依赖冲突解决的三种方式

(1)最短路径原则

当存在groupId和artifactId一致但是version不一致的jar包冲突时,模块会自动选择距离自己路径短的包。如:bepe-manager到common-lib(1.0)的距离为2,bepe-manager到common-lib(2.0)的距离为1,就会选择距离短的common-lib(2.0),这就是最短路径原则。

Maven中jar包冲突原理及解决的方式

(2)声明优先原则

当冲突包路径距离长度一样时,这个时候就会依据其在pom文件中声明的先后顺序。

Maven中jar包冲突原理及解决的方式

在manager模块pom.xml中,如果先引用bepe-common,就会用2.0版本的common-lib。

<dependency>  
     <groupId>com.company.bepe</groupId>  
     <artifactId>bepe-common</artifactId>  
     <version>2.2</version>  
</dependency>
  
<dependency>  
     <groupId>om.company.bepe</groupId>  
     <artifactId>bepe-dal</artifactId>  
     <version>2.2</version>  
</dependency>  

(3)依赖排除

通过标签将不需要依赖的包进行排除,通过这种方式我们就灵活进行取舍。但是该如何发现冲突呢?接下来将讨论关于依赖冲突排查的方式。

依赖冲突情况该怎么排查?

我们可以借助一些插件工具帮助找出冲突jar的具体位置。下面分享一下我在项目中排查并解决包冲突的两种方式。

  • maven-enforcer-plugin 插件
  • Maven Helper 插件

1. maven-enforcer-plugin插件

Maven提供了Maven-Enforcer-Plugin插件 , 用来校验约定遵守情况,比依赖 jar 包的版本等等。当规则检查不通过的时候则会构建失败。

(1) 在pom.xml中引入该插件

Maven中jar包冲突原理及解决的方式

rules内则是定义校验规则,通过配置可实现重复依赖检测。也支持自定义做一些其他检验如版本检验等。关于maven-enforcer-plugin插件rules的其他配置用法,感兴趣的朋友们,可以去查阅其相关的资料。

<rules> 
    <requireMavenVersion> 
        <version>3.0.4</version> 
    </requireMavenVersion> 
    <!--要求JDK版本)--> 
    <requireJavaVersion> 
        <version>6.0</version> 
    </requireJavaVersion> 
    <bannedDependencies> 
        <!--是否检查传递性依赖(间接依赖)--> 
        <searchTransitive>true</searchTransitive> 
        <excludes> 
            <exclude>junit:junit</exclude> 
        </excludes> 
        <message>must use TestNG</message> 
    </bannedDependencies> 
</rules> 

(2) 配置好插件后进行项目构建,当存在包冲突时会在console中打印出来。

Maven中jar包冲突原理及解决的方式

(3) 依据信息便可将不需要的jar包通过排除掉。

Maven中jar包冲突原理及解决的方式

2. Maven Helper

使用IntelliJ IDE的Maven helper插件方便找到和排除冲突的依赖项

(1)安装插件

(2) 重启后即可使用,打开pom文件后,文件下面会多出Dependency Analyzer这一个tab,进入Dependency Analyzer视图之后有三个查看选项,分别是Conflicts(冲突)、All Dependencies as List(列表形式查看所有依赖)、All Dependencies as Tree(树结构查看所有依赖)。通过查看信息后再做出对应的依赖冲突处理。

Maven中jar包冲突原理及解决的方式

关于依赖冲突解决方式有三种:最短路径原则、声明优先原则、依赖排除。在没有手动进行依赖排除的情况下,会依据最短路径原则、声明优先原则来选择jar包。关于依赖冲突排查可借助如maven-enforcer-plugin 与Maven Helper 插件。根据实际情况及环境,选择组合最优的解决方案解决依赖冲突问题。

二、NoClassDefFoundError问题定位及解决

NoClassDefFoundError错误的发生,是因为Java虚拟机在编译时能找到合适的类,而在运行时不能找到合适的类导致的错误。例如在运行时我们想调用某个类的方法或者访问这个类的静态成员的时候,发现这个类不可用,此时Java虚拟机就会抛出NoClassDefFoundError错误。

简单总结就是,NoClassDefFoundError发生在编译时对应的类可用,而运行时在Java的classpath路径中,对应的类不可用导致的错误。

怎么解决NoClassDefFoundError错误?

(1)把依赖的jar包拷贝到build的目录,并添加classpath,

     <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
              <classpathPrefix>lib/</classpathPrefix>
              <mainClass>com.mycmp.demo.Main</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <execution>
            <id>copy</id>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>
                ${project.build.directory}/lib
              </outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>

(2)把所有依赖的jar包打包到build的jar包里面,实现零依赖

      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
          <archive>
            <manifest>
              <mainClass>com.mycm.demo.Main</mainClass>
            </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
      </plugin>
posted @ 2020-03-23 23:05  傲骄鹿先生  阅读(351)  评论(0编辑  收藏  举报