• 1.4 打破双亲委派机制


    什么是打破双亲委派机制呢?

    那么这里第一步, 我们需要知道什么是双亲委派机制?

    前面已经说了什么是双亲委派机制了, 那打破是怎么回事呢?

     比如, 我现在有一个自定义类加载器, 加载的是~/com/lxl/jvm/User1.class类, 而在应用程序的target目录下也有一个com/lxl/jvm/User1.class,  那么, 最终User1.class这个类将被哪个类加载器加载呢? 根据双亲委派机制, 我们知道, 他一定是被应用程序类加载器AppClassLoader加载, 而不是我们自定义的类加载器, 为什么呢? 因为他要向上寻找, 向下委托. 当找到了以后, 便不再向后执行了. 

    我们要打破双亲委派机制, 就是要让自定义类加载器来加载我们的User1.class, 而不是应用程序类加载器来加载

    接下来分析, 如何打破双亲委派机制呢? 双亲委派机制是在哪里实现的? 是在ClassLoader类的loadClass(...)方法实现的. 如果我们不想使用系统自带的双亲委派模式, 只需要重新实现ClassLoader的loadClass(...)方法即可. 下面是ClassLoader中定义的loadClass()方法. 里面实现了双亲委派机制

    下面给DefinedClassLoaderTest.java增加一个loadClass方法, 拷贝上面的代码即可. 删除掉中间实现双亲委派机制的部分

     这里需要注意的是, com.lxl.jvm是自定义的类包, 只有我们自己定义的类才从这里加载. 如果是系统类, 依然使用双亲委派机制来加载.

    来看看运行结果:

    调用了user1的sout方法
    com.lxl.jvm.DefinedClassLoaderTest

    现在User1方法确实是由自定义类加载器加载的了

     源码:

    package com.lxl.jvm;
    
    import java.io.FileInputStream;
    import java.lang.reflect.Method;
    
    /**
     * 自定义的类加载器
     */
    public class DefinedClassLoaderTest extends ClassLoader{
    
        private String classPath;
    
        public DefinedClassLoaderTest(String classPath) {
            this.classPath = classPath;
        }
    
        /**
         * 重写findClass方法
         *
         * 如果不会写, 可以参考URLClassLoader中是如何加载AppClassLoader和ExtClassLoader的
         * @param name
         * @return
         * @throws ClassNotFoundException
         */
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadBytes(name);
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
    
        private byte[] loadBytes(String name) throws Exception {
            // 我们需要读取类的路径
            String path = name.replace('.', '/').concat(".class");
            //String path = "";
            // 去路径下查找这个类
            FileInputStream fileInputStream = new FileInputStream(classPath + "/"  + path);
            int len = fileInputStream.available();
    
            byte[] data = new byte[len];
            fileInputStream.read(data);
            fileInputStream.close();
    
            return data;
        }
    
        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) {
                    /**
                     * 直接执行findClass()...什么意思呢? 首先会使用自定义类加载器加载类, 不在向上委托, 直接由
                     * 自己执行
                     *
                     * jvm自带的类还是需要由引导类加载器自动加载
                     */
                    if (!name.startsWith("com.lxl.jvm")) {
                        c = this.getParent().loadClass(name);
                    } else {
                        c = findClass(name);
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    
        public static void main(String[] args) throws Exception {
            DefinedClassLoaderTest classLoader = new DefinedClassLoaderTest("/Users/luoxiaoli");
            Class<?> clazz = classLoader.loadClass("com.lxl.jvm.User1");
            Object obj = clazz.newInstance();
            Method sout = clazz.getDeclaredMethod("sout", null);
            sout.invoke(obj, null);
            System.out.println(clazz.getClassLoader().getClass().getName());
        }
    
    } 
  • 相关阅读:
    Js获取当前日期时间及其它操作
    Java JDBC 基础知识
    java自动创建多级目录
    [Java]读取文件方法大全
    table固定首行(二)
    table固定首行(一)
    DIV滚动条
    查看本机开放的端口号,查看某个端口号是否被占用,查看被占用的端口号被哪个进程所占用,如何结束该进程
    DWZ SSH2 菜单树--使用Struts2 标签(iterator/set/if 组合使用)
    synchronized与lock,哪个效率更高
  • 原文地址:https://www.cnblogs.com/ITPower/p/13211490.html
Copyright © 2020-2023  润新知