• 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 的实现中都会用到。

  • 相关阅读:
    https页面打不开
    Centos6.5安装步骤(U盘安装)
    利用Metrics+influxdb+grafana构建监控平台
    CentOS 7安装Oracle 11gR2以及设置自启动
    如何安装Oracle Instant Client
    (转)rlwrap真是一个好东西
    oracle数据库11g(11.2.0.1)安装报错:提示ins_ctx.mk编译错误。
    oracle查看所有表及各表行数
    dp hdu5653 xiaoxin and his watermelon candy
    C语言free函数的原理——————————【Badboy】
  • 原文地址:https://www.cnblogs.com/metoy/p/3915990.html
Copyright © 2020-2023  润新知