jvm双亲委派源码,自己看,自己写ClassLoader

本文解析了Java类加载过程中的loadClass方法原理,展示了默认的双亲委派机制,并演示如何通过自定义ClassLoader打破这种机制。重点讲解了findClass和defineClass的作用,以及如何在实际场景中实现类的加载和替换。
摘要由CSDN通过智能技术生成

请添加图片描述
其中BootStrapClassLoader加载jre/lib/rt.jar resources.jar charsets.jar
ExtClassLoader加载jre/ext/
AppClassLoader加载其他的类

找到sum.misc.Launcher
在这里插入图片描述
在AppClassLoader.loadClass处打断点,随便执行个自己定义的类中的main方法
在这里插入图片描述
跟到核心类ClassLoader.loadClass,我先贴个图
我们先是从AppClassLoader进入

    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 {
                	//AppClassLoader的父类是ExtClassLoader,依次往上
                    if (parent != null) {
                    	//父类的loadClass方法也是调用的ClassLoader.loadClass
                        c = parent.loadClass(name, false);
                    } else {
                    	//跟到了BootStrapClassLoader没有父类了,调用c++代码找class
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                
				//首先是ExtClassLoader中的c == null然后执行,返回
				//接着是AppClassLoader中的c == null
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //最终AppClassLoader中执行findClass找到了我们自定义的类
                    //看下文
                    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;
	}

跳转到URLClassLoader给出的findClass
这一整个findClass的逻辑就是从路径中找Class文件,然后交给defineClass去转化字节码为类

protected Class<?> findClass(final String name)
        throws ClassNotFoundException
    {
        final Class<?> result;
        try {
            result = AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
                    public Class<?> run() throws ClassNotFoundException {
                        String path = name.replace('.', '/').concat(".class");
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                            	//最终执行defineClass方法
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            return null;
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
        if (result == null) {
            throw new ClassNotFoundException(name);
        }
        return result;
    }

如果想打破双亲委派就需要自己写ClassLoader类覆盖掉loadClass方法
自己写ClassLoader打破双亲委派试一下

package com.example.learn.learnjvm;
//import sum.misc.Launcher.AppClassLoader;

import java.io.IOException;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader {
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException{
        try{
            String filename = name.substring(name.lastIndexOf(".") + 1) + ".class";
            InputStream ls = getClass().getResourceAsStream(filename);
            if(ls == null){
                throw new ClassNotFoundException(name);
            }
            byte[] b = new byte[ls.available()];
            ls.read(b);
            
            return defineClass(name, b, 0, b.length);
        } catch (IOException e) {
            throw new ClassNotFoundException(name);
        }
    }
}

其中defineClass是jvm把二进制数据转换成类的方法。
测试类这样执行

public class Test {

    public Test() throws ClassNotFoundException {
        MyClassLoader mcl = new MyClassLoader();
        Class clazz = mcl.loadClass("com.example.learn.learnjvm.Test");
        System.out.println(clazz.getClassLoader());
    }

    public static void main(String[] args) throws ClassNotFoundException {
        new Test();
    }
}

报错,看来找不到java.lang.Object,也就是说最基本的类都没有初始化
在这里插入图片描述

loadClass替换成findClass就好了

public class MyClassLoader extends ClassLoader {
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException{
        try{
            String filename = name.substring(name.lastIndexOf(".") + 1) + ".class";
            InputStream ls = getClass().getResourceAsStream(filename);
            if(ls == null){
                throw new ClassNotFoundException(name);
            }
            byte[] b = new byte[ls.available()];
            ls.read(b);
            
            return defineClass(name, b, 0, b.length);
        } catch (IOException e) {
            throw new ClassNotFoundException(name);
        }
    }
}

测试类


public class Test {

    public Test() throws ClassNotFoundException {
        MyClassLoader mcl = new MyClassLoader();
        Class clazz = mcl.findClass("com.example.learn.learnjvm.Test");
        System.out.println(clazz.getClassLoader());
    }

    public static void main(String[] args) throws ClassNotFoundException {
        new Test();
    }
}

ok
在这里插入图片描述

总结

loadClass() 主要进行类加载的方法,默认的双亲委派机制就实现在这个方法中
findClass() 根据名称或位置加载.class字节码
definClass() 把字节码转化成java.lang.Class

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值