天天看点

记录一次解决maven jar包冲突的过程

项目介绍

 项目A需要是用maven来管理jar包,主要需要引用两个依赖

依赖1:<groupId>com.pilosa</groupId>

            <artifactId>pilosa-client</artifactId>

            <version>1.0.0</version>

(后面用pilosa表示)

依赖2:<groupId>org.apache.hbase</groupId>

            <artifactId>hbase-client</artifactId>

             <versio>5.13.1-cdh-1.2.0</version>

(后面用hbase表示)

错误过程表述

 问题1: protobuf-java 各版本不兼容

 启动的时候,启动成功 ,但执行到 pilosa(依赖1)包中相关的方法时,提示关于com.google.protobuf 包的NoSuchMethodError方  法异常,通过IDE 工具查看,该类在

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>xxx</version>
</dependency>             

依赖中,并且pilosa(依赖1) 和 hbase(依赖2) 都含有该依赖,但是版本不一样。看到这样的情况,我们一般会排除一个protobuf-java依赖。当排除pilosa包中的protobuf-java依赖时,发现pilosa执行相关方法 会报关于protobuf-java的错误;没办法不排除pilosa包中的依赖,排除hbase包中的protobuf-java依赖,发现pilosa执行时没问题了,但是hbase执行相关方法时 报protobuf-java的错误。

这怎么办,排除哪个都不行,查阅资料得知,可能引用的jar包相互不兼容,pilosa和hbase 所依赖的pritobuf-java不兼容。解决jar包不相互兼容问题,可以使用maven插件shade 中的relocation 功能.(详情可以baidu/google 一大堆资料),大致功能就是重定向包名,当修改了包名,两个包名不一样了,那 这两个包不就可以同时加载了,不会出现冲突了嘛(当然引用该包的import 也会跟着修改的)。  具体做法就是再创建一个子项目,项目名叫hbase-java-conflict (我打算将hbase包中的protobuf-java 重定向),具体pom文件如下:

<dependencies>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>${hbase.version}-cdh${cdh.version}</version>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <relocations>
                                <relocation>
                                    <pattern>com.google.protobuf</pattern>
                                    <shadedPattern>com.google.shaded.protobuf</shadedPattern>
                                </relocation>
                                
                            </relocations>

                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>           

com.google.protobuf 就是原来的包名  com。google.shaded.protobuf就是重定向后的包名 ,然后将子项目 执行mvn clean install ,打包依赖到本地后,在项目中引用该子项目的依赖。在执行时 没有报关于protobuf相关的错误了

(注意有的时候pom文件 <build> 配置了 shade relocations 功能,执行mvn clean install 时直接报错:Failed to execute goal org.apache.maven.plugins:maven-shade-plugin:3.0.0:shade (default) on project java.test: Error creating shaded jar: null: IllegalArgumentException -> [Help 1]   ,本人对hbase包重定向没问题,但对pilosa包重定向报上面的错误,经过个人查阅资料,只需要升级 maven-shade-plugin 插件的版本,从3.0.0升级位3.1.0 就好了。 具体原因大家可以自行深入研究)

 问题2:  window正常运行,linux环境启动报  java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log

  遇到这样的问题,作为程序员只能硬着头皮去解决。哎,程序员辛苦了!该类属于依赖

 <groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId> ,

查看项目类结构确实 又有 两个引用,pilosa依赖和hbase依赖各有一个slf4j-api的引用,去掉一个试试呗,不行;去掉另一个还是不行。  难道又是不jar包兼容 ?和解决问题1 一样,在hbase子项目中使用万能的relocations功能,配置后信心满满的启动查看日志,傻了! 报错 :Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/spi/LocationAwareLogger

        at org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(SLF4JLogFactory.java:156)

        at org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(SLF4JLogFactory.java:131)

        at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:655)

        at org.apache.hadoop.conf.Configuration.<clinit>(Configuration.java:180)

        at load.data.totaluser.TotalUserLoadData.<init>(TotalUserLoadData.java:56)

        at load.data.totaluser.TotalUserLoadDataMain.main(TotalUserLoadDataMain.java:14)

生无可恋啊,这是咋回事啊。如果哭三天能解决问题,我直接选择哭三天 。 静下心来突然意识到错误提示变了,变成找不到类了。但是有个问题,这个类我不是重定向了嘛,为什么执行hbase代码提示的确实重定向前的类?难道hbase子项目没有重定向成功?要是这样,这可咋办啊?应该不会出现这样的错误吧?带着这些问题反复尝试,还是一样的结果?没办法只有在子项目中一层一层的看 报错堆栈的类,突然发现hbase子项目没有上述报错堆栈中的SLF4JLogFactory.java  .  这,,这.我崩溃了,居然没有这个类你是怎么加载的啊。 经过程序员反复的检查,尝试。在项目A中(注意不是hbase 子项目)发现LogFactory.java 有三处,而且这三处是在不同的jar包中,看到这我明白了,我之前通过排除slf4j-api其他的版本,但是并不能排除其他jar,于是我看还有哪些jar包中含有LogFactory类,然后排除了相面两个依赖。

<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>           
<groupId>commons-logging</groupId>
 <artifactId>commons-logging</artifactId>           

把对hbase子项目有冲突(不仅仅是版本冲突,可能不同的jar包含有相同的类--包名和类名都相同)的jar包统统处理掉,然后去掉了对 org.slf4j.spi 的重定向(不是不兼容导致的嘛) ,再次执行mvn clean install,重新启动,终于没有报该错误了,本来以为我可以休息下了,没想到神经再次紧张起来,别急,还有别的错,只不过不是关于org.slf4j.spi的错啦!

问题3:Exception in thread "main" java.lang.NoSuchFieldError: INSTANCE

 报错堆栈:Exception in thread "main" java.lang.NoSuchFieldError: INSTANCE

        at org.apache.http.impl.io.DefaultHttpRequestWriterFactory.<init>(DefaultHttpRequestWriterFactory.java:53)

        at org.apache.http.impl.io.DefaultHttpRequestWriterFactory.<init>(DefaultHttpRequestWriterFactory.java:57)

        at org.apache.http.impl.io.DefaultHttpRequestWriterFactory.<clinit>(DefaultHttpRequestWriterFactory.java:47)

        at org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<init>(ManagedHttpClientConnectionFactory.java:83)

        at org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<init>(ManagedHttpClientConnectionFactory.java:96)

        at org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<init>(ManagedHttpClientConnectionFactory.java:105)

        at org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<clinit>(ManagedHttpClientConnectionFactory.java:63)

        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager$InternalConnectionFactory.<init>(PoolingHttpClientConnectionManager.java:586)

        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:180)

        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:164)

        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:155)

        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:131)

        at com.pilosa.client.PilosaClient.connect(PilosaClient.java:465)

        at com.pilosa.client.PilosaClient.clientExecute(PilosaClient.java:487)

        at com.pilosa.client.PilosaClient.readServerSchema(PilosaClient.java:324)

        at com.pilosa.client.PilosaClient.readSchema(PilosaClient.java:346)

        at load.data.totaluser.TotalUserLoadData.getUserLabelIndex(TotalUserLoadData.java:82)

        at load.data.totaluser.TotalUserLoadData.<init>(TotalUserLoadData.java:70)

        at load.data.totaluser.TotalUserLoadDataMain.main(TotalUserLoadDataMain.java:14)

    根据 类名 查询 ,上述类所属的包在下面两个包中

<groupId>org.apache.httpcomponents</groupId>  
<artifactId>httpclient</artifactId>

<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>             

   先判断 两个jar包 依赖有冲突,还是老办法,先保留一个版本(我一般保留版本高的版本,可以向下兼容嘛),于是去掉了hbase 子项目关于httpclient 和 httpcore的依赖,再次打包到linux 服务去上,这个时候自己已经麻木了,失败次数太多了都不知道什么叫成功了 。启动,查看日志,没有任何错误,数据也对应写进去了,一切顺利。 哎,程序员的幸福也许就在那短暂的一刻,在这一刻最想做的是 致敬所有的程序猿/媛 们

总结

  遇到jar包冲突,先去掉低版本的依赖试试,一般能解决jar包冲突;如果冲突的各个版本不相互兼容,可以使用maven shade插件的relocation功能(注意点 : 类冲突不仅仅发生在不同版本的依赖哦,还可能发生在不同jar包之间哦,比如问题2中的LogFactory类,具体是那种需要自己查看的)

上一篇: WRITE