ClassLoader

我们知道,Java利用ClassLoader将类载入内存,并且在同一应用中,可以有很多个ClassLoader,通过委派机制,把装载的任务传递给上级的装载器的,依次类推,直到启动类装载器(没有上级类装载器)。如果启动类装载器能够装载这个类,那么它会首先装载。如果不能,则往下传递。当父类为null时,JVM内置的类(称为:bootstrap class loader)就会充当父类。想想眼下的越来越多用XML文件做配置文件或者是描述符、部署符。其实这些通过XML文档描述的配置信息最终都要变成Java类,基实都是通过ClassLoader来完成的。URLClassLoader是ClassLoader的子类,它用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源。也就是说,通过URLClassLoader就可以加载指定jar中的class到内存中。
下面来看一个例子,在该例子中,我们要完成的工作是利用URLClassLoader加载jar并运行其中的类的某个方法。

首先我们定义一个接口,使所有继承它的类都必须实现action方法,如下:

  public   interface  ActionInterface   {
     public  String action();
}
完成后将其打包为testInterface.jar文件。

接下来新建一工程,为了编译通过,引入之前打好的testInterface.jar包。并创建TestAction类,使它实现ActionInterface接口。如下:



  public   class  TestAction  implements  ActionInterface   {
     public  String action()   {
         return   " com.mxjava.TestAction.action " ;
    }
}

完成后将其打包为test.jar,放在c盘根目录下。下面要做的就是利用URLClassLoader加载并运行TestAction的action方法,并将返回的值打印在控制台上。

新建一工程,引入testInterface.jar包。并创建一可执行类(main方法),在其中加入如下代码:

URL url  =   new  URL(“file:C: / test.jar”);
URLClassLoader myClassLoader  =   new  URLClassLoader( new  URL[]   { url } );
Class myClass  =  myClassLoader.loadClass(“com.mxjava.TestAction”);
ActionInterface action  =  (ActionInterface)myClass.newInstance();
System.out.println(action.action());
  在上面的例子中,首先利用URLClassLoader加载了C:/test.jar包,将其中的com.mxjava.TestAction类载入内存,将其强制转型为testInterface包中的ActionInterface类型,最后调用其action方法,并打印到控制台中。

  执行程序后,在控制台上如期打印出我们想要的内容。但是,事情并没有那么简单,当我们将该代码移动web应用中时,就会抛出异常。原来,Java为我们提供了三种可选择的ClassLoader:
1. 系统类加载器或叫作应用类加载器 (system classloader or application classloader)
2. 当前类加载器
3. 当前线程类加载器

  在上例中我们使用javac命令来运行该程序,这时候使用的是系统类加载器 (system classloader)。这个类加载器处理 -classpath下的类加载工作,可以通过ClassLoader.getSystemClassLoader()方法调用。 ClassLoader 下所有的 getSystemXXX()的静态方法都是通过这个方法定义的。在代码中,应该尽量少地调用这个方法,以其它的类加载器作为代理。否则代码将只能工作在简单的命令行应用中。当在web应用中时,服务器也是利用ClassLoader来加载class的,由于ClassLoader的不同,所以在强制转型时JVM认定不是同一类型。(在JAVA中,一个类用其完全匹配类名(fully qualified class name)作为标识,这里指的完全匹配类名包括包名和类名。但在JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识。因此,如果一个名为Pg的包中,有一个名为Cl的类,被类加载器KlassLoader的一个实例kl1加载,Cl的实例,即C1.class在JVM中表示为(Cl, Pg, kl1)。这意味着两个类加载器的实例(Cl, Pg, kl1) 和 (Cl, Pg, kl2)是不同的,被它们所加载的类也因此完全不同,互不兼容的。)为了能够使程序正确运行,我们首要解决的问题就是,如何将URLClassLoader加载的类,同当前ClassLoader保持在同一类加载器中。解决方法很简单,利用java提供的第三种ClassLoader—当前线程类加载器即可。jdk api文档就会发现,URLClassLoader提供了三种构造方式:

// 使用默认的委托父 ClassLoader 为指定的 URL 构造一个新 URLClassLoader。  
URLClassLoader(URL[] urls)
// 为给定的 URL 构造新 URLClassLoader。  
URLClassLoader(URL[] urls, ClassLoader parent)
// 为指定的 URL、父类加载器和 URLStreamHandlerFactory 创建新 URLClassLoader。
URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory)  
接下来要做的就是,在构造URLClassLoader时,将当前线程类加载器置入即可。如下:

  URLClassLoader myClassLoader  =   new  URLClassLoader( new  URL[]   { url } , Thread.currentThread().getContextClassLoader());
总结:
  Java是利用ClassLoader来加载类到内存的,ClassLoader本身是用java语言写的,所以我们可以扩展自己的ClassLoader。利用URLClassLoader可以加载指定jar包中的类到内存。在命行上利用URLClassLoader加载jar时,是使用系统类加载器来加载class的,所以在web环境下,就会出错。这是因为JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识的。我们只要利用URLClassLoader的第二种构造方法并传入当前线程类加载器即可解决。

 

 

如果动态的加入JAR文件的话,建议使用下面的类

 

package com.vol.classloader;


import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;

import sun.misc.Launcher;

/**
 * @author wangzonghao
 * @since 2008-12-21
 * @version 1.0
 */
public class ClassLoaderUtil {
    private static Field classes;

    private static Method addURL;
    static {
        try {
            classes = ClassLoader.class.getDeclaredField("classes");
            addURL = URLClassLoader.class.getDeclaredMethod("addURL",
                    new Class[] { URL.class });
        } catch (Exception e) {
            e.printStackTrace();
        }
        classes.setAccessible(true);
        addURL.setAccessible(true);
    }

    private static URLClassLoader system = (URLClassLoader) getSystemClassLoader();

    private static URLClassLoader ext = (URLClassLoader) getExtClassLoader();

    public static ClassLoader getSystemClassLoader() {
        return ClassLoader.getSystemClassLoader();
    }

    public static ClassLoader getExtClassLoader() {
        return getSystemClassLoader().getParent();
    }

    /**
     * 获得加载的类
     *
     * @return
     */
    public static List getClassesLoadedBySystemClassLoader() {
        return getClassesLoadedByClassLoader(getSystemClassLoader());
    }

    public static List getClassesLoadedByExtClassLoader() {
        return getClassesLoadedByClassLoader(getExtClassLoader());
    }

    public static List getClassesLoadedByClassLoader(ClassLoader cl) {
        try {
            return (List) classes.get(cl);
        } catch (Exception e) {
         e.printStackTrace();
        }
        return null;
    }

    public static URL[] getBootstrapURLs() {
        return Launcher.getBootstrapClassPath().getURLs();
    }

    public static URL[] getSystemURLs() {
        return system.getURLs();
    }

    public static URL[] getExtURLs() {
        return ext.getURLs();
    }

    private static void list(PrintStream ps, URL[] classPath) {
        for (int i = 0; i < classPath.length; i++) {
            ps.println(classPath[i]);
        }
    }

    public static void listBootstrapClassPath() {
        listBootstrapClassPath(System.out);
    }

    public static void listBootstrapClassPath(PrintStream ps) {
        ps.println("BootstrapClassPath:");
        list(ps, getBootstrapClassPath());
    }

    public static void listSystemClassPath() {
        listSystemClassPath(System.out);
    }

    public static void listSystemClassPath(PrintStream ps) {
        ps.println("SystemClassPath:");
        list(ps, getSystemClassPath());
    }

    public static void listExtClassPath() {
        listExtClassPath(System.out);
    }

    public static void listExtClassPath(PrintStream ps) {
        ps.println("ExtClassPath:");
        list(ps, getExtClassPath());
    }

    public static URL[] getBootstrapClassPath() {
        return getBootstrapURLs();
    }

    public static URL[] getSystemClassPath() {
        return getSystemURLs();
    }

    public static URL[] getExtClassPath() {
        return getExtURLs();
    }

    public static void addURL2SystemClassLoader(URL url) {
        try {
            addURL.invoke(system, new Object[] { url });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void addURL2ExtClassLoader(URL url) {
        try {
            addURL.invoke(ext, new Object[] { url });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void addClassPath(String path) {
        addClassPath(new File(path));
    }

    public static void addExtClassPath(String path) {
        addExtClassPath(new File(path));
    }

    public static void addClassPath(File dirOrJar) {
        try {
            addURL2SystemClassLoader(dirOrJar.toURL());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }

    public static void addExtClassPath(File dirOrJar) {
        try {
            addURL2ExtClassLoader(dirOrJar.toURL());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值