We are always talking about the parent delegation model in ClassLoader design (if you are not familiar with this phrase, you may like to refer here), but we seldom ask why such a design? What the intention behind it? In this blog, we will elaborate on this aspect.
Parent Delegation in Detail
The ClassLoader is the abstraction that is responsible to load the core of Java world – class. We give a name of class to ClassLoader
and it give us the class
instance1.
public Class<?> loadClass(String name) throws ClassNotFoundException {}
The delegation model of Java ClassLoader
requires that any request for a class loader to load a given class should be first delegated to its parent class loader before do it by itself.
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
The parent class loader, in turn, goes through the same process and ask its parent recursively. This chain of delegation continues through to the bootstrap class loader (also known as the primordial or system class loader). If a class loader’s parent can load a given class, it returns that class. Otherwise, the class loader attempts to load the class itself.
Hierarchy
At the top of the hierarchy is the bootstrap class loader as we have introduced, which is responsible for loading only the classes that are from the core Java API, like String.class
, Array
. These classes are the most trusted and are used to bootstrap the JVM.
The extensions class loader can load classes that are standard extensions packages in the extensions directory.
The application class loader can load classes from the local file system, and will load files from the CLASSPATH
. If we want to customize the class loading process, like getting a class file from network, we can extend application class loader.
Customization & Security
In order to do the customization of class loading, ClassLoader
have to leave some space for user. At the same time, Java have to enforce security limit. User shouldn’t be able to load a fake String
class and disturb the normal usage. So we have the parent delegation requirement as findClass
suggested: ask parent first, then do it by itself.
/**
* This method should be overridden by class loader implementations that
* follow the delegation model for loading classes, and will be invoked by
* the {@link #loadClass <tt>loadClass</tt>} method after checking the
* parent class loader for the requested class.
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
So can malicious user violate this requirement? It seems that we can directly return our class because our loadClass
will override the father’s, right?
Yes and no. We indeed override the loadClass
method, but we can’t return arbitrary class. It is because that we can’t really init
any class and we have no access to its constructor. We can only init by defineClass
:
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{}
What we can customize is the name of class and content (byte[]
), that all. Security limit is enforced by father class.
Because class loading is always delegated first to the parent of the class loading hierarchy, the most trusted repository (the core API) is checked first, followed by the standard extensions, then the local files that are on the class path. Finally, classes that are located in any repository that your own class loader can access, are accessible. This system prevents code from less-trusted sources from replacing trusted core API classes by assuming the same name as part of the core API.
PS
Can following code run? Test it and you can understand some of security limit what ClassLoader
does.
package java.lang;
/**
* @author zzt
*/
public class ClassloaderCheck {
public static void main(String[] args) {
}
}
Written with StackEdit.
We may wonder: the
ClassLoader
is also a class, how it is loaded? It is kind ofchicken or egg
problem. It is a very common problem in programming related areas, another example is the process creation, which is usually done byfork
an existing process. It is usually solved by introducing some extra element: in Java, the firstClassLoader
is loaded by c++, called bootstrap. ↩︎
评论
发表评论