今天偶然看到一篇Java技术栈发表的文章,关于自定义类加载器的实现感觉有点问题。在此做一个梳理。
原文链接:https://www.jianshu.com/p/e808ed28a5d6
本文代码示例来自原文,稍作修改。
我们看看原文的例子:
疑问:第一时间感觉两处“return super.loadClass(name);”应该直接“return null”,不然在找不到类的情况下会死循环?!
接下来做了一番分析和验证。
自定义加载器使用中我们调用ClassLoader.loadClass加载一个类,来loadClass关于类加载双亲委托模型的实现:
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//判断当前类是否已经被加载,调用本地方法findLoadedClass0
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//双亲委托模型:父加载器不为空,调用父加载器加载,否则查看启动类加载器是否已加载该类
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//调用本地方法findBootstrapClass
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//父加载器或者启动类加载器都未加载该类,则调用本加载器的findClass方法
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;
}
}
findClass在ClassLoader中并未实现,须用户在自定义加载器中实现:
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
图解双亲委托模型:
image.png
一句话总结:自底向上委托,自顶向下加载。
我们再回过头来分析找不到类的情况下代码的调用过程:
LocalClassLoader.loadClass => 父加载器返回null => LocalClassLoader.findClass失败 => super.loadClass === LocalClassLoader.loadClass
代码进入死循环
本地验证:
public class LocalClassLoader extends ClassLoader {
private String path = "F:/study/";
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> cls = findLoadedClass(name);
if (cls != null) {
return cls;
}
if (!name.endsWith(".Key")) {
return super.loadClass(name);
}
try {
InputStream is = new FileInputStream(path + name.replace(".", "/") + ".class");
byte[] bytes = IOUtils.readNBytes(is, is.available());
return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
return super.loadClass(name);
}
public static void main(String[] args) {
try {
LocalClassLoader lcl = new LocalClassLoader();
Class<?> cls = lcl.loadClass("com.yalin.test.Key"); //1
//Class<?> cls = lcl.loadClass("com.yalin.test.jdk.Key"); //2
Field field = cls.getDeclaredField("key");
field.setAccessible(true);
Object value = field.get(cls.newInstance());
System.out.println(value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
加载F:/study/目录存在的类com.yalin.test.Key,输出成功:
image.png
加载F:/study/目录不存在的类com.yalin.test.jdk.Key,代码进入死循环:
image.png
转载请备注原文链接。
网友评论