Class loader and parent delegation mechanism 、 The relationship of the whole Commission mechanism
One . Class loader
1.1 Class loader rt.jar in sun.misc.Launcher.class
public class Launcher {
static class AppClassLoader extends URLClassLoader {
}
static class ExtClassLoader extends URLClassLoader {
}
}
JVM When loading any class , Delegate to top loader Bootstrap ClassLoader, Bootstrap Will load Launcher.class( No compilation required , It exists directly in rt.jar in )
That is to say Bootstrap Start up , AppClassLoader,ExtClassLoader Has become available
1.2 The base class of two kinds of loaders URLClassLoader
public class URLClassLoader extends SecureClassLoader implements Closeable {
}
public class SecureClassLoader extends ClassLoader {
}
1.3 native Bottom loading .class file ClassLoader.defineClass()
protected final Class<?> defineClass(String name, byte[] b, int off, int len){
return defineClass(name, b, off, len, null);
}
native Method to read an array of bytes , That’s what we usually call bytecode files , You can also read a resource based on the byte stream
native Native supports loading compiled .class file , In network transmission .class file , Even myself new Out of the byte array
Two . Parent delegate mechanism
2.1 One of the two parents appointed : loadClass()
2.1.1 ClassLoader.loadClass()
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
// native Read whether the class has been loaded
private native final Class<?> findLoadedClass0(String name);
protected Class<?> loadClass(String name, boolean resolve) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// Recursively looking for the parent loader
if (parent != null) {
c = parent.loadClass(name, false);
else
//parent == null when Access to the top loader
c = findBootstrapClassOrNull(name);
} catch (ClassNotFoundException e) {
}
if (c == null) {
// findClass find class File will be called after defineClass Method to import bytecode into method area , Caching results at the same time
c = findClass(name);
}
}
}
AppClassLoader and ExtClassLoader All use the rule of changing parental delegation
2.1.2 The execution directory of different classloaders
- start-up (Bootstrap) Class loader : Responsible for loading %Java_Home%/lib The following core class library or -Xbootclasspath Option specified jar package . from native Method to implement the loading process , The program can’t get the loader directly , Nothing can be done with it .
- Expand (Extension) Class loader : The extension class loader is created by sun.misc.Launcher.ExtClassLoader Realized . Responsible for loading %Java_Home%/lib/ext Or by system variable -Djava.ext.dir Specify the class library in the location . The program can access and use the extension class loader .
- System (System) Class loader : The system class loader is created by sun.misc.Launcher.AppClassLoader Realized , Also called application class loader . Responsible for loading system classpath -classpath or -Djava.class.path The class library in the directory indicated by the variable . The program can access and use the system classloader .
2.2 Two of them are appointed by parents :findClass()
2.2.1 ClassLoader.findClass()
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
2.2.2 AppClassLoader and ExtClassLoader Of findClass()
// Search path
private final URLClassPath ucp;
protected Class<?> findClass(final String name) {
final Class<?> result;
result = AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>(){
public Class<?> run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
// Find your own path class file
Resource res = ucp.getResource(path, false);
if (res != null) {
// If you find it, inject
return defineClass(name, res);
} else {
return null;
}
}
}, acc);
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
2.3 Why a parental delegation mechanism is needed ?
2.3.1 Solve the problem of dependence
Make sure that the core class loads first , The core class loads , Other user-defined classes don’t have dependency problems .
2.3.2. Don’t reload , Ensure that the core class is not tampered with
Bootstrap ClassLoader It’s a classloader that’s invisible to users , It’s also the top loader ,%Java_Home%/lib The following core classes will load first , If the user defines java.lang.Integer
, The program can only use AppClassLoader
To load , Once executed to its parent class URLClassLoader Of
findClass()` The statement in , Will skip loading the class with the same name
Class<?> c = findLoadedClass(name);
It is worth mentioning that , Class loader loads… In the network java.lang.Integer
, Another mechanism is needed to detect
URLClassLoader extends SecureClassLoader
, jdk Self contained classLoader All have this ability
public final Class<?> loadClass(String name, boolean resolve){
// First check if we have permission to access the package. This
// should go away once we've added support for exported packages.
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
int i = name.lastIndexOf('.');
if (i != -1) {
sm.checkPackageAccess(name.substring(0, i));
}
}
return super.loadClass(name, resolve);
}
Using a custom class loader does not inherit SecureClassLoader It can be realized with The core class coexists with the same name ,
reason :JVM The identity class is exactly the same , You need a full name limit + Load the class loader of this class
But generally not , So there is a saying on the Internet :
It is recommended to customize class loader inheritance URLClassLoader One of the reasons is to inherit SecureClassLoader Try to be as safe as possible .
2.3.3 Can you not follow the parental delegation ?
Certainly.
- Customize a classloader and override
loadClass
Can not follow - A simpler way , Use Class.forName( All names are limited ) , In fact, it’s the overall delegation mechanism
3、 … and . JVM How to load compiled .class file
Class.forName()
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
-
By default, the caller’s ClassLoader To load a class
-
You can also specify the specified classloader
ClassLoader.loadClass()
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// Find out from the cache if the class is loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
}
} catch (ClassNotFoundException e) {
// Throw exceptions to prove the present ClassLoader The target class does not exist in the directory
// Use catch The scheduler continues to execute
}
if (c == null) {
// findClass Expose to subclass implementation , Make it possible to break parental delegation
c = findClass(name);
}
}
return c;
}
}
The user to use ClassLoader.loadClass() It’s essentially using the parental delegation model
findClass(name) Combined findClass(name), findClass() It’s just an extension of parental delegation , You can expand the search path of the class by yourself
Four . Break the parental delegation mechanism
4.1 Class.forName()
As mentioned above ,class.forName()
It’s called native Method , Don’t go with parental delegation code
4.2 With the help of Launcher Construction method and setContextClassLoader()
public class Launcher {
static class AppClassLoader extends URLClassLoader {
}
static class ExtClassLoader extends URLClassLoader {
}
public Launcher() {
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
}
// hold AppClassLoader Set to thread loader
Thread.currentThread().setContextClassLoader(this.loader);
}
}
Launcher When instantiated, will AppClassLoader Set in the AppClassLoader ExtClassLoader In the thread context of
That is, after loading the core class , Before loading custom classes , The thread that loads the class , Can get AppClassLoader
setContextClassLoader(this.loader); stay SPI Breaking the parental delegation model plays a key role