一、概念
JDK 默认提供了如下几种ClassLoader
-
List item
Bootstrp loader
Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类。 -
List item
ExtClassLoader
Bootstrp loader加载ExtClassLoader,并且将ExtClassLoader的父加载器设置为Bootstrp loader.ExtClassLoader是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。 -
List item
AppClassLoader
Bootstrp loader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为 ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是 sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。
综上所述,它们之间的关系可以通过下图形象的描述:
普及一点基础知识
(1)rt.jar:Java基础类库,也就是Java doc里面看到的所有的类的class文件,默认就在Root Classloader的加载路径里面的,而在Claspath配置该变量是不需要的;同时jre/lib目录下的其他jar:jce.jar、jsse.jar、charsets.jar、resources.jar都在Root Classloader中
(2)tools.jar:是系统用来编译一个类的时候用到的,即执行javac的时候用到,javac XXX.java实际上就是运行
java -Calsspath=%JAVA_HOME%\lib\tools.jar xx.xxx.Main XXX.java
(3)dt.jar:d是关于运行环境的类库,主要是swing的包 在用到swing时最好加上。
注:dt.jar和tools.jar位于:%Java_Home%\lib\下,而rt.jar位于:%Java_Home%\jre\lib\下。JRE大家都知道是运行Java程序所不可缺少的运行环境,因此rt.jar放在JRE的lib下也就理所应当了。
二、基础
类加载过程详解
JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)
双亲委派机制(全盘负责委托机制)
某个特定的类加载器在接到加载类的请求时,首先将加载任务委托交给父类加载器,除非指定类加载器,父类加载器又将加载任务向上委托,直到最父类加载器,如果最父类加载器可以完成类加载任务,就成功返回,如果不行就向下传递委托任务,由其子类加载器进行加载。
双亲委派机制的好处:
保证java核心库的安全性(例如:如果用户自己写了一个java.lang.String类就会因为双亲委派机制不能被加载,不会破坏原生的String类的加载)
代理模式:
就是指定了类加载器,是先自己尝试加载,如果无法加载则向上传递。tomcat就是代理模式。
示例代码
//实例
package template;
public class LoaderTest {
public LoaderTest(){
System.out.println("创建了新对象");
}
public void test() {
System.out.println("测试方法");
}
}
//自定义ClassLoader
package classloader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
public class MyClassCloader extends ClassLoader {
private String rootPath;
public MyClassCloader(String rootPath){
this.rootPath = rootPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//check if the class have been loaded
Class<?> c = findLoadedClass(name);
if(c!=null){
return c;
}
//load the class
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}
else{
c = defineClass(name,classData, 0, classData.length);
return c;
}
}
private byte[] getClassData(String className){
String path = rootPath+"/"+className.replace('.', '/')+".class";
System.out.println(path);
InputStream is = null;
ByteArrayOutputStream bos = null;
try {
is = new FileInputStream(path);
bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int temp = 0;
while((temp = is.read(buffer))!=-1){
bos.write(buffer,0,temp);
}
return bos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
is.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
//测试类
package template;
import org.junit.Test;
import classloader.MyClassCloader;
public class SpringBeanTest {
public static void main(String[] args) {
ClassLoader c = SpringBeanTest.class.getClassLoader(); //获取Test类的类加载器
System.out.println(c); //sun.misc.Launcher$AppClassLoader@73d16e93
ClassLoader c1 = c.getParent(); //获取c这个类加载器的父类加载器
System.out.println(c1);//sun.misc.Launcher$ExtClassLoader@15db9742
ClassLoader c2 = c1.getParent();//获取c1这个类加载器的父类加载器
System.out.println(c2 );//输出null,因为BooStrClassLoader是c编写的
}
@Test
public void testClassLoader() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
MyClassCloader loader = new MyClassCloader("E:/My folder/JavaTest");
Class<?> c = loader.loadClass("template.LoaderTest");
Class ss= Class.forName("template.LoaderTest");
ss.newInstance();
}
以上资料仅供学习参考,整理不易。