ClassLoader学习笔记

来源:互联网 发布:广东篮球队球员数据 编辑:程序博客网 时间:2024/05/07 08:24

一、什么是ClassLoader

大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程序的一个入口函数来调用系统的相关功能,而这些功能都被封装在不同的class文件当中,所以经常要从这个class文件中要调用另外一个class文件中的方法,如果另外一个文件不存在的,则会引发系统异常。而程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它class所引用。所以ClassLoader就是用来动态加载class文件到内存当中用的。

二、双亲委托机制

知道了什么是ClassLoader,那么JAVA是如何加载Class的呢,Java有一套双亲委托机制来动态加载class,请见下图:
Jvm双亲委托机制
当一个装载器被要求加载一个class的时候,会首先委托自己的parent ClassLoader去加载,如果parent能加载,则由parent返回class的对象,如上图的图1箭头所示,当所有的parent ClassLoader无法返回的时候,则由当前的ClassLoader加载class

三、运行时包

由同一个ClasLoader加载的属于同一个包的类,组成了运行时包,属于同一个包但不属于同一个ClassLoader的类不能访问相同包内可见的成员和方法。
Jvm这么做是出于安全考虑,假设我们写一个java.lang.String类,并自己自定义一个ClassLoader,那么可以访问由Bootstrap ClassLoader加载的java.lang.XXX的包内可见的方法和成员,这样显然是不可取也很不安全的。

总结:双亲委托机制增加了Jvm的安全,运行时包增加了对包内可见成员的保护

四、定义自己的类加载器

参考API,我们知道Classloader位于java.lang包下,通常使用 loadclass方法加载一个class,一个class可以是文件系统,也可以是网络上的一个文件,甚至是一个二进制数据流。如

 ClassLoader loader = new NetworkClassLoader(host, port); Object main = loader.loadClass("Main", true).newInstance();

NetworkClassLoader 继承了ClassLoader,所以需要覆盖重写findClass方法(请参考API文档findClass方法说明),同时要定义加载loadClassData方法从网络上加载二进制数据,加载完成后我们可以得到Class的引用对象,调用newInstance()方法我们就完成了一个类加载以及实例化的过程。

class NetworkClassLoader extends ClassLoader {    String host;    int port;    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         . . .    }}

五、完整示例

我们以第四点说明的例子为入口,完整写完这个示例。

ClassLoadInstance :这个类比较简单,Transform强制类型转换为自己类型,用来判断是否是同一个class,我们从不同的classloader加载的class虽然包名和类名一致,但是不同的classloader,运行时会报出 java.lang.ClassCastException,由此证明我们实验成功

public class ClassLoadInstance {    public ClassLoadInstance instance;    public void Transform(Object obj) {        instance = (ClassLoadInstance) obj;    }}

NetworkClassLoader :这个是我们自定义的classloader,负责从网络加载class,实现一个classloader需要继承自classloader,并重写findclass方法,重要的是实现getClzData()

package com.demo.test;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.net.MalformedURLException;import java.net.URL;public class NetworkClassLoader extends ClassLoader{    private String rootUrl;    public NetworkClassLoader(String url){        //super(null);        this.rootUrl = url;    }    @Override    protected Class<?> findClass(String clzName) throws ClassNotFoundException {        // TODO Auto-generated method stub        byte[] clzDate = null;        clzDate = getClzData(clzName);        return defineClass(clzName, clzDate, 0, clzDate.length);    }    private byte[] getClzData(String clzName){        String path = getClzPath(clzName);        ByteArrayOutputStream outputStream = null;        try {            URL url = new URL(path);            InputStream inputStream = url.openStream();            int len = -1;            byte[] buff = new byte[1024*4];            outputStream = new ByteArrayOutputStream();            while((len = inputStream.read(buff))!= -1){                outputStream.write(buff, 0, len);            }        } catch (MalformedURLException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        return outputStream.toByteArray();    }    private String getClzPath(String clzName){        return rootUrl + clzName.replace(".", "/")+".class";    }}

NetClassLoadTest :测试实践类,证明不同的classloader加载的相同包名和类名的class,会出现类型转换异常
注意:需要先建立一个web项目,并把ClassLoadInstance.class文件放在和JSP目录同级别目录下,不是放在/WEB-INF/classes,/WEB-INF目录是不可以访问的

/** *  */package com.demo.test;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/** * @author Administrator * */public class NetClassLoadTest {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        String clzName = "com.demo.web.ClassLoadInstance";        String url = "http://localhost:8080/web/classes/";        NetworkClassLoader loader1 = new NetworkClassLoader(url);        NetworkClassLoader loader2 = new NetworkClassLoader(url);        try {            Class clz1 = loader1.loadClass(clzName);            Class clz2 = loader2.loadClass(clzName);            Object obj1 = clz1.newInstance();            Object obj2 = clz2.newInstance();            System.out.println(clz1.getClassLoader());            System.out.println(clz1.getClassLoader().getParent());            System.out.println(clz1.getClassLoader().getParent().getParent());            try {                clz1.getMethod("Transform", Object.class).invoke(obj1, obj2);            } catch (IllegalArgumentException e) {                // TODO Auto-generated catch block                e.printStackTrace();            } catch (InvocationTargetException e) {                // TODO Auto-generated catch block                e.printStackTrace();            } catch (NoSuchMethodException e) {                // TODO Auto-generated catch block                e.printStackTrace();            } catch (SecurityException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        } catch (ClassNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }catch (InstantiationException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IllegalAccessException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}
0 0