10、自定义类加载器

自定义类加载器的3个步骤:

  1、继承ClassLoader;

  2、重写findClass()方法;

  3、在findClass()方法调用defineClass()方法;

package com.shtec.classLoader;

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

/**
 * 自定义类加载器的3个步骤:
 *    1、继承ClassLoader
 *    2、重写findClass()方法
 *    3、在findClass()方法调用defineClass()方法
 *  
 * @author sunhao
 *
 */
public class CustomClassLoader extends ClassLoader{
    
    private String classLoaderName;
    private String classPath; //类加载器的加载路径
    private static final String suffix = ".class";
    
    public CustomClassLoader(){}
    
    public CustomClassLoader(String classLoaderName){
        super();//将系统类加载器当做该类加载器的父加载器
        this.classLoaderName = classLoaderName;
    }
    
    public CustomClassLoader(ClassLoader parent, String classLoaderName){
        super(parent);//显示指定该类加载器的父加载器
        this.classLoaderName = classLoaderName;
    }

    public void setClassPath(String classPath) {
        this.classPath = classPath;
    }

    /*
     * ***********************************************************************
     * ClassLoader中的loadClass(String name, boolean resolve)方法会调用findClass方法*
     * 该方法执行,说明自定义类加载器生效                                                 *
     * ***********************************************************************
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        
        System.out.println("自定义类加载器【" + this.classLoaderName + "】查找类" + name);
        
        byte[] data = this.loadClassData(name);
        
        //defineClass方法将字节数组转化为Class对象
        return this.defineClass(name, data, 0, data.length);
    }
    
    /*
     * 加载.class文件,并转换为字节数组
     */
    private byte[] loadClassData(String className){
        InputStream in = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        className = className.replace(".", "/");
        try {
            in = new FileInputStream(new File(this.classPath + className + suffix));
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[8192];
            int len = 0;
            while((len=in.read(buffer)) != -1){
                baos.write(buffer, 0, len);
            }
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(baos != null){
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return data;
    }
    
    public static void main(String[] args) throws Exception {
        
        CustomClassLoader customClassLoader = new CustomClassLoader("customClassLoader");
        customClassLoader.setClassPath("D:\\classes\\");
        
        Class<?> clazz = customClassLoader.loadClass("com.shtec.classLoader.Test");
        System.out.println("当前类加载器:" + clazz.getClassLoader());
     

//1、默认情况下,重写的“findClass()”方法不会执行,说明自定义的类加载器加载类的操作并没有生效
//输出:
//当前类加载器:sun.misc.Launcher$AppClassLoader@73d16e93
/*
* 原因:java类加载器对类的加载,遵循【双亲委派机制】,也就是说通过自定义的类加载器去加载"com.shtec.classLoader.Test"类时,
* 自定义类加载器会把加载任务先交给它的父加载器,而自定义类加载器的父加载器默认是【系统类加载器】(查看自定义类加载器的构造方法),
* 最终,系统类加载器发现(System.getProperties("java.class.path"))类路径下有对应Test类的.class文件,于是将其加载。
* 所以,重写的“findClass()”方法不会执行,最终打印出:【类加载器:sun.misc.Launcher$AppClassLoader@73d16e93】。
*/

/*
* 2、如果想让重写的“findClass()”方法执行,只需要将类路径下的Test.class文件删除,由于删除后,系统类加载器加载不了这个.class文件,因此,对“com.shtec.classLoader.Test.class”的加载工作将交由我们自定义的类加载器。
* 最终输出:
* 自定义类加载器【customClassLoader】查找类com.shtec.classLoader.Test
* 当前类加载器:com.shtec.classLoader.CustomClassLoader@15db9742
*/

    }
}
posted @ 2020-02-22 02:21  java小天地  阅读(350)  评论(0编辑  收藏  举报