当用 java -jar yourJarExe.jar 来运行一个经过打包的应用程序的时候,你会发现如何设置 - classpath 参数应用程序都找不到相应的第三方类,报 ClassNotFound 错误。实际上这是由于当使用 - jar 参数运行的时候,java VM 会屏蔽所有的外部 classpath, 而只以本身 yourJarExe.jar 的内部 class 作为类的寻找范围。
** 解决方案 **
一 BootStrap class 扩展方案
Java 命令行提供了如何扩展 bootStrap 级别 class 的简单方法.
-Xbootclasspath: 完全取代基本核心的 Java class 搜索路径.
不常用, 否则要重新写所有 Java 核心 class
-Xbootclasspath/a: 后缀在核心 class 搜索路径后面. 常用!!
-Xbootclasspath/p: 前缀在核心 class 搜索路径前面. 不常用, 避免
引起不必要的冲突.
语法如下:
(分隔符与 classpath 参数类似,unix 使用: 号, windows 使用; 号,这里以 unix 为例)
java -Xbootclasspath/a:/usrhome/thirdlib.jar: -jar yourJarExe.jar
二 extend class 扩展方案
Java exten class 存放在 {Java_home}/jre/lib/ext 目录下. 当调用 Java 时, 对扩展 class 路径的搜索是自动的. 总会搜索的. 这样, 解决的方案就很简单了, 将所有要使用的第三方的 jar 包都复制到 ext 目录下.
三 User class 扩展方案
当使用 - jar 执行可执行 Jar 包时, JVM 将 Jar 包所在目录设置为 codebase 目录, 所有的 class 搜索都在这个目录下开始. 所以如果使用了其他第三方的 jar 包, 一个比较可以接受的可配置方案, 就是利用 jar 包的 Manifest 扩展机制.
步骤如下:
1. 将需要的第三方的 jar 包, 复制在同可执行 jar 所在的目录或某个子目录下. 比如: jar 包在 /usrhome/yourJarExe.jar 那么你可以把所有 jar 包复制到 / usrhome 目录下或 / usrhome/lib 等类似的子目录下.
2. 修改 Manifest 文件
在 Manifest.mf 文件里加入如下行
Class-Path:classes12.jar lib/thirdlib.jar
Class-Path 是可执行 jar 包运行依赖的关键词. 详细内容可以参考 。要注意的是 Class-Path 只是作为你本地机器的 CLASSPATH 环境变量的一个缩写,也就是说用这个前缀表示在你的 jar 包执行机器上所有的 CLASSPATH 目录下寻找相应的第三方类 / 类库。你并不能通过 Class-Path 来加载位于你本身的 jar 包里面(或者网络上)的 jar 文件。因为从理论上来讲,你的 jar 发布包不应该再去包含其他的第三方类库(而应该通过使用说明来提醒用户去获取相应的支持类库)。如果由于特殊需要必须把其他的第三方类库(jar, zip, class 等)直接打包在你自己的 jar 包里面一起发布,你就必须通过实现自定义的 ClassLoader 来按照自己的意图加载这些第三方类库。
以上三种方法推荐第一种,扩展性好,操作起来也最方便.
另外编写自己的 ClassLoader, 来动态载入 class, 是更加复杂和高级技术. 限于篇幅, 不赘述. 有兴趣了解可以去 google 一下 custom classloader,或者参考我的另一篇日志:。
Java 的安全机制随不同的 JDK 版本有不同的变化, 会影响很多核心 CLASS, 比如 Thread, 所以很多大型商业软件, 要求 JDK 的版本很严格. 部分原因也在此. 这也要求在发布自己编写的应用时候, 不管大小, 都要说明开发和测试的 JDK 版本.
本文所述方法测试基于 j2sdk 1.4.2_04-b05
----------------------------------------------------------------------------------------------
附: 背景知识
自 JDK 1.2 以后, JVM 采用了委托 (delegate) 模式来载入 class.采用这种设计的原因可以参考
归纳来讲: 是基于 JVM sandbox(沙盒) 安装模型上提供应用层的可定制的安全机制.
Java 虚拟机 (JVM) 寻找 Class 的顺序
1. Bootstrap classes
属于 Java 平台核心的 class, 比如 java.lang.String 等. 及 rt.jar 等重要的核心级别的 class. 这是由 JVM Bootstrap class loader 来载入的. 一般是放置在 {java_home}/jre/lib 目录下
2. Extension classes
基于 Java 扩展机制, 用来扩展 Java 核心功能模块. 比如 Java 串口通讯模块 comm.jar. 一般放置在 {Java_home}/jre/lib/ext 目录下
3. User classes
开发人员或其他第三方开发的 Java 程序包. 通过命令行的 - classpath 或 - cp, 或者通过设置 CLASSPATH 环境变量来引用. JVM 通过放置在 {java_home}/lib/tools.jar 来寻找和调用用户级的 class. 常用的 javac 也是通过调用 tools.jar 来寻找用户指定的路径来编译 Java 源程序. 这样就引出了 User class 路径搜索的顺序或优先级别的问题.
3.1 缺省值: 调用 Java 或 javawa 的当前路径 (.), 是开发的 class 所存在的当前目录
3.2 CLASSPATH 环境变量设置的路径. 如果设置了 CLASSPATH, 则 CLASSPATH 的值会覆盖缺省值
3.3 执行 Java 的命令行 - classpath 或 - cp 的值, 如果制定了这两个命令行参数之一, 它的值会覆盖环境变量 CLASSPATH 的值
3.4 -jar 选项: 如果通过 java -jar 来运行一个可执行的 jar 包, 这当前 jar 包会覆盖上面所有的值. 换句话说,-jar 后面所跟的 jar 包的优先级别最高, 如果指定了 - jar 选项, 所有环境变量和命令行制定的搜索路径都将被忽略. JVM APPClassloader 将只会以 jar 包为搜索范围.
有关可执行 jar 有许多相关的安全方面的描述, 可以参考 来全面了解.
这也是为什么应用程序打包成可执行的 jar 包后, 不管你怎么设置 classpath 都不能引用到第三方 jar 包的东西了
注意
有时候我们需要从 classpath 中读取 properties 文件, 对于单独运行的 jar 包, 需要在 jar/META-INF/MANIFEST.MF 文件里设置 classpath, 这样程序才能从 classpath 中加载文件:
Manifest-Version: 1.0
Implementation-Title:
Implementation-Version: 1.0-SNAPSHOT
Implementation-Vendor-Id:
Built-By: test
Build-Jdk: 1.7.0_75
Class-Path: classes/ lib/core-renderer-R8.jar
Created-By: Apache Maven
Main-Class: batch.BatchMain
Archiver-Version: Plexus Archiver
对于运行 jar 包, 在环境变量里设置的 classpath 是无效的
Suppose we have a package called containing the classes:
and the files defining this package are stored physically under the directory (on) or (on).
The file structure will look like this:
|
|
When we invoke Java, we specify the name of the application to run: org.mypackage.HelloWorld. However we must also tell Java where to look for the files and directories defining our package. So to launch the program, we use the following command:
|
|
where:
Note that if we ran Java in D:\myprogram\ (on Linux, /home/user/myprogram/) then we would not need to specify the classpath since Java implicitly looks in the for files containing classes.
In and higher, one can add all jar-files in a specific directory to the classpath using wildcard notation.
Windows example:
- java -classpath ".;c:\mylib\*" MyApp
Linux example:
- java -classpath '.:/mylib/*' MyApp
The named CLASSPATH may be alternatively used to set the classpath. For the above example, we could also use on Windows:
Sometimes you have to check the JAVA_HOME also, if it is pointing towards the right JDK version
- set CLASSPATH=D:\myprogram
- java org.mypackage.HelloWorld
Now, suppose the program uses a supporting library enclosed in a called , physically in the directory.
The corresponding physical file structure is :
- D:\myprogram |
- ---> lib |
- ---> supportLib.jar
- |
- ---> org |
- --> mypackage |
- ---> HelloWorld.class
- ---> SupportClass.class
- ---> UtilClass.class
We should use the following :
- java -classpath D:\myprogram;D:\myprogram\lib\supportLib.jar org.mypackage.HelloWorld
or alternatively:
- set CLASSPATH=D:\myprogram;D:\myprogram\lib\supportLib.jar
- java org.mypackage.HelloWorld
Suppose that our program has been enclosed in a called , put directly in the directory. We have the following file structure:
- D:\myprogram |
- ---> helloWorld.jar
- |
- ---> lib\
- |
- ---> supportLib.jar
The defined in this has this definition:
- Main-Class: org.mypackage.HelloWorld
- Class-Path: lib/supportLib.jar
Note: It's important that the ends with either a new line or carriage return.
Also, note that the classpath string in this case describes the location of the supportLib.jar file relative to the location of the helloWorld.jar file, and not as an absolute file path (as it might be when setting the -classpath parameter on the command line, for example). Thus, the actual locations of the jar file and its support library are irrelevant so long as the relative directory structure between the two is preserved.
To launch the program, we can use the following command:
- java -jar D:\myprogram\helloWorld.jar
It is not necessary to define the Classpath to the program classes, or the support library classes, because it is already defined in the.
Caution, it is useless to define the Main class at launch, the manifest of the JAR file must contain a line of the form
- Main-Class: classname
in order for the -jar option to work .
The syntax for specifying multiple library JAR files in the is to separate the entries with a space:
- Class-Path: lib/supportLib.jar lib/supportLib2.jar
Being closely associated with the file system, the Classpath syntax depends on the operating system. For example:
This does not apply when the Classpath is defined in , where each file path must be separated by a space (""), regardless of the operating system.
Application programmers may want to find out/debug the current settings under which the application is running:
- System.getProperty("java.class.path")
按照 developrworks 上说的:
创建可执行 JAR
来个简单的:
java -Djava.ext.dirs=m:\test -jar test.jar
相关的 jar(依赖的 jar) 目录均在 m:\test 下,test.jar 就是需要运行的 jar
来源: http://www.bubuko.com/infodetail-1969691.html