-
什么是双亲委派
- java虚拟机中,任何一个类由加载他的类加载器和这个类一同来确立起唯一性
- JVM对类的唯一标识,可以简单理解为由ClassLoader id + PackageName + ClassName组成,因此在一个运行程序中有可能存在两个包名和类名完全一致的类,但是如果这两个类不是由一个ClassLoader加载,会被视为两个不同的类,此时无法将一个类的实例强制转化为另外一个类,这就是类加载器的隔离性
- 为了解决类加载器的隔离问题,JVM引入了双亲委派模型
-
双亲委派模式
-
任何一个类加载器在接到一个类的加载请求时,都会让其父类进行加载,只有父类无法加载(或者没有父亲)的情况下,才尝试自己加载
-
-
使用双亲委派机制,可以保证,每一个类只会有一个类加载器
- 例如:java最基础的Object类,他存放在rt.jar中,这是Bootstrap的职责范围,当向上委派到Booststrap时就会被加载
-
如果没有使用双亲委派模式,可以任由自定义加载器进行加载的话,java这些核心类的API就会被随意篡改,无法做到一致性加载效果
-
-
JDK中ClassLoader.loadClass()类加载器中的加载类的方法,源码如下
-
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); }
-
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 1.首先要保证线程安全 synchronized (getClassLoadingLock(name)) { // 2.先判断这个类是否被加载过,如果加载过,直接跳过 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { // 3.有父类,优先交给父类尝试加载;如果为空,使用BootstrapClassLoader类加载器 if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // 父类加载失败,这里捕获异常,但不需要做任何处理 } // 4.没有父类,或者父类无法加载,尝试自己加载 if (c == null) { 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; } }
-
-
如何自定义类加载器
-
针对某些场景,比如通过网络来传输JAVA类的字节码文件,为保证安全性,这些字节码经过了加密处理,这时系统提供的类加载器就无法对其进行加载,此时需要我们可以自定义一个类加载器来完成文件的加载
-
自定义类加载器需要继承classLoader类
-
public class CustomClassLoader extends ClassLoader { private String classPath; public CustomClassLoader(String classPath) { this.classPath = classPath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> c = findLoadedClass(name); if (c == null) { byte[] data = loadClassData(name); if (data == null) { throw new ClassNotFoundException(); } return defineClass(name, data, 0, data.length); } return null; } protected byte[] loadClassData(String name) { try { // package -> file folder name = name.replace(".", "//"); FileInputStream fis = new FileInputStream(new File(classPath + "//" + name + ".class")); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len = -1; byte[] b = new byte[2048]; while ((len = fis.read(b)) != -1) { baos.write(b, 0, len); } fis.close(); return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } }
-
测试类如下
public class ClassLoaderTest { public static void main(String[] args) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); System.out.println("current loader:" + loader); } }
-
将ClassLoaderTest.java源文件放在指定的目录下,并通过javac命令编译成ClassLoaderTest.class,最后进行测试
-
public class CustomClassLoaderTest { public static void main(String[] args) throws Exception { String classPath = "/Downloads"; CustomClassLoader customClassLoader = new CustomClassLoader(classPath); Class<?> testClass = customClassLoader.loadClass("com.example.ClassLoaderTest"); Object obj = testClass.newInstance(); System.out.println(obj.getClass().getClassLoader()); } } 输出结果:com.example.CustomClassLoader@60e53b93
-
在实际使用过程中,最好不要重写loadClass方法,避免破坏双亲委派模型
-
-
总结
- 双亲委派,指的是在接受类加载请求时,会让父类加载器试图加载该类,只有在父类加载器无法加载该类或者没有父类时,才会尝试从自己的类路径中加载该类
- 针对某些场景,如果要实现类的隔离,可以自定义类加载器来实现特定类的加载
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交