大家应该都听过热部署。热部署可以使应用程序在不全部重新编译,不停止服务的情况下,增加新功能,修复bug。使用Java如何实现这样的功能呢?动态加载类功不可没。
先给出一段简单的代码。该程序会修改classpath来增加运行时的jar包,并使用URLClassLoader来加载指定的类,以达到热部署的效果。
p.s. 简化处理的代码片段,不能处理UNC URL,获取home path的时候不考虑Mac平台的特殊性。诸多缺陷仅供演示。
package com.joshua.test;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
public class ClassLoaderTest {
private static final String CLASSPATH_SEPARATOR = System.getProperty("path.separator");
private static ClassLoader classLoader;
static {
String classPath = System.getProperty("java.class.path");
String homePath = getHomePath(classPath);
File[] jars = new File(homePath + "/lib").listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return dir.canRead() & name.endsWith(".jar");
}
});
// Add jars in lib dir into classpath.
final List<URL> jarPaths = new LinkedList<URL>();
StringBuilder classPathSB = new StringBuilder(classPath);
for (File jar : jars) {
try {
jarPaths.add(jar.toURI().toURL());
classPathSB.append(CLASSPATH_SEPARATOR);
classPathSB.append(jar.getPath());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
// Modify the classpath
System.setProperty("java.class.path", classPathSB.toString());
classLoader = AccessController.doPrivileged(
new java.security.PrivilegedAction<URLClassLoader>() {
@Override
public URLClassLoader run() {
return new URLClassLoader(jarPaths.toArray(new URL[jarPaths.size()]));
}
}
);
Class clazz = null;
try {
clazz = classLoader.loadClass("$全类名");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (clazz == null) System.exit(-1);
Object loadedInstance = null;
try {
loadedInstance = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (loadedInstance == null) System.exit(-1);
}
public static String getHomePath(String classPath) {
StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator);
String binPath = null;
if (st.countTokens() > 0) {
binPath = st.nextToken();
}
try {
return new File(binPath).getCanonicalFile().getParent();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}