[size=medium] 由于现在我们在用java做工程的时候普遍使用IDE来辅助我们开发,并且大多数流行的 java IDE 都大大简化了对于 classpath 的配置过程,以至于很多在程序员的开发环境运行良好的代码发布之后才出现各种各样的问题。所以为此我们需要所以要想让程序包能够顺利的执行于产品环境,我们通常的做法是写一堆的 bat 或 shell 脚本来完成各种运行参数的配置。而这些大段大段的脚本当中,对 classpath 的配置占了相当大的比重。
因此 Heikki Uusitalo 写了一个 BootLoader 类。将这个类作为引导程序去根据自定义的规则来加载所有必需的 jar 包等资源,然后再启动真正的 main 方法。把这个 BootLoader 类单独打包成一个 jar 文件,运行的时候只需要运行它即可。当然你可以根据实际情况修改读取的路径、规则等信息。
但是源码存在问题,所以在此我修改了一下。请注意下面这个代码片段
其实我们也是在实现了一一个sandox,所以这段代码也可以启发我们把它不仅应用在代替classpath这一个方面,也可以用到任何需要定制不同的ClassLoader来装载不同的类的情景中去。[/size]
因此 Heikki Uusitalo 写了一个 BootLoader 类。将这个类作为引导程序去根据自定义的规则来加载所有必需的 jar 包等资源,然后再启动真正的 main 方法。把这个 BootLoader 类单独打包成一个 jar 文件,运行的时候只需要运行它即可。当然你可以根据实际情况修改读取的路径、规则等信息。
但是源码存在问题,所以在此我修改了一下。请注意下面这个代码片段
Thread.currentThread().setContextClassLoader(classLoader);
,这个语句很有必要,因为我们是用自己定制的classloader来加载我们需要的类集合,并且由于我们把需要真正运行的入口类的main函数放到另外的线程中去而不是在主线程中,所以根据jvm类加载和查找的原理,如果不加入这个语句,则主动调用
Thread.currentThread().getContextClassLoader()
这样语句来查找被我们用自己定制的classloader加载的类则会找不到这个类。举例为,如果我们在使用JMS的时候使用本文提供的方法读入JMS使用到的jar包而注释掉上述提到的语句的时候,就会出现类找不到的错误,究其原因,就是因为在某些类初始化的时候会直接使用
Thread.currentThread().getContextClassLoader()
这样的语句来获得ClassLoader以来载入需要的类,但需要类却是在我们定制的classloader中被加载的,所以会出现找不到类的情况。但是为什么我们不通过这样显式的通过当前线程获得ClassLoader的方式,却能找到相应需要的类呢? 这是因为被放入新建线程通过反射被调用的入口类是被用我们定制的ClassLoader所装载的,并且由于jvm对类的采用了懒装载策略,用到的时候在查找装载,所以不会出现类找不到的情况。 具体代码如下:
package bootup;
import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* @author Loskey
*
*/
public class BootupLoader {
private static ClassLoader classLoader;
/**
* @param args
*/
public synchronized static void main(final String[] args) {
String jarlib = System.getProperty("jarlib", ".");
String app_main_class = System.getProperty("appmainclass");
try {
if (null == app_main_class) {
System.out.println("Application main class NOT assigned, please check it in!");
System.exit(1);
}
if (jarlib.equalsIgnoreCase(".")) {
System.out.println("Default value of jarlib to be used, which is the current directory!");
}
File libRoot = new File(jarlib);
if (!libRoot.exists()) {
System.out.println("No 'lib' folder exists!");
}
File[] libs = libRoot.listFiles(new FileFilter() {
public boolean accept(File dir) {
String name = dir.getName().toLowerCase();
return name.endsWith("jar") || name.endsWith("zip");
}
});
URL[] urls = new URL[libs.length];
for (int i = 0; i < libs.length; i++) {
urls[i] = new URL("file", null, libs[i].getAbsolutePath());
System.out.println(libs[i].getAbsolutePath());
}
classLoader = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
final Method mtd = classLoader.loadClass(app_main_class).getMethod("main", new Class[] { String[].class });
new Thread(new Runnable() {
public void run() {
try {
synchronized (BootupLoader.class) {
Thread.currentThread().setContextClassLoader(classLoader); mtd.invoke(null, new Object[] { args });
BootupLoader.class.notifyAll();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}, "AppMain").start();
BootupLoader.class.wait();
} catch (java.net.MalformedURLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
System.out.println("Application main Class NOT found!");
} catch (NoSuchMethodException e) {
System.out.println("The main method of application main class NOT found");
} catch (SecurityException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
其实我们也是在实现了一一个sandox,所以这段代码也可以启发我们把它不仅应用在代替classpath这一个方面,也可以用到任何需要定制不同的ClassLoader来装载不同的类的情景中去。[/size]