JDK描述
public abstract class ClassLoader
extends Object
类加载器是负责加载类的对象。ClassLoader类是一个抽象类。如果给定类的Binary Name,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”,返回二进制流。
每个 Class 对象都包含一个对定义它的ClassLoader的引用。
数组类的 Class 对象不是由类加载器创建的,而是由Java运行时根据需要自动创建。数组类的类加载器由 Class.getClassLoader()返回,该加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器。
应用程序需要实现 ClassLoader 的子类,以扩展 Java 虚拟机动态加载类的方式。
类加载器通常由安全管理器使用,用于指示安全域。
ClassLoader 类使用委托模型来搜索类和资源。每个ClassLoader实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机的内置类加载器(称为 "bootstrap class loader")本身没有父类加载器,但是可以将它用作ClassLoader实例的父类加载器。
通常情况下,Java 虚拟机以与平台有关的方式,从本地文件系统中加载类。例如,在 UNIX 系统中,虚拟机从 CLASSPATH 环境变量定义的目录中加载类。
然而,有些类可能并非源自一个文件;它们可能源自其他来源(如网络),也可能是由应用程序构造的。defineClass方法将一个byte数组转换为Class类的实例。这种新定义的类的实例可以使用 Class.newInstance 来创建。
类加载器所创建对象的方法和构造方法可以引用其他类。为了确定引用的类,Java 虚拟机将调用最初创建该类的类加载器的loadClass方法。
例如,应用程序可以创建一个网络类加载器,从服务器中下载类文件。示例代码如下所示:
ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();
......
网络类加载器子类必须定义方法findClass和loadClassData,以实现从网络加载类。下载组成该类的字节后,它应该使用方法 defineClass 来创建类实例。示例实现如下:
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}
Binary Name 二进制名称
按照《Java Language Specification》的定义,任何作为 String 类型参数传递给 ClassLoader 中方法的类名称都必须是一个二进制名称。
有效类名称的示例包括:
"java.lang.String"
"javax.swing.JSpinner$DefaultEditor"
JSpinner的内部类DefaultEditor
"java.security.KeyStore$Builder$FileBuilder$1"
KeyStore的内部类Builder的内部类FileBuilder
"java.net.URLClassLoader$3$1"
KeyStore的内部类URLClassLoader的第三个匿名内部类的第一个匿名内部类
代码分析
String的变量
- private boolean initialized = false; 如果成功初始化并通过安全检查,这个变量会设置为true;、
- private ClassLoader parent; 保存父加载器的引用
- private Hashtable package2certs = new Hashtable(11); 哈希表,证书?
- java.security.cert.Certificate[] nocerts; 共享所有包中未签名的类
- private Vector classes = new Vector(); 保存所有被加载的类,JVM会调用addClass(Class c)方法加入
- private Set domains = new HashSet(); 本加载器加载的类的初始被保护的domains
- private HashMap packages = new HashMap(); 保存包名到Package类的映射
- private static ClassLoader scl; 系统类加载器
- private static boolean sclSet; 设置系统类加载器后该值为true
字符数组和offset及count都是final的,必须在构造方法中初始化,之后不能修改。
构造方法
- protected ClassLoader(ClassLoader parent) 构造方法,parent是指导的父类加载器
- protected ClassLoader()
protected ClassLoader(ClassLoader parent) {
SecurityManager security = System.getSecurityManager();//获取安全管理器
if (security != null) {
security.checkCreateClassLoader();
}
this.parent = parent;
initialized = true;
}
protected ClassLoader() {
SecurityManager security = System.getSecurityManager();//获取安全管理器
if (security != null) {
security.checkCreateClassLoader();//检查SecurityConstants.CREATE_CLASSLOADER_PERMISSION
}
this.parent = getSystemClassLoader();//不指定父类加载时,默认为scl,系统类加载器 AppClasssLoader
initialized = true;
}
其他方法
加载类
- public Class<?> loadClass(String name) 加载类,name为binary name
- protected synchronized Class<?> loadClass(String name, boolean resolve) 加载类,name为binary name,resolve为是否需要解析【加载-连接(验证-准备-解析)-初始化】
- loadClassInternal JVM会调用这个类来加载Class,内部调用了loadClass()方法
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name); //掉用本地方法findLoadedClass0查找是否已经加载
if (c == null) {
try {
if (parent != null) {//如果父加载器不为空,则调用父加载器的loadClass方法
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);//若父加载器为空,调用根加载器加载
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name); //如果父加载器都没找到则走到了这里,调用本类的findClass查找Class类
}
}
if (resolve) {
resolveClass(c); //调用本地方法resolveClass0解析
}
return c;
}
- protected Class<?> findClass(String name) 查找类,给定binary name返回Class类,一般会先读取一个字节数组,再调用defineClass
- protected final Class<?> defineClass(byte[] b, int off, int len) 将字节数组转为Class类的实例。在Class被使用执行必须进行解析,这个方法不推荐。
- protected final Class<?> defineClass(String name, byte[] b, int off, int len)
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
check(); //检查initialized变量,构造函数成功会设置为true
protectionDomain = preDefineClass(name, protectionDomain);
Class c = null;
String source = defineClassSourceLocation(protectionDomain);
try {
c = defineClass1(name, b, off, len, protectionDomain, source); //native方法
} catch (ClassFormatError cfe) {
c = defineTransformedClass(name, b, off, len, protectionDomain, cfe, source);
}
postDefineClass(c, protectionDomain);
return c;
}
//[checkName],检查name是否为一个有效的binary name,
private boolean checkName(String name) {
if ((name == null) || (name.length() == 0))
return true;
//若包含/或者 VM不允许数组语法时以[开头
if ((name.indexOf('/') != -1)
|| (!VM.allowArraySyntax() && (name.charAt(0) == '[')))
return false;
return true;
}
URLClassLoader
private final URLClassPath ucp;
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
try {
return 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 {
throw new ClassNotFoundException(name);
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
}
相关问题
ClassLoader实际使用场景
热部署,如tomcat开启热部署后,class file被修改时会重新加载。
代码保护
加解密
注意问题
自定义加载器加载的类不能适java/javax和其他子包的类
相同的Class=相同的ClassName+ PackageName + ClassLoader
总结
- ProtectionDomain 保护域 [http://www.2cto.com/kf/201212/174519.html]
- ProtectionDomain [http://www.cnblogs.com/f1194361820/p/4189269.html]