类加载(三)

一、类加载器

  • 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库(rt.jar)
  • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库,Java 虚拟机的实现会提供一个扩展库目录(%JAVA_HOME%lib/ext/*.jar)
  • 应用类加载器(application class loader):它根据类路径来加载某个类 (CLASSPATH)

  • 自定义加载器:加载自定义路径的类

 1.AppClassLoader

 

 

二、示例

1. 类加载

 public class ClassLoaderTree { 

    public static void main(String[] args) { 
        ClassLoader loader = ClassLoaderTree.class.getClassLoader(); 
        while (loader != null) { 
            System.out.println(loader.toString()); 
            loader = loader.getParent(); 
        } 
    } 
 }

结果 

 sun.misc.Launcher$AppClassLoader@9304b1   //子
 sun.misc.Launcher$ExtClassLoader@190d11   //父

 

2. 

    private static ClassLoader getLocaleClassLoader(File libDir) throws Exception {
        List<URL> urls = new ArrayList<>();

        // 获取所有的jar文件  
        File[] jarFiles = libDir.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".jar");
            }
        });

        // 将jar文件路径写入集合  
        for (File jarFile : jarFiles) {
            urls.add(jarFile.toURI().toURL());
        }

        // 实例化类加载器  
        return new URLClassLoader(urls.toArray(new URL[urls.size()]));
    }

    public static void main(String[] args) throws Exception{
        String name = "com.google.common.collect.ImmutableList";
        ClassLoader classLoader = getLocaleClassLoader(new File("/Users/gl/IntelliJProjects/JBase/kafka/build/dependency/"));
        Class clazz = classLoader.loadClass(name);
        System.out.println(clazz);


        //ClassLoader 为 null, 原因是:
        //Cat.class.getClass()是java.lang.Class对象, 它的加载器是引导类加载器
        Cat.class.getClass().getClassLoader();

        //下面是ClassNotFoundException
        clazz = Cat.class.getClassLoader().loadClass(name);
        System.out.println(clazz);

    }

 

 

三、类加载器的代理模式

  类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。

  代理模式是为了保证 Java 核心库的类型安全所有 Java 应用都至少需要引用java.lang.Object类,也就是说在运行的时候,java.lang.Object

这个类需要被加载到 Java 虚拟机中。如果这个加载过程由 Java 应用自己的类加载器来完成的话,很可能就存在多个版本的 java.lang.Object类,而

且这些类之间是不兼容的。通过代理模式,对于 Java 核心库的类的加载工作由引导类加载器来统一完成,保证了 Java 应用所使用的都是同一个版本的

Java 核心库的类,是互相兼容的。

  不同的类加载器为相同名称的类创建了额外的名称空间。相同名称的类可以并存在 Java 虚拟机中,只需要用不同的类加载器来加载它们即可。不同

类加载器加载的类之间是不兼容的,这就相当于在 Java 虚拟机内部创建了一个个相互隔离的 Java 类空间。这种技术在许多框架中都被用到,后面会详细

介绍。

 

四、加载类的过程

  在前面介绍类加载器的代理模式的时候,提到过类加载器会首先代理给其它类加载器来尝试加载某个类。这就意味着真正完成类的加载工作的类加载器和启

动这个加载过程的类加载器,有可能不是同一个。

真正完成类的加载工作是通过调用 defineClass来实现的;而启动类的加载过程是通过调用 loadClass来实现的。前者称为一个类的定义加载器(defining loader),

后者称为初始加载器(initiating loader)。

在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。

两种类加载器的关联之处在于:一个类的定义加载器是它引用的其它类的初始加载器。

如类 Outer引用了类Inner,则由类 Outer的定义加载器负责启动类Inner的加载过程。

  方法 loadClass()抛出的是 java.lang.ClassNotFoundException异常;

  方法 defineClass()抛出的是 java.lang.NoClassDefFoundError异常。

类加载器在成功加载某个类之后,会把得到的 java.lang.Class类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会

尝试再次加载。也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即 loadClass方法不会被重复调用。

 

五、线程上下文类加载器

  java.lang.Thread中的set/get ContextClassLoader, 如果没有设置ContextClassLoader 的话,线程将继承其父线程的类加载器。初始

线程的类加载器是系统类加载器。

  前面提到的类加载器的代理模式并不能解决 Java 应用开发中会遇到的类加载器的全部问题。Java 提供了很多服务提供者接口(Service Provider

Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 核心库来提供,

如 JAXP 的 SPI 接口定义包含在 javax.xml.parsers包中。

  SPI 接口中的代码经常需要加载具体的实现类,如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory类中的 newInstance()方法用来生成一个

新的 DocumentBuilderFactory的实例。如在 Apache Xerces 中,实现的类是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl而问题在于:

1. SPI 的接口是 Java 核心库的一部分,是由引导类加载器来加载的

2. SPI 的实现是由应用类加载器来加载的

  

六、类加载器与 Web 容器

  首先尝试去加载某个类,如果找不到再代理给父类加载器。其目的是使得 Web 应用自己的类的优先级高于 Web 容器提供的类。这种代理模式的一个例

外是:Java 核心库的类是不在查找范围之内的。这也是为了保证 Java 核心库的类型安全。

 

posted @ 2013-12-23 16:53  等风来。。  Views(274)  Comments(0)    收藏  举报
------------------------------------------------------------------------------------------------------------ --------------- 欢迎联系 x.guan.ling@gmail.com--------------- ------------------------------------------------------------------------------------------------------------