Java自定义类的加载器

Java自定义类的加载器

 

转自:

Java的ClassLoader加载机制

一看你就懂,超详细java中的ClassLoader详解

深入理解Java类加载器(ClassLoader)

 

一、ClassLoader作用

Java程序在运行的时候,JVM通过类加载机制(ClassLoader)把class文件加载到内存中,只有class文件被载入内存,才能被其他class引用,使程序正确运行起来.


二、ClassLoader的分类

Java中的ClassLoader有三种:Bootstrap ClassLoaderExtension ClassLoaderApp ClassLoader

1. Bootstrap ClassLoader

由C++写的,由JVM启动.

启动类加载器,负责加载java基础类,对应的文件是%JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等

2.Extension ClassLoader

Java类,继承自URLClassLoader 扩展类加载器,

对应的文件是 %JRE_HOME/lib/ext 目录下的jar和class等

3.App ClassLoader

Java类,继承自URLClassLoader 系统类加载器,

对应的文件是应用程序classpath目录下的所有jar和class等


三、ClassLoader的加载机制

Java的加载机制是双亲委派机制来加载类,

 

为什么要使用这种方式?这个是为了保证 如果加载的类是一个系统类,那么会优先由Bootstrap ClassLoader 、Extension ClassLoader先去加载,而不是使用我们自定义的ClassLoader去加载,保证系统的安全!

这三种类加载器存在父子关系,App ClassLoader的父类加载器是Extension ClassLoader,Extension ClassLoader的父类加载器是Bootstrap ClassLoader,要注意的一点是,这里的父子并不是继承关系.

public class ClassLoaderParentMain {

    public static void main(String[] args){
        ClassLoader ClassLoader1 = ClassLoaderParentMain.class.getClassLoader();
        ClassLoader ClassLoader2 = ClassLoader1.getParent();
        ClassLoader ClassLoader3 = ClassLoader2.getParent();

        System.out.println(ClassLoader1);
        System.out.println(ClassLoader2);
        System.out.println(ClassLoader3);
    }
}

输出的结果为:

 

当这三者中的某个ClassLoader要加载一个类时,会先委托它的父类加载器尝试加载,一直往上,如果最顶级的父类加载器没有找到该类,那么委托者则亲自到特定的地方加载

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) { // 首先,检测是否已经加载 
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) { //父加载器不为空则调用父加载器的loadClass 
                        c = parent.loadClass(name, false);
                    } else { //父加载器为空则调用Bootstrap Classloader 
                        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(); //父加载器没有找到,则调用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() 
                resolveClass(c);
            }
            return c;
        }
    }

先加载Extension ClassLoader ,如果没有加载到,那么使用Bootstrap ClassLoader去加载,如果都没有,那么使用App ClassLoader去加载。如果没找到,那么就抛出异常ClassNotFoundException.

这里画张图来表示下

只有被同一个类加载器实例加载并且文件名相同的class文件才被认为是同一个class.

 


四、自定义ClassLoader

1、为什么要自定义ClassLoader

因为系统的ClassLoader只会加载指定目录下的class文件,如果你想加载自己的class文件,那么就可以自定义一个ClassLoader.

而且我们可以根据自己的需求,对class文件进行加密和解密。

2.如何自定义ClassLoader

2.1

新建一个类继承自java.lang.ClassLoader,重写它的findClass方法。

2.2

将class字节码数组转换为Class类的实例

2.3

调用loadClass方法即可

下面的代码就是自定义Class的例子

public class MyClassLoader extends ClassLoader {
    //指定路径
    private String path ;


    public MyClassLoader(String classPath){
        path=classPath;
    }

    /**
     * 重写findClass方法
     * @param name 是我们这个类的全路径
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class log = null;
        // 获取该class文件字节码数组
        byte[] classData = getData();

        if (classData != null) {
            // 将class的字节码数组转换成Class类的实例
            log = defineClass(name, classData, 0, classData.length);
        }
        return log;
    }

    /**
     * 将class文件转化为字节码数组
     * @return
     */
    private byte[] getData() {

        File file = new File(path);
        if (file.exists()){
            FileInputStream in = null;
            ByteArrayOutputStream out = null;
            try {
                in = new FileInputStream(file);
                out = new ByteArrayOutputStream();

                byte[] buffer = new byte[1024];
                int size = 0;
                while ((size = in.read(buffer)) != -1) {
                    out.write(buffer, 0, size);
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    in.close();
                } catch (IOException e) {

                    e.printStackTrace();
                }
            }
            return out.toByteArray();
        }else{
            return null;
        }


    }
}

可以再getData里面做很多事情 ,比如加密解密之类的 都是可以的。

 

1、我们生成一个.class文件的测试样品

public class Log {
    public static void main(String[] args) {
        System.out.println("load Log class successfully");
    }
}

2、我们执行命令行 javac Log.java 生成我们的Log.class文件

3、再main函数入口 写配置参数

public class ClassLoaderMain {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
        //这个类class的路径
        String classPath = "/Users/mac/CommProjects/ClassLoaderDemo/src/com/xuhuawei/classloaderdemo/Log.class";

        MyClassLoader myClassLoader = new MyClassLoader(classPath);
        //类的全称
        String packageNamePath = "com.xuhuawei.classloaderdemo.Log";

        //加载Log这个class文件
        Class<?> Log = myClassLoader.loadClass(packageNamePath);

        System.out.println("类加载器是:" + Log.getClassLoader());

        //利用反射获取main方法
        Method method = Log.getDeclaredMethod("main", String[].class);
        Object object = Log.newInstance();
        String[] arg = {"ad"};
        method.invoke(object, (Object) arg);
    }


}

输出的结果为:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 27
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
### 回答1: Java自定义类加载器的一个简单的示例代码如下: public class MyClassLoader extends ClassLoader { public Class<?> findClass(String name) { byte[] b = loadClassData(name); return defineClass(name, b, 0, b.length); } private byte[] loadClassData(String name) { // load the class data from the connection } } ### 回答2: Java自定义类加载器代码样例如下: ```java import java.io.*; public class MyClassLoader extends ClassLoader { private String path; // 类加载器加载的路径 public MyClassLoader(String path) { this.path = path; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] data = loadClassData(name); return defineClass(name, data, 0, data.length); } private byte[] loadClassData(String name) { try { FileInputStream fis = new FileInputStream(new File(path + name + ".class")); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len; while ((len = fis.read()) != -1) { baos.write(len); } fis.close(); return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } } ``` 使用自定义类加载器加载: ```java public class Main { public static void main(String[] args) throws Exception { MyClassLoader classLoader = new MyClassLoader("加载路径"); Class<?> clazz = classLoader.loadClass("com.example.TestClass"); // 加载TestClass Object obj = clazz.newInstance(); // 创建TestClass实例 // 使用obj进行操作,调用方法等 } } ``` 以上是一个简单的自定义类加载器的代码样例。我们可以根据实际需求,通过自定义类加载器来实现更复杂的功能,如加载加密/动态生成的文件,实现文件的热加载等。 ### 回答3: 自定义类加载器是指通过继承ClassLoader并重写其的方法来实现自己的类加载器。下面是一个简单的Java自定义类加载器的代码示例: ```java import java.io.*; public class MyClassLoader extends ClassLoader { // 自定义类加载器需要指定文件的路径 private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { // 将文件转换成字节流 byte[] data = loadClassData(name); // 调用父的defineClass方法创建对象 return defineClass(name, data, 0, data.length); } private byte[] loadClassData(String className) { try { // 文件的完整路径 String path = classPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class"; InputStream inputStream = new FileInputStream(new File(path)); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int length; // 读取文件并写入内存 while ((length = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, length); } inputStream.close(); outputStream.close(); // 返回字节流 return outputStream.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } public static void main(String[] args) { // 实例化自定义类加载器并指定文件路径 MyClassLoader classLoader = new MyClassLoader("路径"); try { // 使用自定义类加载器加载 Class<?> clazz = classLoader.loadClass("名"); // 可以对加载进行操作 // ... } catch (ClassNotFoundException e) { e.printStackTrace(); } } } ``` 这个自定义类加载器首先需要重写`findClass`方法,在该方法通过`loadClassData`方法将文件读取为字节流,然后调用父的`defineClass`方法创建对象。 `loadClassData`方法根据指定的文件路径将文件读取为字节流,并将字节流写入ByteArrayOutputStream,最后返回字节数组。 在示例代码的`main`方法,实例化自定义类加载器并指定文件路径,然后通过`loadClass`方法加载指定的。可以在加载后对其进行相关操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值