概述
用如下代码测试
public class TestClassLoader {
public static void main(String[] arsg){
TestClassLoader loader=new TestClassLoader();
String s="test";
System.out.println(s.getClass().getClassLoader());
System.out.println(loader.getClass().getClassLoader().getParent());
System.out.println(loader.getClass().getClassLoader());
}
}
发现classloader有三种
null
jdk.internal.loader.ClassLoaders$PlatformClassLoader@2530c12
jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b
3个ClassLoader类的关系及实例化过程
其中null其实为BootClassLoader,我们查看源代码可以发现其实有三个loader
private static final BootClassLoader BOOT_LOADER;
private static final PlatformClassLoader PLATFORM_LOADER;
private static final AppClassLoader APP_LOADER;
三个loader类的关系如下图,可以看出他们顶级父类都ClassLoader
在查看这3个的实例化过程,我可以看出PLATFORME_LOADER的parent是BOOT_LOADER、APP_LOADER的parent是PLATFORM_LOADER
static {
ArchivedClassLoaders archivedClassLoaders = ArchivedClassLoaders.get();
if (archivedClassLoaders != null) {
// assert VM.getSavedProperty("jdk.boot.class.path.append") == null
BOOT_LOADER = (BootClassLoader) archivedClassLoaders.bootLoader();
setArchivedServicesCatalog(BOOT_LOADER);
PLATFORM_LOADER = (PlatformClassLoader) archivedClassLoaders.platformLoader();
setArchivedServicesCatalog(PLATFORM_LOADER);
} else {
// -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute
String append = VM.getSavedProperty("jdk.boot.class.path.append");
URLClassPath ucp = (append != null && !append.isEmpty())
? new URLClassPath(append, true)
: null;
BOOT_LOADER = new BootClassLoader(ucp);
PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);
}
// A class path is required when no initial module is specified.
// In this case the class path defaults to "", meaning the current
// working directory. When an initial module is specified, on the
// contrary, we drop this historic interpretation of the empty
// string and instead treat it as unspecified.
String cp = System.getProperty("java.class.path");
if (cp == null || cp.isEmpty()) {
String initialModuleName = System.getProperty("jdk.module.main");
cp = (initialModuleName == null) ? "" : null;
}
URLClassPath ucp = new URLClassPath(cp, false);
if (archivedClassLoaders != null) {
APP_LOADER = (AppClassLoader) archivedClassLoaders.appLoader();
setArchivedServicesCatalog(APP_LOADER);
APP_LOADER.setClassPath(ucp);
} else {
APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
ArchivedClassLoaders.archive();
}
}
3个classLoader的loader类的过程
其他们都是调用父类的BuiltinClassLoader的方法,其实就是对顶级类ClassLoader中loadClass方法重写
@Override
protected Class<?> loadClass(String cn, boolean resolve)
throws ClassNotFoundException
{
Class<?> c = loadClassOrNull(cn, resolve);
if (c == null)
throw new ClassNotFoundException(cn);
return c;
}
protected Class<?> loadClassOrNull(String cn, boolean resolve) {
synchronized (getClassLoadingLock(cn)) {
// check if already loaded
Class<?> c = findLoadedClass(cn);
if (c == null) {
// find the candidate module for this class
LoadedModule loadedModule = findLoadedModule(cn);
if (loadedModule != null) {
// package is in a module
BuiltinClassLoader loader = loadedModule.loader();
if (loader == this) {
if (VM.isModuleSystemInited()) {
c = findClassInModuleOrNull(loadedModule, cn);
}
} else {
// delegate to the other loader
c = loader.loadClassOrNull(cn);
}
} else {
// check parent
if (parent != null) {
c = parent.loadClassOrNull(cn);
}
// check class path
if (c == null && hasClassPath() && VM.isModuleSystemInited()) {
c = findClassOnClassPathOrNull(cn);
}
}
}
if (resolve && c != null)
resolveClass(c);
return c;
}
}
从以上代码逻辑我们可以看出如果当前classloader是APP_LOADER,他先让其PARENT(PLATFORM_LOADER)的PARENT(BOOT_LOADER)去找类,如果BOOT_LOADER找不到类,在让PLATFORM_LOADER去找,若找不到在让APP_LOADER去找,如果APP_LOADER加载不到类,会义PLATFORM_LOADER加载类,如果也加载不到在让APP_LOADER加载,这就是传说的JAVA类加载双亲委派机制
实践
根据以上原理如查我们自已定义定义目录下加载类
package java1.lang1;
public class Integer1 {
public String toString(String s){
System.out.println(s);
return "ni mei";
}
public static void main(String[] args){
String s=new String();
s.toString();
}
}
并将此类从project中删除,并class文件放在“G:\CPTEST\java1\lang1”下,如何实现呢,需要自定义如下类加载器
package std.loader;
import java.io.*;
public class MyDefindClassLoader extends ClassLoader {
private final ClassLoader parent=this.getClass().getClassLoader();
private String classPath;
public MyDefindClassLoader(String path){
this.classPath=path;
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
System.out.println("mydefineClassLoader loading class "+name);
String realpath=this.classPath+"/"+name.replace(".","/")+".class";
File f=new File(realpath);
FileInputStream inputStream=new FileInputStream(f);
byte[] b=new byte[(int) f.length()];
inputStream.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
}
用如下代码进行TEST
package std.loader;
import java.lang.reflect.Method;
public class TestClassLoader {
public static void main(String[] arsg){
TestClassLoader loader=new TestClassLoader();
System.out.println(loader.getClass().getClassLoader().getParent().getParent());
System.out.println(loader.getClass().getClassLoader().getParent());
System.out.println(loader.getClass().getClassLoader());
MyDefindClassLoader myDefindClassLoader=new MyDefindClassLoader("G:/CPTEST");
try {
Class<?> integer1= myDefindClassLoader.loadClass("java1.lang1.Integer1");
Method method= integer1.getDeclaredMethod("toString",String.class);
method.invoke(integer1.newInstance(),"Are you sure jjj?");
} catch ( Exception e) {
e.printStackTrace();
}
System.out.println(myDefindClassLoader.getClass().getClassLoader());
}
}
执行日志如下:
null
jdk.internal.loader.ClassLoaders$PlatformClassLoader@2530c12
jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b
mydefineClassLoader loading class java1.lang1.Integer1
Are you sure jjj?
jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b
如果类Integer1 不从project中删除运行的如果下,主要原因是主要自定义的MyDefindClassLoader 中parent先从项目中找到了类,不会从自定义目录中去找。
null
jdk.internal.loader.ClassLoaders$PlatformClassLoader@2530c12
jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b
Are you sure jjj?
jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b