ClassLoader
源码分析文章中,已经对
ClassLoader
的部分方法进行了分析,具体可查看:水煮Java虚拟机 - ClassLoader源码分析(一),本文将继续分析余下的几个方法:
defineClass resolveClass getResource definePackage
2.8 defineClass
#defineClass(String name, byte[] b, int off, int len)
方法负责将二进制的字节码转换为
java.lang.Class
对象,源码如下:
@Deprecated
protected final Class<?> defineClass(byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(null, b, off, len, null);
}
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(name, b, off, len, null);
}
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
// 获取 ProtectionDomain 对象
protectionDomain = preDefineClass(name, protectionDomain);
// 获取关联的位置
String source = defineClassSourceLocation(protectionDomain);
// 调用本地方法 defineClass1 来
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
// 设置类的签名者信息
postDefineClass(c, protectionDomain);
return c;
}
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
int len = b.remaining();
// Use byte[] if not a direct ByteBufer:
// 判断是否使用的是直接内存
if (!b.isDirect()) {
// 判断是否为数组
if (b.hasArray()) {
return defineClass(name, b.array(),
b.position() + b.arrayOffset(), len,
protectionDomain);
} else {
// no array, or read-only array
byte[] tb = new byte[len];
b.get(tb); // get bytes out of byte buffer.
return defineClass(name, tb, 0, len, protectionDomain);
}
}
// 解析 ProtectionDomain 对象
protectionDomain = preDefineClass(name, protectionDomain);
// 获取类对应的位置
String source = defineClassSourceLocation(protectionDomain);
// 调用本地方法 defineClass2 定义类
Class<?> c = defineClass2(name, b, b.position(), len, protectionDomain, source);
// 设置类的签名者信息
postDefineClass(c, protectionDomain);
return c;
}
private native Class<?> defineClass0(String name, byte[] b, int off, int len,
ProtectionDomain pd);
private native Class<?> defineClass1(String name, byte[] b, int off, int len,
ProtectionDomain pd, String source);
private native Class<?> defineClass2(String name, java.nio.ByteBuffer b,
int off, int len, ProtectionDomain pd,
String source);
#preDefineClass(String name,ProtectionDomain pd)
方法,用于确定类的保护域,其定义如下:private ProtectionDomain preDefineClass(String name,
ProtectionDomain pd)
{
// 检查类名是否合法
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);
// Note: Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
// relies on the fact that spoofing is impossible if a class has a name
// of the form "java.*"
// 判断类名是否为null或者是否以 java. 开头
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
if (pd == null) {
pd = defaultDomain;
}
// 检查证书
if (name != null) checkCerts(name, pd.getCodeSource());
return pd;
}
#defineClassSourceLocation(ProtectionDomain pd)
方法用于获取类源文件的位置,如下:private String defineClassSourceLocation(ProtectionDomain pd)
{
// 获取 CodeSource 对象
CodeSource cs = pd.getCodeSource();
String source = null;
if (cs != null && cs.getLocation() != null) {
// 获取与此域关联的位置
source = cs.getLocation().toString();
}
return source;
}
#postDefineClass(Class<?> c, ProtectionDomain pd)
方法,用于设置类的签名者信息,如下:private void postDefineClass(Class<?> c, ProtectionDomain pd)
{
if (pd.getCodeSource() != null) {
// 获取证书集合
Certificate certs[] = pd.getCodeSource().getCertificates();
// 设置类的签名者
if (certs != null)
setSigners(c, certs);
}
}
2.9 resolveClass
#resolveClass(Class<?> c)
方法主要是用于完成指定 Class 对象的链接,其调用的是本地方法,如下:
protected final void resolveClass(Class<?> c) {
resolveClass0(c);
}
private native void resolveClass0(Class<?> c);
2.10 getResource
#getResource(String name)
方法,用于获取指定名称的资源,其实现如下:
public URL getResource(String name) {
URL url;
// 判断父类加载器是否为null
if (parent != null) {
// 从父类加载器中获取对应的资源
url = parent.getResource(name);
} else {
// 父类加载器为null,则从启动类加载器中获取对应的资源
url = getBootstrapResource(name);
}
// 若未获取到,则查找对应的资源
if (url == null) {
// findResource 返回null,需要子类重写
url = findResource(name);
}
return url;
}
#getBootstrapResource(String name)
方法,用于从启动类加载器查找指定资源,实现如下:private static URL getBootstrapResource(String name) {
// 获取启动类路径 URLClassPath 对象
URLClassPath ucp = getBootstrapClassPath();
// 创建 Resource 对象
Resource res = ucp.getResource(name);
// 获取资源对应的 URL
return res != null ? res.getURL() : null;
}
static URLClassPath getBootstrapClassPath() {
// 获取启动类路径
return sun.misc.Launcher.getBootstrapClassPath();
}
#findResource(String name)
方法,返回null,需要子类重写,定义如下:protected URL findResource(String name) {
return null;
}
2.11 definePackage
#definePackage()
方法,用于按照包名定义包对象
java.lang.Package
,代码如下:
protected Package definePackage(String name, String specTitle,
String specVersion, String specVendor,
String implTitle, String implVersion,
String implVendor, URL sealBase)
throws IllegalArgumentException
{
// 同步 packages 对象,它为 HashMap 类型
synchronized (packages) {
// 从 packages 中获取指定的 Package 对象
Package pkg = getPackage(name);
if (pkg != null) {
// 若不存在,直接抛出异常
throw new IllegalArgumentException(name);
}
// 新创建 Package 对象
pkg = new Package(name, specTitle, specVersion, specVendor,
implTitle, implVersion, implVendor,
sealBase, this);
// 将其添加到 packages 中
packages.put(name, pkg);
return pkg;
}
}
#getPackage(String name)
方法,用于从 packages 中获取指定的 Package 对象,其实现如下:protected Package getPackage(String name) {
Package pkg;
synchronized (packages) {
// 获取 Package 对象
pkg = packages.get(name);
}
// 若 pkg 为空
if (pkg == null) {
// 判断父类加载器是否为空
if (parent != null) {
// 父类加载器不为空,从父类加载器中获取 Package 对象
pkg = parent.getPackage(name);
} else {
// 若父类加载器为空,则从已加载的系统包中获取指定的 Package 对象
pkg = Package.getSystemPackage(name);
}
// 若 pkg 不为空
if (pkg != null) {
synchronized (packages) {
// 再次从 packages 中获取指定的 Package 对象
Package pkg2 = packages.get(name);
if (pkg2 == null) {
// 若仍然为空,则将 pkg 加入到 packages 中
packages.put(name, pkg);
} else {
// 否则,将 pkg2 赋值给 pkg
pkg = pkg2;
}
}
}
}
return pkg;
}
#getPackages()
方法,用于获取当前 ClassLoader 和它的父类所定义的所有的 Package,其实现如下:protected Package[] getPackages() {
Map<String, Package> map;
// 对 packages 对象加锁,创建 map
synchronized (packages) {
map = new HashMap<>(packages);
}
Package[] pkgs;
// 判断父类加载器是否为空,如果不为空,直接从父类获取
if (parent != null) {
pkgs = parent.getPackages();
} else {
// 否则获取系统的Packages
pkgs = Package.getSystemPackages();
}
if (pkgs != null) {
// 遍历获取到的 packages,添加至 map中
for (int i = 0; i < pkgs.length; i++) {
// 获取Package的名称,如果map中不存在,则添加至map中
String pkgName = pkgs[i].getName();
if (map.get(pkgName) == null) {
map.put(pkgName, pkgs[i]);
}
}
}
// 将 Map 对象转换为 Package[] 数组
return map.values().toArray(new Package[map.size()]);
}
参考资料
【1】ClassLoader 源码分析
【2】Java 安全模型介绍
【3】分布式Java应用基础与实践
【4】《jdk8u源码分析》sun.misc.Launcher
文章转载自然笑,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。