ClasssLoader

ClasssLoader 入门了解

概念

类加载就是用来加载类到虚拟机中。

Java源程序通过编译之后,生成java字节码文件(.class文件),类加载器负责读取(loadClass())加载字节码文件,转换成一个java.lang.Class实例。

类加载器双亲委派模型

为什么要使用双亲委派模型:避免类的重复加载

  • 防止内存中出现多份同样的字节码

    比如BootstrapClassLoader 已经加载过java.lang.String,现在我们自己在classpath下由创建了一个java.lang.String,这个时候类加载的时候首先会去调用BootstrapClassLoader 去加载rt.jar下的java.lang.String,而不会加载classpath下的java.lang.String

  • 避免类重复加载,保证安全性

    假设上面的String加载的是我们ClassPath下的java.lang.String,比如我们调用String.equals()方法的时候,在里面加一些入侵代码,然后类在加载的时候加载到jvm中了,这样就会导致入侵代码执行,造成安全隐患。

JDK已有的类加载器
  • BootStrap Classloader 启动类加载器 C++编写 主要加载rt.jar 并不继承ClassLoader
  • Extension Classloader 拓展类加载器 加载 %JAVA_HOME%/lib/ext/*.jar
  • AppClassLoader 应用类加载器或者说是系统类加载器 可以通过 ClassLoader.getSystemClassLoader()来获取它。 加载路径是从Classpath下加载

自定义加载器加载路径是完全自定义的

自定义类加载器
/**
 * Created on 2018/5/10
 *
 * @author wang.teng
 */
public class MyClassLoader extends ClassLoader {
    private String path;

    public MyClassLoader(String path) {
        super();//让系统类加载器成为该类的父加载器
        this.path = path;
    }

    public MyClassLoader(ClassLoader parent, String path) {
        super(parent);
        this.path = path;
    }
    /**
     * 跟据名称加载到相应的类
     * eg:com.wt.classloader
     *
     * @param name
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] b = readClassForByte(name);
        return this.defineClass(name, b, 0, b.length);
    }

    private byte[] readClassForByte(String name) {
        name =name.replaceAll("\\.","/");
        String path = this.path + name + ".class";
        InputStream is = null;
        ByteArrayOutputStream bos = null;
        File file = new File(path);
        try {
            is = new FileInputStream(file);
            bos = new ByteArrayOutputStream();
            int tmp = 0;
            while ((tmp = is.read()) != -1) {
                bos.write(tmp);
            }
            return bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

测试方法:

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader myClassLoader = new MyClassLoader("J://tmp//");
        // MyClassLoader myClassLoader = new MyClassLoader(null,"J://tmp//");
        //com.wt.classloader.Demo
        Class<?> c = myClassLoader.loadClass("com.wt.classloader.Demo");
        c.newInstance();
    }

如果不指定加载器,默认会加载AppClassLoader ,如存在同包名的类,那末会加载classpath下的的类,不会加载指定路径下的类。

  1. 第一种方式,编写一个Demo.java,没有包,编译成.class之后,放到J:temp/下,这时,加载Demo.class的方法是:

    MyClassLoader myClassLoader = new MyClassLoader("J://tmp//");
    Class<?> c = myClassLoader.loadClass("Demo");
  2. 第二种,同样编写Demo.java ,这时加上包名com.wt.classloader,同样放入J:temp/下,这时在我们的工作空间同样写上Demo.java 包也相同。

    MyClassLoader myClassLoader = new MyClassLoader("J://tmp//");
    Class<?> c = myClassLoader.loadClass("com.wt.classLoader.Demo");

    这时大家可以先猜猜看会加载 J:tmp//com/wt/classloader/Demo.class,还是会加载Classpath:com.wt.classloader.Demo.class。答案是后者,为什么呢?其实很简单,MyClassLoader的父类是AppClassLoader,而AppClassLoader会加载ClassPath下面的类,这时候就会去找com.wt.classloader.Demo.class,这个时候我们包下面刚好有,那就会直接加载,加载完之后就返回,并不会再去加载J://tmp//下的Demo.class。

    那么我们如何加载我们J:tmp//下的Demo.class呢,其实很简单,只需要设置其父类加载器为BootStrapClassLoader,因为再BootStrapClassLoader加载的包下是找不到com.wt.classLoader.Demo的,找不到就会找加载我们所指定路径的class。

ClassLoader使用场景
  • tomcat的框架 (WebappClassLoader 加载webapps下的class文件)

猜你喜欢

转载自blog.csdn.net/wagnteng/article/details/80294422