java中默认提供了三类classloader,分别加载不同目录下的class或者jar包,如果有一些非通用的需求,比如想从一个特定的位置加载class,如http地址、网盘、U盘等;又比如想对class做一些隔离;这个时候,默认的classloader就不能满足要求了,需要自定义classloader
自定义classloader很简单,
(1)继承java.lang.ClassLoader
(2)重写父类相关的方法
先看下ClassLoader的相关代码,java.lang.ClassLoader#loadClass(java.lang.String, boolean)
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
可以看到,jvm是根据一定的顺序来查找特定的class,如果父类或者bootstrapclassloader找不到,最终会调用findClass来加载指定的class,所以,只需重写该方法即可
网上的实例代码:https://blog.youkuaiyun.com/xyang81/article/details/7292380
import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.URL; /** * 加载网络class的ClassLoader */ public class NetworkClassLoader extends ClassLoader { private String rootUrl; public NetworkClassLoader(String rootUrl) { this.rootUrl = rootUrl; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class clazz; //根据类的二进制名称,获得该class文件的字节码数组 byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); } //将class的字节码数组转换成Class类的实例 clazz = defineClass(name, classData, 0, classData.length); return clazz; } private byte[] getClassData(String name) { InputStream is = null; try { String path = classNameToPath(name); URL url = new URL(path); byte[] buff = new byte[1024*4]; int len = -1; is = url.openStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); while((len = is.read(buff)) != -1) { baos.write(buff,0,len); } return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch(Exception e) { } } } return null; } private String classNameToPath(String name) { return rootUrl + "/" + name.replace(".", "/") + ".class"; } }