阅读ClassLoader源码并自定classLoader

 工具要求:以下过程在Idea执行

对于类加载器,除了jvm自身的类加载器,还可以通过阅读源码,自定义自己的类加载器,spring框架中和tomcat都有属于自己的类加载器

开始工作

写一个 类继承ClassLoader,如下

/**
 * Created by admin on 2019/6/23.
 */
public class ClassLoaderTest01 extends  ClassLoader {
}

ctrl+鼠标点击类名ClassLoader进入

找到loadClass()

 /**
     * Loads the class with the specified <a href="#name">binary name</a>.  The
     * default implementation of this method searches for classes in the
     * following order:
     *
     * <ol>
     *
     *   <li><p> Invoke {@link #findLoadedClass(String)} to check if the class
     *   has already been loaded.  </p></li>
     *
     *   <li><p> Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method
     *   on the parent class loader.  If the parent is <tt>null</tt> the class
     *   loader built-in to the virtual machine is used, instead.  </p></li>
     *
     *   <li><p> Invoke the {@link #findClass(String)} method to find the
     *   class.  </p></li>
     *
     * </ol>
     *
     * <p> If the class was found using the above steps, and the
     * <tt>resolve</tt> flag is true, this method will then invoke the {@link
     * #resolveClass(Class)} method on the resulting <tt>Class</tt> object.
     *
     * <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link
     * #findClass(String)}, rather than this method.  </p>
     *
     * <p> Unless overridden, this method synchronizes on the result of
     * {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method
     * during the entire class loading process.
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @param  resolve
     *         If <tt>true</tt> then resolve the class
     *
     * @return  The resulting <tt>Class</tt> object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     */
    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;
        }
    }

看到

synchronized//避免同步重复问题

//First, check if the class has already been loaded这行注释

意思是在加载一个类之前先检查这个类是否已经被加载,他的方式类似递归向父加载器检查是否被加载  -----即双亲委派(app-ext-boot再boot-ext-app),如下代码

 如果最终都没有找到这个类被加载

就执行findClass()

但是,发现findClass直接抛出异常,

 从以上看出,我们只需要继承ClassLoader类并重写findClass()即可实现自定义的类加载器

那么如何自定义加载我们的类呢?

进入ClassLoader的实现类

我们可以看到以下两个类,他们分别负责加载不同路径下的class文件

ExtClassLoader
AppClassLoader

 extClassLoader加载的是java.ext.dirs路径下的

appClassLoader则加载的是java.class.path即我们常说的classpath路劲

通过以上的源码查看和分析,我们实现以上的ClassLoaderTest01

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

/**
 * Created by admin on 2019/6/23.
 */
public class ClassLoaderTest01 extends  ClassLoader {


    @Override
    protected  Class<?> findClass(String name) throws ClassNotFoundException{

        //读取class字符流:注意这里的class最好不要放在classpath下面,要不然会报错
        File file=new File("E:/Cybersource_Doc/",name.replace(".","/").concat(".class"));
        try {
            //转换字节流
            FileInputStream fis=new FileInputStream(file);
            //定义一个字节数组输出流
            ByteArrayOutputStream bos=new ByteArrayOutputStream();
            int read=0;
            while ((read = fis.read())!=0 ){
                bos.write(read);
            }
            //转换为字节数组
            byte[] bytes = bos.toByteArray();
            bos.close();
            fis.close();
            return  defineClass(name,bytes,0,bytes.length);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }


//测试方法
    public static void main(String[] args) {

        //实例化我们自定义的classLoader
        ClassLoader loader=new ClassLoaderTest01();

        try {
            //传入class文件名称
            Class<?> cl = loader.loadClass("StaticClassLoader");
            //通过反射拿到StaticClassLoader
            StaticClassLoader scl = (StaticClassLoader)cl.newInstance();
            //调用里面的test方法
            scl.test();
        }catch (Exception e){

            e.printStackTrace();
        }


    }


}
工具中准备一个用来给我们测试的class,如下
/**
 * Created by lijinquan on 2019/6/24.
 */
public class StaticClassLoader {

  static {
        System.out.println("我是StaticClassLoader的静态代码块!");
    }

    public void test(){
        System.out.println("test123");
    }

    public static void main(String[] args) {

    }

}

鼠标右键该java文件,并

得到如下

再右键此class文件,复制到我们一个自己独立的文件夹,方便我们测试

测试代码

    public static void main(String[] args) {

        //实例化我们自定义的classLoader
        ClassLoader loader=new ClassLoaderTest01();

        try {
            //传入class文件名称
            Class<?> cl = loader.loadClass("StaticClassLoader");
            //通过反射拿到StaticClassLoader
            StaticClassLoader scl = (StaticClassLoader)cl.newInstance();
            //调用里面的test方法
            scl.test();
        }catch (Exception e){

            e.printStackTrace();
        }


    }

可能会出错误:原因是生成的class可能还放在运行的项目里面,需要拿出来放到独立文件夹目录下即可

最终运行结果

当然,还可以测试下我们自定义类加载器的父类

在测试方法中加入


            System.out.println(loader.getParent());
            System.out.println(ClassLoaderTest01.class.getClassLoader());

打印出

即使自定义的ClassLoader的父类是appClassLoader

这个父类是默认的,自定义中可以通过supper指定父类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值