0%

上一节 我们写了一个类加载器的实现,其中一个重要的方法是findClass,看一下它的介绍:
https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#loadClass(java.lang.String,%20boolean)

Read more »

上一节走读了类加载器的Java doc,这一节我们实现一个自定义的类加载器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<!-- more -->
public class MyTest16 extends ClassLoader {

private String classLoaderName;
private final String fileExtension = ".class";

public MyTest16(String classLoaderName){
/**
父类ClassLoader的构造器
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
**/
//使用系统类加载器作为当前类的父类委托加载器
super();
this.classLoaderName = classLoaderName;
}

public MyTest16(ClassLoader classLoader,String classLoaderName){
/*
父类ClassLoader带参数的构造器
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
*/
//使用自定义的类加载器作为当前类的父类委托加载器
super(classLoader);
this.classLoaderName = classLoaderName;
}

private byte[] loadClassData(String name ){
InputStream inputStream = null;
ByteArrayOutputStream baos = null;
byte [] bytes = null;

try{
this.classLoaderName = this.classLoaderName.replace(".","/");
baos = new ByteArrayOutputStream();
int ch = 0;
while (-1 !=(ch = inputStream.read())){
baos.write(ch);
}
bytes = baos.toByteArray();
}catch (Exception e){
e.printStackTrace();
}finally {
try{
inputStream.close();
baos.close();
}catch (Exception e){
e.printStackTrace();
}
}
return bytes;
}

@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
byte [] data = loadClassData(className);//中间调用子类的findClass方法
return defineClass(className,data,0,data.length);
}

public static void test(ClassLoader classLoader) throws Exception{
Class<?> clazz = classLoader.loadClass("com.twodragonlake.jvm.classloader.MyTest");
Object object = clazz.newInstance();
System.out.println(object);
}

public static void main(String[] args) throws Exception {
MyTest16 myTest16 = new MyTest16(MyTest16.class.getClassLoader(),"myClassLoader");
MyTest16.test(myTest16);
}
}


输出:

1
com.twodragonlake.jvm.classloader.MyTest@1540e19d

这段程序打印出系统类加载器到最上层的加载器的结构关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyTest13 {
<!-- more -->
public static void main(String[] args) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);

while(null != classLoader){
classLoader = classLoader.getParent();
System.out.println(classLoader);
}

}
}

运行结果:

1
2
3
4
sun.misc.Launcher$AppClassLoader@18b4aac2 【系统类加载器】
sun.misc.Launcher$ExtClassLoader@1540e19d 【扩展类加载器】
null 【根类加载器】

可以看到最后输出的是null。
这行代码: ClassLoader classLoader = ClassLoader.getSystemClassLoader();得到系统类加载器,看一下doc说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* Returns the system class loader for delegation. This is the default
* delegation parent for new <tt>ClassLoader</tt> instances, and is
* typically the class loader used to start the application.
*返回一个针对委托的系统类加载器,并且他是默认新建类加载器实例的委托双亲(即自定义类加载器的父级,见下图),它是一个典型的启动应用的类加载器。
* <p> This method is first invoked early in the runtime's startup
* sequence, at which point it creates the system class loader and sets it
* as the context class loader of the invoking <tt>Thread</tt>.
*此方法在运行期的早期就会被调用,在这个时间点创建系统类的加载器,并且设定其为调用线程的上下文的一个类加载器。
* <p> The default system class loader is an implementation-dependent
* instance of this class.
*默认的系统类加载器与这个类实现相关的实例
* <p> If the system property "<tt>java.system.class.loader</tt>" is defined
* when this method is first invoked then the value of that property is
* taken to be the name of a class that will be returned as the system
* class loader. The class is loaded using the default system class loader
* and must define a public constructor that takes a single parameter of
* type <tt>ClassLoader</tt> which is used as the delegation parent. An
* instance is then created using this constructor with the default system
* class loader as the parameter. The resulting class loader is defined
* to be the system class loader.
* 如果设定了java.system.class.loader那么这个方法返回的就是java.system.class.loader设定的类加载器。这个类被系统类加载器加载,并且
* 定义一个公共的构造方法,接受一个ClassLoader参数用作为委托的双亲,用默认系统类类加载器作为构造器的参数,就会创造一个实例 ,所得到的就是系统类加载器
*/
//返回系统类加载器
@CallerSensitive
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}

这里写图片描述

getParent方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Returns the parent class loader for delegation. Some implementations may
* use <tt>null</tt> to represent the bootstrap class loader. This method
* will return <tt>null</tt> in such implementations if this class loader's
* parent is the bootstrap class loader.
* 返回父加载器用于委托,有些实现返回null用来表示根类加载器,如果一个类的父加载器是根加载器,那么这个方法将会返回null
*/
public final ClassLoader getParent() {
if (parent == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Check access to the parent class loader
// If the caller's class loader is same as this class loader,
// permission check is performed.
checkClassLoaderPermission(parent, Reflection.getCallerClass());
}
return parent;
}

parent 变量是ClassLoader的成员变量:

1
2
3
4
5
// The parent class loader for delegation
// Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
用于委托的双亲加载器,JVM将这个变量的偏移量进行了硬编码,,这样新的变量就要加载这个变量的后边
private final ClassLoader parent;

下一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MyTest14 {
public static void main(String[] args) throws IOException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String resourceName = "com/twodragonlake/jvm/classloader/MyTest13.class";
/*
Finds all the resources with the given name. A resource is some data
(images, audio, text, etc) that can be accessed by class code in a way
that is independent of the location of the code.
返回给定名字所有的资源,资源可以是(图片,音频,文本,等)可以被class字节码以一种与字节码位置无关的方式去访问,
classLoader.getResources(resourceName){....}
*/
Enumeration<URL> urls = classLoader.getResources(resourceName);
while(urls.hasMoreElements()){
System.out.println(urls.nextElement());
}

System.out.println("----------------");

Class<?> clazz = String.class;
System.out.println(clazz.getClassLoader());//自定义的类有系统类加载器加载
System.out.println("----------------");
clazz = MyTest14.class;
System.out.println(clazz.getClassLoader()); //由根类加载器加载 因为系统类加载器的加载目录包含rt目录

}
}

打印:

1
2
3
4
5
file:/E:/Study/intelIde/jvm_lecture/out/production/classes/com/twodragonlake/jvm/classloader/MyTest13.class
----------------
null
----------------
sun.misc.Launcher$AppClassLoader@18b4aac2

获取ClassLoader的方式:
获得当前类的ClassLoader:
class.getClassLoader();
获取当前线程上下文的ClassLoader:
Thread.currentThread().getContextClassLoader();
获得系统的ClassLoader:
ClassLoader.getSystemClassLoader()
获得调用者的ClassLoader:
DriverManager.getCallerClassLoader();