1、Classloader的作用
1.1、Classloader加载类
classloader加载雷猴并把类型信息保存到方法区后,会创建一个Class对象(class的元数据
),存放在堆区的
1.2、确保class类唯一性
- 类加载器
- 类名
识别方式: ClassLoader Instance id+PackageName+ClassName
2、Classloader加载机制
2.1、 Classloader的双亲委派加载机制
image.png- 加载jvm的类由BootstrapClassLoader
- 加载jvm扩展类由ExtClassLoader
- 加载CLASSPATH下的类使用AppClassLoader
源代码如下: ClassLoader的loadClass方法保证了双亲委派机制,先从父类中加载,再从子类中加载
package java.lang.ClassLoader;
import ....
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First,在虚拟机内存中查找是否已经加载过此类...类缓存的主要问题所在!!!
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//先让上一层加载器进行加载
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
//调用此类加载器所实现的findClass方法进行加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
2.2、 自定义Classloader
2.1、classloader的方法说明
名称 | 说明 | 备注 |
---|---|---|
findClass(String name) | 查找类,如果不想破坏双亲委派机制,可以重写该类 | |
loaderClass(String name) | 查找类,重写该方法会破坏双亲委派的加载机制,也可以代码层面保证不破坏 | |
findLoadedClass(String name) | 确定类是否加载 | |
definclass(String name, byte[] b, int off, int len) | 返回类 |
在加载指定路径包时破坏双亲委派加载机制
package com.tianzehao.test;
import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
public class ClassTest1 extends ClassLoader {
private String libJarPath="";
public ClassTest1(String filename) {
super(ClassTest1.class.getClassLoader());
this.libJarPath = filename;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
JarInputStream jarInput =null;
String resName = name.replace(".", "/") + ".class";
if(name.startsWith("com.tianzehao")){
try {
InputStream fileInputStream = new FileInputStream(libJarPath);
try {
jarInput = new JarInputStream(fileInputStream);
JarEntry entry = null;
while ((entry = jarInput.getNextJarEntry()) != null) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
int chunk = 0;
byte[] data = new byte[256];
if( entry.getName().equals(resName)){
while (-1 != (chunk = jarInput.read(data))) {
bytes.write(data, 0, chunk);
}
byte[] tmpByte = bytes.toByteArray();
return defineClass(name,tmpByte,0,tmpByte.length);
}
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
return super.loadClass(name);
}
}
2.3、 自定义Classloader说明
自定义classloader加载机制不在双亲委派之列,即父类是用自定义classloader,子类不会自动使用自定义classloader加载,需要自己通过classloader加载
如果一个包中依赖另一个类(AppClassLoader父类无法加载的包)需要都用自定义ClassLoader进行加,不然找不到包
demo
package com.tianzehao.test;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
public class App {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
String path = System.getProperty("user.dir") + File.separator + "asd" + File.separator + "javaTest-1.0-SNAPSHOT.jar";
String path1 = System.getProperty("user.dir") + File.separator + "asd" + File.separator + "hiveTest-1.0-SNAPSHOT.jar";
System.out.println(path);
// 这里如果不加载两个测试类,会报一个类找不到的
ClassTest classloader = new ClassTest(new URL[]{new URL("file:" + path),new URL("file:" + path1)});
// ClassTest1 classloader = new ClassTest1(path);
Class clazz = classloader.loadClass("com.tianzehao.A");
Object a = clazz.newInstance();
Method runMethod = clazz.getMethod("run");
runMethod.invoke(a);
}
}
网友评论