可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I built a stand-alone Java application that has a bunch of dependencies (Apache Commons libs, etc) as well as a dependency on the Spring framework, which in turn has a bunch of dependencies.
I built this in Eclipse, where it runs fine. Now I need to deploy it to production and so I'm trying to figure out the best way to package it with all dependencies and also how to even invoke the thing (it will be invoked from the command line).
The brute-force way would be to export my project as a jar, find all the dependent jars (and their dependencies!), copy them to a common directory, write a shell script that includes every one on the classpath and run it. Obviously that seems stupid and tedious.
Should I use Ant, Maven? Should I package all of the dependent Spring jars into one big file? Any tips on dealing with this would be helpful.
回答1:
Java deployment is still stupid and tedious in 2009. So the usual option is to write this small script. If you put all your JARs into one directory (say, lib\
), you can use a common script which builds the classpath automatically (see this blog entry). I suggest to add a cd /d %~dp0
at the beginning to cd into the script's directory.
Maven 2 has a plugin which can assemble your application in one "super JAR" (mvn assembly:assembly). This works unless you use the DB2 JCC driver (which contains "COM.ibm." packages which get converted to com.ibm. -> ClassNotFoundException).
The last solution, if you have already an ANT build.xml which works, is to unpack all the dependent JARs and create a "super JAR" yourself.
回答2:
With Ant you could do something along the following:
- Group all dependencies together.
- Package a JAR-file with your application, and embed references to the dependencies in the MANIFEST.MF-file with the "Class-Path"-directive, inside the jar. That should eliminate the need for constructing the classpath at launch.
- Bundle it all together as an archive, and distribute it. It should be sufficient to run the jar directly with "java -jar your.jar".
For managing your dependencies, you could go with Ivy. Much like Maven, it is able to retrieve dependencies at build time from whichever repositories you configure, including Maven repositories or your own private repository.
回答3:
We found that the best solution for dependency management and packaging is to use Maven with the Assembly plugin. This provides a very elegant means to packaging you projects, along with all the runtime dependencies and supporting scripts into a binary distribution. One of the greatest advantages of using assemblies is that it is possible to split your projects into distinct components according to necessity. If you're using Spring Integration its quite common you would have separate components to be split over multiple machines. The assembly plugin allows you to configure this to your requirements.
回答4:
All this Maven love and no one mentions Ivy? Oh dear!
Well, here's Ant Ivy and here's comparison to Maven2. Please take a look at it too before going with almost everyone is suggesting.
回答5:
You can use a launcher that sets the classpath correctly. Then, you'll have much more flexibility to package your JARs. You could easily put them all in a directory, which is much cleaner than creating a "bastard" ueber jar ...
One of such launcher is commons-launcher.
回答6:
We are using Maven 2 with m2eclipse plugin; works pretty good. Plugin has a dependency graph view, will show conflicts etc.
Now, if you want to package all of your dependencies into the single jar, you can check out Jar Jar Links (no kidding!).
回答7:
A lot of your concerns disappear if you make your JAR executable and add a manifest including ClassPath. You do have to ZIP the JAR and its dependencies together, unfortunately, but your CLASSPATH problems are taken care of.
What about creating an OSGi bundle? I haven't looked into it myself yet, but it's supposed to be the next thing for packaging Java apps. It's used in app servers like Spring's dm Server. Perhaps it can help you as well.
回答8:
In my ANT build script, I generate the *.bat and *.sh startup scripts with the list of JARs dynamically built at build time based on the contents of the build's lib dir.
For instance, to create the list of jars with appropriate separators:
<path id="jar-classpath">
<fileset dir="${build.war.dir}/WEB-INF/lib">
<include name="**/*.jar" />
</fileset>
</path>
<pathconvert refid="jar-classpath" targetos="unix" property="exec.classpath.unix">
<map from="${build.war.dir}\WEB-INF\lib\" to="../lib/"/>
</pathconvert>
<pathconvert refid="jar-classpath" targetos="windows" property="exec.classpath.windows">
<map from="${build.war.dir}\WEB-INF\lib\" to="../lib/"/>
</pathconvert>
You can then use the ${exec.classpath.unix}
and ${exec.classpath.windows}
values in the java invocation scripts.
回答9:
The maven-assembly-plugin may not be the best choice. You should have a look at the onejar-maven-plugin, as described here:
http://blog.jayway.com/2009/03/22/executable-jar-with-onejar-maven-plugin/
It lets all your dependency jars stay jars, and your code is in its own jar. All of those jars are put in a bigger jar, which is made executable.
回答10:
You could also use Gradle and here is how you can do it there:
mainClassName = "my.App"
task runnableJar(type: Jar, dependsOn: [':assemble']) {
from files(sourceSets.main.output.classesDir)
from files(sourceSets.main.output.resourcesDir)
from configurations.runtime.asFileTree.files.collect { zipTree(it) }
manifest {
attributes 'Main-Class': mainClassName
}
}