双亲委派模型如何保证类加载的安全性?

  • Post category:Java

双亲委派模型是Java中一种用于保证类加载安全性的机制,它通过在一个类加载器无法找到类时委派给其父加载器来保证类的唯一性,并避免了类的重复加载。具体来说,当一个类加载器需要加载某个类时,它会首先将该请求委派给父加载器,一直到最顶层的父加载器(通常为Bootstrap ClassLoader)无法加载该类,才会由当前类加载器自己去加载该类。这种机制使得每一个类只能被加载一次,避免了类的重复加载,从而保证了类的唯一性,保证类加载的安全性。

以下是两个示例说明:

示例一

我们来看一个简单的代码示例:

public class Test {

    public static void main(String[] args) {
        MyClassLoader classLoader1 = new MyClassLoader("classLoader1");
        MyClassLoader classLoader2 = new MyClassLoader("classLoader2");

        try {
            // 通过classLoader1加载Test类
            Class<?> clazz1 = classLoader1.loadClass("Test");
            System.out.println("classLoader1 load Test success!");
            System.out.println("clazz1: " + clazz1.getClassLoader());

            // 通过classLoader2加载Test类
            Class<?> clazz2 = classLoader2.loadClass("Test");
            System.out.println("classLoader2 load Test success!");
            System.out.println("clazz2: " + clazz2.getClassLoader());

            // 判断两个类是否相等,输出结果为false
            System.out.println("clazz1 equals clazz2 ? " + (clazz1 == clazz2));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

class MyClassLoader extends ClassLoader {

    public MyClassLoader(String name) {
        super(null);
        this.name = name;
    }

    private String name;

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    private byte[] getClassData(String className) {
        String path = classNameToPath(className);
        // 从指定路径中读取字节码文件到byte数组中
        byte[] data = new byte[0];
        // ...
        return data;
    }

    private String classNameToPath(String className) {
        return className.replace(".", "/") + ".class";
    }

}

在这个示例中,我们自定义了两个类加载器MyClassLoader1和MyClassLoader2,通过分别使用这两个类加载器来加载同一个Test类,我们可以看到输出结果为false,这是因为双亲委派模型保证了类的唯一性,同一个类加载器只会加载一个Class对象,不同的类加载器加载同一个类也会产生不同的Class对象,因此这两个对象不相等。这就是双亲委派模型保证类加载的安全性的一个例子。

示例二

再来看一个更加现实的场景。我们知道,很多Java企业应用系统都是采用分布式部署架构的,将一个系统拆分成多个不同的模块,部署在不同的服务器上。如果不使用双亲委派模型,那么在不同的模块之间,同一个类可能会被不同的类加载器加载,导致多个相同的Class对象存在于系统中,这将会引发各种问题,如ClassCastException等。

但是,借助双亲委派模型,我们可以在不同的模块之间保证类加载的唯一性和安全性。例如,我们在Tomcat容器中部署了两个Web应用,分别是A和B,那么Tomcat就会分别为这两个Web应用分配两个不同的类加载器,每个类加载器都有自己的类加载路径。按照双亲委派模型的原理,当A应用需要加载一个类时,它会先向父类加载器(如Tomcat的公共类加载器)发送请求,该父类加载器会去查找是否已经加载过该类,如果没有,则会向上委托,一直传递到最顶层的类加载器(如Bootstrap ClassLoader),如果依然找不到,则会由自己的类加载器加载该类。同样地,当B应用需要加载同一个类时,也会按照同样的流程去查找类加载器并加载类。

由此可以看出,双亲委派模型通过不同类加载器之间的委派关系,保证了在不同的模块之间也能够保证类的唯一性和安全性。