通过加参数调用 javac,可以在编译时使用自定义的 ClassLoader 来完成类的定位加载:
javac -J-Djava.system.class.loader=my.ClassLoaderImpl theSource.java
自定义 ClassLoader 的方式可以参考这里(基本上来说就是通过继承 findClass(String) 方法来使JVM按照自己期望的规则来加载类)。还处于beta阶段的 Mustang Java SE 6 据说将提供新的工具 javax.tools.JavaCompilerTool 来替代 javac ,这个全新的编译工具应该会给程序员更好的编译体验。
大多数流行的 java IDE 都大大简化了对于 classpath 的配置过程,以至于很多在程序员的开发环境运行良好的代码发布之后才出现各种各样的问题。所以要想让程序包能够顺利的执行于产品环境,我们通常的做法是写一堆的 bat 或 shell 脚本来完成各种运行参数的配置。而这些大段大段的脚本当中,对 classpath 的配置占了相当大的比重。
因此 Heikki Uusitalo 写了一个 BootLoader 类(原始代码似乎有些问题,这里修改了一下):将这个类作为引导程序去根据自定义的规则来加载所有必需的 jar 包等资源,然后再启动真正的 main 方法。把这个 BootLoader 类单独打包成一个 jar 文件,运行的时候只需要运行它即可。当然你可以根据实际情况修改读取的路径、规则等信息。
- import java.io.*;
- import java.net.*;
- import java.lang.reflect.*;
- public class BootLoader
- {
- public static void main(final String[] args) throws Exception
- {
- // check that the lib folder exists
- File libRoot = new File(LIB_FOLDER);
- if(!libRoot.exists()) {
- throw new Exception("No 'lib' folder exists!");
- }
- // read all *.jar files in the lib folder to array
- 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];
- // fill the urls array with URLs to library files found in libRoot
- for(int i = 0; i < libs.length; i++) {
- urls[i] = new URL("file",null,libs[i].getAbsolutePath());
- }
- // create a new classloader and use it to load our app.
- classLoader = new URLClassLoader(urls,
- Thread.currentThread().
- getContextClassLoader());
- // get the main method in our application to bring up the app.
- final Method mtd = classLoader.loadClass(APP_MAIN_CLASS).getMethod("main",
- new Class[] {String[].class});
- // Using thread to launch the main 'loop' so that the current Main method
- // can return while the app is starting
- new Thread(new Runnable()
- {
- public void run()
- {
- try {
- mtd.invoke(null,new Object[] {args});
- } // forward the args
- catch(Exception e) {
- throw new RuntimeException(e);
- }
- }
- },"AppMain").start();
- // Give the app some time to start before returning from main.
- // This doesn't delay the starting in any way
- Thread.sleep(1000);
- }
- private static final String LIB_FOLDER = "lib";
- private static final String APP_MAIN_CLASS = "com.my.application.MyTest";
- private static ClassLoader classLoader;
- }
import java.io.*; import java.net.*; import java.lang.reflect.*; public class BootLoader { public static void main(final String[] args) throws Exception { // check that the lib folder exists File libRoot = new File(LIB_FOLDER); if(!libRoot.exists()) { throw new Exception("No 'lib' folder exists!"); } // read all *.jar files in the lib folder to array 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]; // fill the urls array with URLs to library files found in libRoot for(int i = 0; i < libs.length; i++) { urls[i] = new URL("file",null,libs[i].getAbsolutePath()); } // create a new classloader and use it to load our app. classLoader = new URLClassLoader(urls, Thread.currentThread(). getContextClassLoader()); // get the main method in our application to bring up the app. final Method mtd = classLoader.loadClass(APP_MAIN_CLASS).getMethod("main", new Class[] {String[].class}); // Using thread to launch the main 'loop' so that the current Main method // can return while the app is starting new Thread(new Runnable() { public void run() { try { mtd.invoke(null,new Object[] {args}); } // forward the args catch(Exception e) { throw new RuntimeException(e); } } },"AppMain").start(); // Give the app some time to start before returning from main. // This doesn't delay the starting in any way Thread.sleep(1000); } private static final String LIB_FOLDER = "lib"; private static final String APP_MAIN_CLASS = "com.my.application.MyTest"; private static ClassLoader classLoader; }