首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Java自定义类加载器与双亲委派模型(2)

Java自定义类加载器与双亲委派模型(2)

2. 自定义类加载器2. 1几个重要函数2.1.1 loadClassloadClass默认实现如下:
public Class<?> loadClass(String name) throws ClassNotFoundException {        return loadClass(name, false);}
再看看loadClass(String name, boolean resolve)函数:
[url=][/url]
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;    }}[url=][/url]

从上面代码可以明显看出,loadClass(String, boolean)函数即实现了双亲委派模型!整个大致过程如下:
  • 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
  • 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载。
  • 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。
话句话说,如果自定义类加载器,就必须重写findClass方法!
2.1.1 find ClassfindClass的默认实现如下:
protected Class<?> findClass(String name) throws ClassNotFoundException {        throw new ClassNotFoundException(name);}
可以看出,抽象类ClassLoader的findClass函数默认是抛出异常的。而前面我们知道,loadClass在父加载器无法加载类的时候,就会调用我们自定义的类加载器中的findeClass函数,因此我们必须要在loadClass这个函数里面实现将一个指定类名称转换为Class对象.
如果是是读取一个指定的名称的类为字节数组的话,这很好办。但是如何将字节数组转为Class对象呢?很简单,Java提供了defineClass方法,通过这个方法,就可以把一个字节数组转为Class对象啦~
2.1.1 defineClassdefineClass主要的功能是:
将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。如,假设class文件是加密过的,则需要解密后作为形参传入defineClass函数。
defineClass默认实现如下:
protected final Class<?> defineClass(String name, byte[] b, int off, int len)        throws ClassFormatError  {        return defineClass(name, b, off, len, null);}
2.2 函数调用过程上一节所提的函数调用过程如下:

自定义函数调用过程

2.3 简单示例首先,我们定义一个待加载的普通Java类:Test.java。放在com.huachao.cl包下:
[url=][/url]
package com.huachao.cl;public class Test {    public void hello() {        System.out.println("恩,是的,我是由 " + getClass().getClassLoader().getClass()                + " 加载进来的");    }}[url=][/url]

注意:
如果你是直接在当前项目里面创建,待Test.java编译后,请把Test.class文件拷贝走,再将Test.java删除。因为如果Test.class存放在当前项目中,根据双亲委派模型可知,会通过sun.misc.Launcher$AppClassLoader 类加载器加载。为了让我们自定义的类加载器加载,我们把Test.class文件放入到其他目录。
在本例中,我们Test.class文件存放的目录如下:

class文件目录

接下来就是自定义我们的类加载器:
[url=][/url]
import java.io.FileInputStream;import java.lang.reflect.Method;public class Main {    static 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;        }        protected Class<?> findClass(String name) throws ClassNotFoundException {            try {                byte[] data = loadByte(name);                return defineClass(name, data, 0, data.length);            } catch (Exception e) {                e.printStackTrace();                throw new ClassNotFoundException();            }        }    };    public static void main(String args[]) throws Exception {        MyClassLoader classLoader = new MyClassLoader("D:/test");        Class clazz = classLoader.loadClass("com.huachao.cl.Test");        Object obj = clazz.newInstance();        Method helloMethod = clazz.getDeclaredMethod("hello", null);        helloMethod.invoke(obj, null);    }}[url=][/url]

最后运行结果如下:
恩,是的,我是由 class Main$MyClassLoader 加载进来的
返回列表