当前访客身份:游客 [ 登录  | 注册加入尚学堂]
启用新域名sxt.cn
新闻资讯

java类加载器-系统类加载器

helloworld 发表于 2年前  | 评论(0 )| 阅读次数(581 )|   1 人收藏此文章,   我要收藏

系统类加载器

 系统类加载器可能都耳详能熟,但是为了完整点,还是先简单的说说系统的类加载器吧。

public class Test {
 
    publicstaticvoidmain(String[] args) {
        ClassLoader cl1 = Test.class.getClassLoader().getParent().getParent();
        System.out.println(cl1);
         
        ClassLoader cl2 = Test.class.getClassLoader().getParent();
        System.out.println(cl2);
         
        ClassLoader cl3 = Test.class.getClassLoader();
        System.out.println(cl3);
    }
}


 

  打印的结果是:

    null
    sun.misc.Launcher$ExtClassLoader@dc6a77
    sun.misc.Launcher$AppClassLoader@1016632

 其实这就是jdk系统中用到的三个类加载器,其中null就是bootstrap classloader,因为是有c++实现,所以在此打印出null。ExtClassLoader就是Extension ClassLoader。AppClassLoader就是App ClassLoader或者叫做system classloader。他们负责加载各自指定位置下的类:

  1)Bootstrap ClassLoader

    负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

  2)Extension ClassLoader

    负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

  3)App ClassLoader

    负责加载classpath中指定的jar包及目录中class

这三个类加载器的关系通过代码就能看出来:App ClassLoader的parent是Extension ClassLoader,Extension ClassLoader的parent是Bootstrap ClassLoader。

委托机制

所谓委托机制就是,当加载一个类时,App ClassLoader委托他的parent(Extension ClassLoader)去加载,Extension ClassLoader又委托他的parent加载直到Bootstrap ClassLoader,如果parent没有加载成功,再由自身去加载。

以上三个类加载器,除了Bootstrap ClassLoader都是间接继承自ClassLoader类(是一个抽象类不能实例化,但没有抽象方法),委托机制的实现就被loadClass方法作为模板方法实现。以下就通过loadClass的源码分析一下委托机制:

定制类加载器

   有时我们可能需要定制我们的类加载器以满足我们的特殊需求。而且我们一般也不需要破坏委托机制(后边会介绍一个破坏了委托机制的类加载器的例子)通常可以有两种方法实现

 一、继承URLClassLoader,比如


public class TestClassLoader extends URLClassLoader {
 
    public TestClassLoader(URL[] urls) {
        super(urls);
    }
    public TestClassLoader(URL[] urls,ClassLoader parent) {
        super(urls,parent);
    }
     
}




 你只需要调用父类构造方法,其它方法你一概不用重写。这也是最简单的实现。使用时只需要调用loadClass。缺点是你只能通过URL定位你要加载的class。

二、继承ClassLoader类,重写findClass方法。比如

public class TestClassLoader extends ClassLoader {
 
    private String basedir;
     
    public TestClassLoader(String basedir,ClassLoader parent){
        super(parent);
        this.basedir = basedir;
    }
     
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> cls = null;
        StringBuffer sb = new StringBuffer(basedir);
        String classname = name.replace('.', File.separatorChar) + ".class";
        sb.append(File.separator + classname);
        File classF = new File(sb.toString());
        if(!classF.exists()){
            throw new ClassNotFoundException();
        }
         
        byte[] raw = new byte[(int) classF.length()];
        try {
            InputStream fin = new FileInputStream(classF);
            fin.read(raw);
            fin.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
         
        cls = defineClass(name,raw,0,raw.length);
        return cls;
    }
}




  在findClass方法里我们可以采用任意方式查找我们要加载的类,这里采用了读文件的方式。使用时同样是调用loadClass方法。

线程上下文类加载器

  线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的。类 java.lang.Thread中的方法 getContextClassLoader()和setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。

  前面提到的类加载器的委托机制并不能解决 Java 应用开发中会遇到的类加载器的全部问题。Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 核心库来提供,如 JAXP 的 SPI 接口定义包含在 javax.xml.parsers包中。这些 SPI 的实现代码很可能是作为 Java 应用所依赖的 jar 包被包含进来,可以通过类路径(CLASSPATH)来找到,如实现了 JAXP SPI 的 Apache Xerces所包含的 jar 包。SPI 接口中的代码经常需要加载具体的实现类。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory类中的 newInstance()方法用来生成一个新的 DocumentBuilderFactory的实例。这里的实例的真正的类是继承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的实现所提供的。如在 Apache Xerces 中,实现的类是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。 而问题在于,SPI 的接口是 Java 核心库的一部分,是由引导类加载器来加载的;SPI 实现的 Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的委托机制无法解决这个问题。

  线程上下文类加载器正好解决了这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器。在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。线程上下文类加载器在很多 SPI 的实现中都会用到。

分享到:0
关注微信,跟着我们扩展技术视野。每天推送IT新技术文章,每周聚焦一门新技术。微信二维码如下:
微信公众账号:尚学堂(微信号:bjsxt-java)
声明:博客文章版权属于原创作者,受法律保护。如果侵犯了您的权利,请联系管理员,我们将及时删除!
(邮箱:webmaster#sxt.cn(#换为@))
北京总部地址:北京市海淀区西三旗桥东建材城西路85号神州科技园B座三层尚学堂 咨询电话:400-009-1906 010-56233821
Copyright 2007-2015 北京尚学堂科技有限公司 京ICP备13018289号-1 京公网安备11010802015183