Executable Jar with depedencies

2020-02-15 07:04发布

问题:

I'm trying to build an executable jar from the command line (I don't want to use ant or OneJar ) . Here is the content of my file.jar:

jar tvf file.jar
     0 Mon Sep 20 17:16:12 CEST 2010 lib/
 45396 Mon Sep 20 17:16:12 CEST 2010 lib/org.apache.commons.logging_1.0.4.v201005080501.jar
321330 Mon Sep 20 17:16:12 CEST 2010 lib/org.apache.commons.httpclient_3.1.0.v201005080502.jar
 55003 Mon Sep 20 17:16:12 CEST 2010 lib/org.apache.commons.codec_1.3.0.v20100518-1140.jar
     0 Mon Sep 20 19:15:00 CEST 2010 META-INF/
   265 Mon Sep 20 19:12:44 CEST 2010 META-INF/MANIFEST.MF
530609 Mon Sep 20 17:16:12 CEST 2010 ped.jar

the content of META-INF/MANIFEST.MF:

Manifest-Version: 1.0
Class-Path: ped.jar lib/org.apache.commons.codec_1.3.0.v20100518-1140.jar lib/org.apache.commons.httpclient_3.1.0.v201005080502.jar lib/org.apache.commons.logging_1.0.4.v201005080501.jar
Main-Class: fr.inserm.umr915.bomcat.ped.PedigreeDrawer

and the ped.jar do contains the Main-Class

jar tvf ped.jar | grep PedigreeDrawer.class
 39541 Mon Sep 20 17:16:10 CEST 2010 fr/inserm/umr915/bomcat/ped/PedigreeDrawer.class

but when I'm trying to execute the file.jar, I got an error:

java -jar file.jar
Exception in thread "main" java.lang.NoClassDefFoundError: fr/inserm/umr915/bomcat/ped/PedigreeDrawer
Caused by: java.lang.ClassNotFoundException: fr.inserm.umr915.bomcat.ped.PedigreeDrawer
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)

So, I'm missing something here. Can't I package an executable jar by just including the dependencies ?

Thanks for your help

Pierre

回答1:

As already mentioned the jars in the Class-Path: attribute are relative to the loaction of the jar file and not inside the jar file. This is probably a remnant from the days Java was meant to run applets.

You'll need to use a tool like maven uberjar to clobber all needed classes together in a single jar to create a standalone executable jar.

You can of course also just unzip all jars in a single directory, add the META-INF and manifest and zip everything up again if you absolutely want to avoid using tools.



回答2:

what is file.jar?
you can try set class-path like this:

Class-Path: lib/org.apache.commons.codec_1.3.0.v20100518-1140.jar lib/org.apache.commons.httpclient_3.1.0.v201005080502.jar lib/org.apache.commons.logging_1.0.4.v201005080501.jar

and run your jar file like this:

java -jar ped.jar


回答3:

You cannot include a Jar inside of a Jar without special packaging (e.g. OneJar, etc).

If you really do not want to use OneJar (or other similar tools), you will need to include multiple Jar files in your distributable.



回答4:

The value "Class-Path" in your file manifest.mf specifies your classpath as if you would start your application without the -jar but with -classpath lib/... parameter. The pathes are interpreted locally and not within your jar file.

An alternative is using one-jar or using a special classloader that looks into the jar files contained in your outer jar file.

With JCL you can load your classes directly from embedded jars like this:

  JarClassLoader jcl=new JarClassLoader();
  jcl.add("myjar.jar"); // Add some class source

  jcl.getSystemLoader().setOrder(1); // Look in system class loader first
  jcl.getLocalLoader().setOrder(2); // if not found look in local class loader
  jcl.getParentLoader().setOrder(3); // if not found look in parent class loader
  jcl.getThreadLoader().setOrder(4); // if not found look in thread context class loader
  jcl.getCurrentLoader().setOrder(5); // if not found look in current class loader

  // A custom class loader that extends org.xeustechnologies.jcl.ProxyClassLoader
  MyLoader loader=new MyLoader();
  loader.setOrder(6);

  jcl.addLoader(loader); //Add custom loader

But then you have to add jcl to your classpath instead... or add an exploded jcl to your jar.



回答5:

OneJar is meant to solve this kind of problem. You may write a custom classloader to same effect.