ClassLoader源码分析文章中,已经对
ClassLoader的部分方法进行了分析,具体可查看:水煮Java虚拟机 - ClassLoader源码分析(一),本文将继续分析余下的几个方法:
defineClass resolveClass getResource definePackage
2.8 defineClass
#defineClass(String name, byte[] b, int off, int len)方法负责将二进制的字节码转换为
java.lang.Class对象,源码如下:
@Deprecatedprotected 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 arraybyte[] 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;// 判断父类加载器是否为nullif (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);// 获取资源对应的 URLreturn 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 赋值给 pkgpkg = pkg2;}}}}return pkg;}#getPackages()
方法,用于获取当前 ClassLoader 和它的父类所定义的所有的 Package,其实现如下:protected Package[] getPackages() {Map<String, Package> map;// 对 packages 对象加锁,创建 mapsynchronized (packages) {map = new HashMap<>(packages);}Package[] pkgs;// 判断父类加载器是否为空,如果不为空,直接从父类获取if (parent != null) {pkgs = parent.getPackages();} else {// 否则获取系统的Packagespkgs = 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进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




