zoukankan      html  css  js  c++  java
  • java类加载器-系统类加载器

    系统类加载器

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

    public class Test {
    
    	public static void main(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 的实现中都会用到。

  • 相关阅读:
    Leetcode 191.位1的个数 By Python
    反向传播的推导
    Leetcode 268.缺失数字 By Python
    Leetcode 326.3的幂 By Python
    Leetcode 28.实现strStr() By Python
    Leetcode 7.反转整数 By Python
    Leetcode 125.验证回文串 By Python
    Leetcode 1.两数之和 By Python
    Hdoj 1008.Elevator 题解
    TZOJ 车辆拥挤相互往里走
  • 原文地址:https://www.cnblogs.com/metoy/p/3915990.html
Copyright © 2011-2022 走看看