一、Java类的载入器:
双亲委派模型:
1、JVM提供了三种类型的类加载器:引导类载入器(bootstrap class loader)、扩展类载入器(extension class loader)、应用程序类载入器(也叫系统类载入器:system class loader);
2、引导类载入器用于引导启动java虚拟机;引导类载入器使用本地代码实现,用来载入jvm需要的类,以及所有java核心类,比如java.lang和java.io等包下的类。扩展类载入器载入标准扩展目录中的类,标准扩展目录是jdk/jre/lib/ext。系统类载入器会搜索在环境变量CLASSPATH中指明的路径和JAR文件,他作为自定义载入器的父类,子类只需要覆盖一些方法就可以实现自定义载入类;
3、当需要载入一个类时,会首先调用应用程序载入器(系统类载入器),应用程序载入器会将请求交给扩展类载入器,扩展类载入器又将请求交给引导类载入器,因此引导类载入器总是会首先执行载入类的任务,如果引导类载入器找不到需要载入的类,那么扩展类载入器尝试载入该类,如果扩展类载入器也找不到这个类,就由应用程序载入器执行载入任务,如果还是找不到这个类,则抛出java.lang.ClassNotFoundException异常;
4、双亲委派模型的作用是为了安全性,比如当用户编写了一个java.lang.object的类,他可以恶意的访问磁盘中的任意目录,但是在载入时双亲委派模型会交由引导类载入器载入,,结果是引导类载入器搜索其核心库,找到标准的java.lang.Object类并载入,自定义的java.lang.Object类并没有被载入,因此恶意代码不会被执行,安全性得到保障;
自定义类加载器:
1、继承java.lang.CLassLoader类;
2、重写父类的findClass方法;
通过ClassLoader的源码可以看到,findClass方法并未有实现代码,其是交由子类来实现自定义查找类并加载,一般不建议重写loadClass方法;
二、Tomcat载入器UML图:
1、Loader接口作为tomcat的载入器,webappLoader为其实现类;createClassLoader方法会创建一个webappClassLoader类型的载入器,webappClassLoader类继承自URLClassLoader(URLClassLoader最终继承自ClassLoader类)的自定义载入器;WebappLoader的对类的加载功能实现都是由这个自定义类载入器实现的;
2、createClassLoader默认会根据loaderClass的字符串变量生成一个webappClassLoader的自定义载入器,可以通过setLoaderClass改变loaderClass的值来指定自定义的类加载器,但是这个自定义的类加载必须从WebappClassLoader派生过来;
3、WebappLoader的run方法运行在一个线程中,每隔checkInterval秒的时间就扫描一次路径下的文件(checkInterval默认是15秒,可以改变),如果有文件发生改变了,则创建一个新的线程,在线程中调用Context.reload方法重新加载类(首先调用notifyContext生成一个WebappContextNotifier线程,然后在这个线程中调用Context.reload方法重新加载类);
4、webappClassLoader实现了reloader接口来实现类的自动重载,这个接口中最重要的方法modified,如果web应用程序中的某个servlet被修改了,modified会返回true,然后扫描线程会通知Context重新载入servlet类;
StandardContext默认是不支持自动重载servlet类的,要想开启这个功能,需要在server.xml文件中配置下面这样一行:
<Context path="/myApp" docBase="myApp" debug="0" reloadable="true"/>
5、WebappClassLoader作为一个自定义类载入器,考虑了优化和安全两方面,比如在resourceEntries中缓存以及功能载入过的类,在notFoundResources中缓存加载失败的类的名字,这样当再次加载同一个类,如果在resourceEntries中则直接返回,如果在notFoundResources中则直接抛出ClassNotFoundException异常;
缓存类资源的类是ResourceEntry,其中loadedClass存储了类的字节流,lastModified存储了最后一次修改时间;
6、基于安全性的考虑,WebappClassLoader不允许加载指定的类和包,代码如下:
7、WebappClassLoader.loadClass载入类时的流程:
a) 首先检查本地缓存,如果则直接返回;
b) 如果本地缓存没有,则检查上一层缓存,即调用java.lang.ClassLoader.findLoadedClass;
c) 若两个缓存都没有,则使用系统的类载入器加载;
d) 若启用了SecurityManager,则检查是否允许载入该类,如果是禁止的,则抛出ClassNotFoundException异常;
e) 如果标志位delefate打开,则调用父类载入器来载入,如果父类载入器为空,则使用系统类载入器载入;
f) 从当前仓库中载入相关类;
g) 如果当前仓库也没有,且delegate关闭,则使用父类载入器来载入,如果父类载入器为空,则使用系统类载入器载入;
h) 此时若仍未找到需要的类,则抛出ClassNotFoundException异常;