java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java双亲委派机制

Java类加载策略之双亲委派机制全面分析讲解

作者:小王笃定前行

这篇文章主要介绍了Java双亲委派机制,双亲委派模型是Java加载类的机制,采用双亲委派模型的好处是Java类随着它的类加载器一起具备了一种带有优先级的层级关系,通过这种层级关系可以避免类的重复加载,感兴趣的朋友可以参考下

前言

ava虚拟机(JVM)的类加载机制是Java应用中不可或缺的一部分。本文将详细介绍JVM的双亲委派机制,并阐述各关键点。

一、什么是双亲委派机制

双亲委派机制(Parent-Delegate Model)是Java类加载器中采用的一种类加载策略。该机制的核心思想是:如果一个类加载器收到了类加载请求,默认先将该请求委托给其父类加载器处理。只有当父级加载器无法加载该类时,才会尝试自行加载。

二、类加载器与层级关系

Java中的类加载器主要有如下三种:

这三种类加载器之间存在父子层级关系。启动类加载器是最高级别的加载器,没有父加载器;扩展类加载器的父加载器是启动类加载器;应用类加载器的父加载器是扩展类加载器。

  除了以上三个内置类加载器,用户还可以通过继承 java.lang.ClassLoader 类自定义类加载器,根据实际需求处理类加载请求。

三、双亲委派机制作用及如何破环机制

通过上述两块内容,我们对双亲委派机制、加载流程及层级有了一些了解,这时我们不妨抛出几个疑问。

为什么需要双亲委派

1. 通过双亲委派机制,可以避免类的重复加载,当父加载器已经加载过某一个类时,子加载器就不会再重新加载这个类。

2. 通过双亲委派机制,可以保证安全性。因为BootstrapClassLoader在加载的时候,只会加载JAVA_HOME中的jar包里面的类,如java.lang.String,那么这个类是不会被随意替换的。

那么,就可以避免有人自定义一个有破坏功能的java.lang.String被加载。这样可以有效的防止核心Java API被篡改。

这是在JDK1.8的java.lang.ClassLoader类中的源码,这个方法就是用于加载指定的类。

实现双亲委派机制 的代码也都集中在这个方法之中:

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;
        }
    }

通过以上代码得出结论:

双亲委派机制的优缺点

优点:

缺点:

如何打破这个机制

既然上述文章中我们已经知道了双亲委派的实现方式,那么如何打破这个机制呢。

想要破坏这种机制,那么就需要自定义一个类加载器,继承ClassLoader类重写其中的loadClass方法,使其不进行双亲委派即可。

写个示例

import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
public class CustomClassLoader extends ClassLoader {
    // 自定义类加载器必须提供一个加载类文件的位置
    private String classesPath;
    public CustomClassLoader(String classesPath, ClassLoader parent) {
        super(parent);
        this.classesPath = classesPath;
    }
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        //首先,检查已加载的类
        Class<?> loadedClass = findLoadedClass(name);
        if (loadedClass == null) {
            // 如果已加载类中没有该类, 尝试用自定义的方法加载
            try {
                loadedClass = findClassInPath(name);
            } catch (ClassNotFoundException e) {
                // 如果自定义加载方法找不到类,则委托给父类加载器
                loadedClass = super.loadClass(name, resolve);
            }
        }
        if (resolve) {
            resolveClass(loadedClass);
        }
        return loadedClass;
    }
    private Class<?> findClassInPath(String className) throws ClassNotFoundException {
        try {
            String filePath = className.replace('.', '/') + ".class";
            byte[] classBytes = Files.readAllBytes(Paths.get(classesPath, filePath));
            return defineClass(className, classBytes, 0, classBytes.length);
        } catch (Exception e) {
            throw new ClassNotFoundException("Class not found in classes path: " + className, e);
        }
    }
    public static void main(String[] args) throws Exception {
        String pathToClasses = "/path/to/your/classes";
        String className = "com.example.SampleClass";
        String methodName = "sampleMethod";
        // 创建自定义类加载器实例,将类的加载权交给它
        CustomClassLoader customClassLoader = new CustomClassLoader(pathToClasses, CustomClassLoader.class.getClassLoader());
        // 使用自定义类加载器加载类
        Class<?> customClass = customClassLoader.loadClass(className);
        // 创建类的实例并调用方法
        Object obj = customClass.newInstance();
        Method method = customClass.getDeclaredMethod(methodName);
        method.setAccessible(true);
        method.invoke(obj);
    }
}

上面的示例代码中,我们重写了 loadClass 方法,先尝试通过 findClassInPath 从指定的路径加载类,如果无法加载就委托给父类加载器。这样,我们就实现了打破双亲委派机制的自定义类加载器。

以下是代码的详细解析:

有哪些工具选择了破坏机制

既然在上文中,我们已经清楚怎么打破双亲机制,那么有哪些工具选择了破坏机制呢?为什么?

这些工具和技术之所以要打破双亲委派机制,主要是出于以下原因:

需要注意的是,打破双亲委派机制可能会带来类加载冲突、安全性和性能等问题,因此在实际应用中要谨慎使用。

四、总结

本文介绍了JVM的双亲委派机制,包括概念、类加载器层级关系、双亲委派流程及实例分析等方面的内容。双亲委派机制可以确保Java应用类型安全,同时避免类加载冲突。在某些特定场景下,我们可以通过自定义类加载器对类加载策略进行调整,以满足应用特性和性能需求。

以上就是Java双亲委派机制全面分析讲解的详细内容,更多关于Java双亲委派机制的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文