03 类加载机制解析四:打破双亲委派机制

5 篇文章 0 订阅

1 打破双亲委派机制

上节自定义的类加载器,实现了加载指定目录的class,但是还是遵循了双亲委派机制

如何打破双亲委派机制
很简单,我们已经知道了双亲委派机制就是在ClassLoader的loadClass方法实现的,只要我们重写该方法就可以了呗

package study.wyy.jvm.classLoader;

import java.io.FileInputStream;

public class MyClassLoader extends ClassLoader {
    private String classPath;

    public MyClassLoader(String classPath) {
        this.classPath = classPath;
    }

    private byte[] loadByte(String name) throws Exception {
        name = name.replaceAll("\\.", "/");
        FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
        int len = fis.available();
        byte[] data = new byte[len];
        fis.read(data);
        fis.close();
        return data;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] bytes = loadByte(name);
            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }


    /**
     * @Description 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载
     * @Author wyaoyao
     * @Date 2020/9/3 9:33 下午
     * @Param
     * @Return
     * @Exception
     */
    @Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);

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

测试

package study.wyy.jvm.classLoader;



public class MyClassLoaderTest {

    public static void main(String[] args) throws Exception {
        //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader
        MyClassLoader classLoader = new MyClassLoader("/Users/wyaoyao/Documents/test");
        //test目录创建 study.wyy.jvm.model 几级目录,将TargetClass类的class文件TargetClass.class丢入该目录
        Class clazz = classLoader.loadClass("study.wyy.jvm.model.TargetClass");
        System.out.println(clazz.getClassLoader().getClass().getName());
    }
}

输出

/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=57488:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/tools.jar:/Users/wyaoyao/IdeaProjects/jvm/target/classes study.wyy.jvm.classLoader.MyClassLoaderTest
java.io.FileNotFoundException: /Users/wyaoyao/Documents/test/java/lang/Object.class (No such file or directory)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at study.wyy.jvm.classLoader.MyClassLoader.loadByte(MyClassLoader.java:14)
	at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:25)
	at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:635)
	at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:26)
	at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at study.wyy.jvm.classLoader.MyClassLoaderTest.main(MyClassLoaderTest.java:11)
Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Object
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:635)
	at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:26)
	at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at study.wyy.jvm.classLoader.MyClassLoaderTest.main(MyClassLoaderTest.java:11)
Caused by: java.lang.ClassNotFoundException
	at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:29)
	at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)

报错了,why呢
因为TargetClass会继承我们的Object类,加载TargetClass会去加载Object,但是我们自定义的ClassLoader加载的路径/Users/wyaoyao/Documents/test下面之后一个TargetClass,没有Object所以报错

如何解决
既然如此,我们可以复制一个Object类放到我们的自定义的ClassLoader加载的路径下,主要包名是java.lang

拷贝过去之后在运行

java.lang.SecurityException: Prohibited package name: java.lang
	at java.lang.ClassLoader.preDefineClass(ClassLoader.java:655)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:754)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:635)
	at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:26)
	at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:635)
	at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:26)
	at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at study.wyy.jvm.classLoader.MyClassLoaderTest.main(MyClassLoaderTest.java:11)
Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Object
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:635)
	at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:26)
	at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at study.wyy.jvm.classLoader.MyClassLoaderTest.main(MyClassLoaderTest.java:11)
Caused by: java.lang.ClassNotFoundException
	at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:29)
	at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	... 7 more

还是错误,这里就是java的安全机制,Object这种基础类怎么可能让你随便加载和修改的,只会允许被引导类加载器加载

如何办呢
只好判断一下只有是我们自己的包下的类study.wyy.jvm交给我们自定义类加载器去加载,Object这种还是由java自己的去加载

package study.wyy.jvm.classLoader;

import java.io.FileInputStream;

public class MyClassLoader extends ClassLoader {
    private String classPath;

    public MyClassLoader(String classPath) {
        this.classPath = classPath;
    }

    private byte[] loadByte(String name) throws Exception {
        name = name.replaceAll("\\.", "/");
        FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
        int len = fis.available();
        byte[] data = new byte[len];
        fis.read(data);
        fis.close();
        return data;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] bytes = loadByte(name);
            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }


    /**
     * @Description 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载
     * @Author wyaoyao
     * @Date 2020/9/3 9:33 下午
     * @Param
     * @Return
     * @Exception
     */
    @Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);

            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();
                if(!name.startsWith("study.wyy.jvm")){
                    // 自定义的类加载器的parent属性就是AppClassLoader
                    c = this.getParent().loadClass(name);
                }else {
                    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;
        }
    }
}

运行结果

study.wyy.jvm.classLoader.MyClassLoader
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值