由提交storm项目jar包引发对jar的原理的探索
序:在开发storm项目时,提交项目jar包当把依赖的第三方jar包都打进去提交storm集群启动时报了发现多个同名的文件错误由此开始了一段对jar包的深刻理解之路。
java.lang.RuntimeException: Found multiple defaults.yaml resources.
You're probably bundling the Storm jars with your topology jar.
[jar:file:/home/hadoop/app/storm/lib/storm-core-0.9.6.jar!/defaults.yaml,
jar:file:/home/hadoop/stormApi-wordcount-1.0-SNAPSHOT-jar-with-dependencies.jar!/defaults.yaml]
新浪微博:intsmaze刘洋洋哥
![](https://images2015.cnblogs.com/blog/758427/201610/758427-20161013211832812-74497194.png)
eclipse打jar包
代码如下:
package cn.intsmaze; public class A { public static void main(String[] args) { System.out.println(args[0]); System.out.println("java工程打jar包"); } } package cn.intsmaze; import java.util.HashMap; import java.util.Map; import redis.clients.jedis.Jedis; public class demo { public static void main(String[] args) { System.out.println("java工程调用第三方jar包"); Jedis jedis = new Jedis("localhost", 6379); Map map=new HashMap(); map.put("11", "1"); d.jedis.hmset("33", map); d.jedis.close(); } }
上面的代码的java工程如下:
使用eclipse把该工程打包成jar包:
选择这个jar包的入口类
把上面代码打包为A.jar后,eclipse会自动为我们生成下面这个文件位于META-INF:
MANIFEST.MF文件
Manifest-Version: 1.0
Main-Class: cn.intsmaze.A 这里指定入口类
把上面代码打包为B.jar
MANIFEST.MF文件
Manifest-Version: 1.0
Main-Class: cn.intsmaze.demo 这里指定入口类
因为这里引用了工程lib下面第三方的jar包,但是该jar包并不在classpath路径下面,所有就没有找到该类。
在MANIFEST.MF文件增加calsspath值即可。
Manifest-Version: 1.0
Main-Class: cn.intsmaze.demo
Class-Path: lib/jedis-2.8.0.jar
然后我们把jedis-2.8.0.jar放到我们B.jar的同一级目录下即可。
这里成功运行了。
注意:
manifest.mf文件定义如下所示:
Manifest-Version: 1.0
Main-Class: com.Task
Class-Path: lib/dom4j-1.6.1.jar lib/jaxen-1.1-beta-7.jar
注意:
<1> manifest.mf文件最后一行必须是一个空行。 <2> lib/dom4j-1.6.1.jar和lib/jaxen-1.1-beta-7.jar之间用一个空格隔开。 <3>每个冒号后有一个空格。
Maven打包:
代码如下:
package cn.intsmaze; public class A { public static void main(String[] args) { System.out.println(args[0]); System.out.println("java工程打jar包"); } } package cn.intsmaze; import java.util.HashMap; import java.util.Map; import redis.clients.jedis.Jedis; public class demo { public static void main(String[] args) { System.out.println("java工程调用第三方jar包"); Jedis jedis = new Jedis("localhost", 6379); Map map=new HashMap(); map.put("11", "1"); d.jedis.hmset("33", map); d.jedis.close(); } }
如上代码当他们的pom文件中只配置了以下属性时:
<dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.3</version> </dependency> </dependencies>
这个时候使用maven打包。观察他的目录结构。
这个jar包里面没有包含依赖的jedis的jar包,且manifest.mf文件中也没有指定入口类和Class-Path(该程序到哪里去加载它依赖的jedis.jar包)。
如果要成功运行这个jar包,我们要在manifest.mf设置Main-Class和Class-Path。
方式二:我们可以在pom文件中设置把工程打jar包时把它依赖的jar包也打进来,同时指定Main-Class。
<build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>cn.intsmaze.demo.RedisDemo</mainClass> </manifest> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build>
观察他的目录结构。
同时也会打一个没有带依赖的jar包(效果就和没添加插件设置一样)
在打包storm工程时的问题:
<dependencies> <dependency> <groupId>org.apache.storm</groupId> <artifactId>storm-core</artifactId> <version>0.9.5</version> </dependency> </dependencies>
这个项目只会依赖jdk的jar包和storm的jar包,不依赖其他第三方jar包,我们把这个工程打出jar包,根据上面很明显我们知道jar包中不包含依赖的storm的jar包,且manifest.mf文件中也没有指定Main-Class和Class-Path。
但是把它提交到storm集群中,它是会运行的,这是因为stom集群的Class-Path的路径有jdk和storm的jar包了(我们使用java -jar命令就是jdk什么的。)。关于Class-Path我可以理解的,但是没有指定入口类,它是怎么启动的,我还要慢慢研究。
当上面的storm工程需要依赖第三方的mysql包时,我们必须在pom文件中要求把依赖的jar包打进来,不然我们要在manifest.mf指明它要到哪里去加载依赖的mysql包,同时还要把mysql包上传到我们打的jar包将要运行在那台机器上的指定目录让他可以根据Class-Path加载进去。
<dependencies> <dependency> <groupId>org.apache.storm</groupId> <artifactId>storm-core</artifactId> <version>0.9.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.27</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>cn.intsmaze.helloworld.WordCountTopologyMain</mainClass> </manifest> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build>
这里把依赖的第三方jar包都打进去了。
但是我们提交该jar包到storm集群中,报了如下错误:
java.lang.RuntimeException: Found multiple defaults.yaml resources.
You're probably bundling the Storm jars with your topology jar.
[jar:file:/home/hadoop/app/storm/lib/storm-core-0.9.6.jar!/defaults.yaml,
jar:file:/home/hadoop/stormApi-wordcount-1.0-SNAPSHOT-jar-with-dependencies.jar!/defaults.yaml]
这里说明stom集群环境中有storm的jar包,我们提交的jar包里面也包含storm的jar包,在读取配置文件时,发现有一样的文件冲突了导致启动错误。
这个时候我们设置如下:
<dependency> <groupId>org.apache.storm</groupId> <artifactId>storm-core</artifactId> <scope>provided</scope>期望JDK、容器或使用者会提供这个依赖 <version>0.9.5</version> </dependency>
这个时候不会把依赖的storm的包打进工程中,只会把依赖的mysql包打进来。
作者:intsmaze(刘洋)
畅销书籍:《深入理解Flink核心设计与实践原理》作者
老铁,你的--->推荐,--->关注,--->评论--->是我继续写作的动力。
由于博主能力有限,文中可能存在描述不正确,欢迎指正、补充!
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。