Java类加载机制及自定义加载器

  一:ClassLoader类加载器,主要的做用是将class文件加载到jvm虚拟机中。jvm启动的时候,并非一次性加载全部的类,而是根据须要动态去加载类,主要分为隐式加载和显示加载。java

  隐式加载:程序代码中不经过调用ClassLoader来加载须要的类,而是经过JVM类自动加载须要的类到内存中。例如,当咱们在类中继承或者引用某个类的时候,JVM在解析当前这个类的时,发现引用的类不在内存中,那么就会自动将这些类加载到内存中。网络

  显示加载:代码中经过Class.forName(),this.getClass.getClassLoader.LoadClass(),自定义类加载器中的findClass()方法等。app

  二:jvm自带的加载器jvm

  (1)BootStrap ClassLoader:主要加载%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。能够通System.getProperty("sun.boot.class.path")ide

     查看加载的路径,以下:oop

package test;

public class TestGC {
    
    public static void main(String []args){

        System.out.println(System.getProperty("sun.boot.class.path"));
        
    }
}

    显示结果以下:测试

D:\Program Files\Java\jdk1.7.0_45\jre\lib\resources.jar;D:\Program Files\Java\jdk1.7.0_45\jre\lib\rt.jar;D:\Program Files\Java\jdk1.7.0_45\jre\lib\sunrsasign.jar;D:\Program Files\Java\jdk1.7.0_45\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.7.0_45\jre\lib\jce.jar;D:\Program Files\Java\jdk1.7.0_45\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.7.0_45\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.7.0_45\jre\classes

  (2)Extention ClassLoader:主要加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。也能够经过System.out.println(System.getProperty("java.ext.dirs"))查看加载类文件的路径。this

  (3)AppClassLoader:主要加载当前应用下的classpath路径下的类。以前咱们在环境变量中配置的classpath就是指定AppClassLoader的类加载路径。spa

  三:类加载器的继承关系.net

  先看一下这三个类加载器之间的继承关系,以下图:

  

  ExtClassLoader,AppClassLoder继承URLClassLoader,而URLClassLoader继承ClassLoader,BoopStrap ClassLoder不在上图中,由于它是由C/C++编写的,它自己是虚拟机的一部分,并非一个java类。jvm加载的顺序:BoopStrap ClassLoder-〉ExtClassLoader->AppClassLoder,下面看一段源码:

public class Launcher {
    private static Launcher launcher = new Launcher();
    private static String bootClassPath =
        System.getProperty("sun.boot.class.path");

    public static Launcher getLauncher() {
        return launcher;
    }

    private ClassLoader loader;

    public Launcher() {
        // Create the extension class loader
        ClassLoader extcl;
        try {
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError(
                "Could not create extension class loader", e);
        }

        // Now create the class loader to use to launch the application
        try {
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError(
                "Could not create application class loader", e);
        }

        Thread.currentThread().setContextClassLoader(loader);
    }

    /*
     * Returns the class loader used to launch the main application.
     */
    public ClassLoader getClassLoader() {
        return loader;
    }
    /*
     * The class loader used for loading installed extensions.
     */
    static class ExtClassLoader extends URLClassLoader {}

/**
     * The class loader used for loading from java.class.path.
     * runs in a restricted security context.
     */
    static class AppClassLoader extends URLClassLoader {}

   从源码中咱们看到:(1)Launcher初始化的时候建立了ExtClassLoader以及AppClassLoader,并将ExtClassLoader实例传入到AppClassLoader中。

   (2)虽然上一段源码中没见到建立BoopStrap ClassLoader,可是程序一开始就执行了System.getProperty("sun.boot.class.path")。

  四:类加载器之间的父子关系

    AppClassLoader的父加载器为ExtClassLoader,ExtClassLoader的父加载器为null,BoopStrap ClassLoader为顶级加载器。

  下面一个小例子就能够证实,以下:新建一个Test类,能够经过getParent()方法获取上一层父机载器,执行以下代码:

package test;

public class TestGC {
    
    public static void main(String []args){
        
        System.out.println(Test.class.getClassLoader().toString());
        
        System.out.println(Test.class.getClassLoader().getParent().toString());
        
        System.out.println(Test.class.getClassLoader().getParent().getParent().toString());
    }
}

  输出结果以下:

  

  五:类加载机制-双亲委托机制

  例如:当jvm要加载Test.class的时候,

  (1)首先会到自定义加载器中查找,看是否已经加载过,若是已经加载过,则返回字节码。

  (2)若是自定义加载器没有加载过,则询问上一层加载器(即AppClassLoader)是否已经加载过Test.class。

  (3)若是没有加载过,则询问上一层加载器(ExtClassLoader)是否已经加载过。

  (4)若是没有加载过,则继续询问上一层加载(BoopStrap ClassLoader)是否已经加载过。

  (5)若是BoopStrap ClassLoader依然没有加载过,则到本身指定类加载路径下("sun.boot.class.path")查看是否有Test.class字节码,有则返回,没有通

知下一层加载器ExtClassLoader到本身指定的类加载路径下(java.ext.dirs)查看。

  (6)依次类推,最后到自定义类加载器指定的路径尚未找到Test.class字节码,则抛出异常ClassNotFoundException。以下图:

  

  六:类加载过程的几个方法

  (1)loadClass     (2)findLoadedClass     (3)findClass     

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先,检查是否已经加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        //父加载器不为空,调用父加载器的loadClass
                        c = parent.loadClass(name, false);
                    } else {
                        //父加载器为空则,调用Bootstrap Classloader
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //父加载器没有找到,则调用findclass
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                //调用resolveClass()
                resolveClass(c);
            }
            return c;
        }
    }

  七:自定义类加载器步骤

  (1)继承ClassLoader    (2)重写findClass()方法   (3)调用defineClass()方法

  下面写一个自定义类加载器:指定类加载路径在D盘下的lib文件夹下。

  (1)新建一个Test.class类,代码以下:

package com.test;

public class Test {

    public void say(){
System.out.println(
"Hello MyClassLoader"); } }

  (2)cmd控制台执行javac Test.java,将生成的Test.class文件放到D盘lib文件夹->com文件夹->test文件夹下。

  (3)自定义类加载器,代码以下:

package test;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader{

    private String classpath;
    
    public MyClassLoader(String classpath) {
        
        this.classpath = classpath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte [] classDate=getDate(name);
            
            if(classDate==null){}
            
            else{
                //defineClass方法将字节码转化为类
                return defineClass(name,classDate,0,classDate.length);
            }
            
        } catch (IOException e) {
            
            e.printStackTrace();
        }
        
        return super.findClass(name);
    }
    //返回类的字节码
    private byte[] getDate(String className) throws IOException{
        InputStream in = null;
        ByteArrayOutputStream out = null;
        String path=classpath + File.separatorChar +
                    className.replace('.',File.separatorChar)+".class";
        try {
            in=new FileInputStream(path);
            out=new ByteArrayOutputStream();
            byte[] buffer=new byte[2048];
            int len=0;
            while((len=in.read(buffer))!=-1){
                out.write(buffer,0,len);
            }
            return out.toByteArray();
        } 
        catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        finally{
            in.close();
            out.close();
        }
        return null;
    }
}

  测试代码以下:

package test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestMyClassLoader {

    public static void main(String []args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException{
        //自定义类加载器的加载路径
        MyClassLoader myClassLoader=new MyClassLoader("D:\\lib");
        //包名+类名
        Class c=myClassLoader.loadClass("com.test.Test");
        
        if(c!=null){
            Object obj=c.newInstance();
            Method method=c.getMethod("say", null);
            method.invoke(obj, null);
            System.out.println(c.getClassLoader().toString());
        }
    }
}

  输出结果以下:

  

  自定义类加载器的做用:jvm自带的三个加载器只能加载指定路径下的类字节码。若是某个状况下,咱们须要加载应用程序以外的类文件呢?好比本地D盘下的,或者去加载网络上的某个类文件,这种状况就可使用自定义加载器了。

   参考网址:http://blog.csdn.net/briblue/article/details/54973413

相关文章
相关标签/搜索