一、内置类加载器
1.跟加载器(c++代码的一段加载逻辑)
2.扩展类加载器 Launcher.ExtClassLoader
3.应用类加载器 Launcher.AppClassLoader
三个内置加载器是逻辑上的父子关系,并非Java语法上的继承关系,具体详见sun.misc.Launcher构造方法,ExtClassLoader实例通过传参设置为AppClassLoader的parent属性。
二、双亲委派
双亲委派的具体过程为,自下而上检查是否加载过某类,有则直接返回,没有则委派给父加载器(详见java.lang.ClassLoader#loadclass),加载过程自上而下(详见java.net.URLClassLoader#findClass)
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, false);
} else {
c = findBootstrapClassOrNull(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);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
protected Class<?> findClass(final String name)throws ClassNotFoundException {
final Class<?> result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
疑问:双亲委派为什么要为什么要向上委派再向下加载,而不直接从上往下加载?
应用程序自定义类大多是由自定义类加载器或者AppClassLoader加载,只需在第一次加载的时候走双亲委派,之后从下判断加载过即可直接返回。
三、打破双亲委派
1.SPI机制(后续再补充)
2.自定义类加载器
自定义类加载器继承ClassLoader,重写findClass方法(会调用defineClass)。自定义类加载器会默认以AppClassLoader为父加载器,具体实现是在ClassLoader的构造函数中默认给parent属性设置
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}
private static synchronized void initSystemClassLoader() {
if (!sclSet) {
if (scl != null)
throw new IllegalStateException("recursive invocation");
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
Throwable oops = null;
scl = l.getClassLoader();
try {
scl = AccessController.doPrivileged(
new SystemClassLoaderAction(scl));
} catch (PrivilegedActionException pae) {
oops = pae.getCause();
if (oops instanceof InvocationTargetException) {
oops = oops.getCause();
}
}
if (oops != null) {
if (oops instanceof Error) {
throw (Error) oops;
} else {
// wrap the exception
throw new Error(oops);
}
}
}
sclSet = true;
}
}
再参见Launcher内
public ClassLoader getClassLoader() {
return this.loader;
}
Launcher的构造函数内
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
即AppClassLoader。
打破双亲委派即不委派,使用自定义类加载器加载指定类,重写loadClass方法,修改委派逻辑,不调用parent的loadClass方法向上委派。
@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) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
注意:上述写法运行会报错Object not found,因为加载一个类的时候,会加载其父类,没有显式父类即以Object为父类,而Object是由内置类加载器加载,不向上委派则自定义类加载器找不到Object.class。此处做一个尝试,把JDK内部的Object.class文件复制到自定义的java.lang包下再次运行依然报错,因为类加载子系统的沙箱安全机制禁止(详见源码内加载逻辑之前的一系列安全校验)。这里简单处理根据类名做区别:
@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) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
if (!name.startsWith("com.zs.classload")){//自定义类的包名
c = this.getParent().loadClass(name);
}else {
c = findClass(name);
}
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
这样就实现指定类仅由自定义类加载器加载,不向上委派,JDK原生类依然遵循双亲委派模式。