类装载器ClassLoader
Posted 练好本领,报国杀敌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类装载器ClassLoader相关的知识,希望对你有一定的参考价值。
1、类装载器的工作机制
类装载器就是类的字节码文件并构造出类在JVM内部表示对象的组件。在Java中,类装载器把一个类装入JVM中,需要以下步骤:
(1)装载:查找和导入Class文件
(2)链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的。
校验:检查载入Class文件数据的正确性。
准备:给类的静态变量分配存储空间。
解析:将符号引用转换成直接引用。
(3)初始化:对类的静态变量、静态代码块执行初始化工作。
类装载工作由ClassLoader及其子类负责。ClassLoader是一个重要的Java运行时系统组件,它负责在运行时查找和装入Class字节码文件。JVM在运行时会产生3个ClassLoader:根装载器、ExtClassLoader(扩展类装载器)和AppClassLoader(应用类装载器)。其中根装载器不是ClassLoader的子类,它使用C++语言编写,因而在Java中看不到它,根装载器负责装载JRE的核心类库,如JRE目标下的rt.jar、charsets.jar等。ExtClassLoader和AppClassLoader都是ClassLoader的子类,其中ExtClassLoader负责装载JRE扩展目录ext中的JAR类包;AppClassLoader负责装载Classpath路径下的类包。
这三个类装载器之间存在父子层级关系,即根装载器是ExtClassLoader的父装载器,ExtClassLoader是AppClassLoader的父装载器。在默认情况下,使用AppClassLoader装载应用程序的类。
实验代码:
public static void main(String[] args) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); System.out.println("current loader:" + loader); System.out.println("parent loader:" + loader.getParent()); System.out.println("grandparent loader:" + loader.getParent().getParent()); }
输出:
current loader:sun.misc.Launcher$AppClassLoader@18b4aac2 parent loader:sun.misc.Launcher$ExtClassLoader@66d3c617 grandparent loader:null
说明:
根装载器在Java中访问不到,所以返回null
扩展:
JVM装载类时使用“全盘负责委托机制”
全盘负责:是指当一个ClassLoader装载一个类是,除非显式地使用另一个ClassLoader,该类所依赖的及引用的类也由这个ClassLoader载入;
委托机制:是指先委托父装载器寻找目标类,只有在找不到的情况下才能从自己的类路径下查找并装载目标类。
这样的设计是从安全角度考虑的,试想,如果有人编写了一个恶意的基础类(如java.lang.String)并装载到JVM中,将会引起多么可怕的后果?
实战经验:
在此我提供一个工具类,通过改类可以方便的查看JVM从哪个类包中加载指定类:
访问方式:http://localhost:8080/srcAdd.jsp?className=java.net.URL
工具类页面:
<%@page contentType="text/html; charset=GBK" %> <%@page import="java.security.*,java.net.*,java.io.*" %> <%! public static URL getClassLocation(final Class cls) { if (cls == null) throw new IllegalArgumentException("null input: cls"); URL result = null; final String clsAsResource = cls.getName().replace(\'.\', \'/\').concat(".class"); final ProtectionDomain pd = cls.getProtectionDomain(); // java.lang.Class contract does not specify if \'pd\' can ever be null; // it is not the case for Sun\'s implementations, but guard against null // just in case: if (pd != null) { final CodeSource cs = pd.getCodeSource(); // \'cs\' can be null depending on the classloader behavior: if (cs != null) result = cs.getLocation(); if (result != null) { // Convert a code source location into a full class file location // for some common cases: if ("file".equals(result.getProtocol())) { try { if (result.toExternalForm().endsWith(".jar") || result.toExternalForm().endsWith(".zip")) result = new URL("jar:".concat(result.toExternalForm()) .concat("!/").concat(clsAsResource)); else if (new File(result.getFile()).isDirectory()) result = new URL(result, clsAsResource); } catch (MalformedURLException ignore) { } } } } if (result == null) { // Try to find \'cls\' definition as a resource; this is not // document.d to be legal, but Sun\'s implementations seem to //allow this: final ClassLoader clsLoader = cls.getClassLoader(); result = clsLoader != null ? clsLoader.getResource(clsAsResource) : ClassLoader.getSystemResource(clsAsResource); } return result; } %> <html> <head> <title>srcAdd.jar</title> </head> <body bgcolor="#ffffff"> 使用方法,className参数为类的全名,不需要.class后缀,如 srcAdd.jsp?className=java.net.URL <% try { String classLocation = null; String error = null; String className = request.getParameter("className"); classLocation = "" + getClassLocation(Class.forName(className)); if (error == null) { out.print("类" + className + "实例的物理文件位于:"); out.print("<hr>"); out.print(classLocation); } else { out.print("类" + className + "没有对应的物理文件。<br>"); out.print("错误:" + error); } } catch (Exception e) { out.print("异常。" + e.getMessage()); } %> </body> </html>
工具类:
在IDEA断点调试的时候,按Alt+F8,弹出Evluate Expression对话框,在Expression处输入:ClassLocationUtils.where(<类名>.class) 即可获知当前类是从哪个jar包中加载的。
/** * tools to find which jar does the class come from * * @author : chenxh,ascend */ public class ClassLocationUtils { /** * find the location of the class come from * * @param cls Class * @return String */ public static String where(final Class cls) { if (cls == null) throw new IllegalArgumentException("null input: cls"); URL result = null; final String clsAsResource = cls.getName().replace(\'.\', \'/\').concat(".class"); final ProtectionDomain pd = cls.getProtectionDomain(); if (pd != null) { final CodeSource cs = pd.getCodeSource(); if (cs != null) result = cs.getLocation(); if (result != null && "file".equals(result.getProtocol())) { try { if (result.toExternalForm().endsWith(".jar") || result.toExternalForm().endsWith(".zip")) result = new URL("jar:".concat(result.toExternalForm()) .concat("!/").concat(clsAsResource)); else if (new File(result.getFile()).isDirectory()) result = new URL(result, clsAsResource); } catch (MalformedURLException ignore) { } } } if (result == null) { final ClassLoader clsLoader = cls.getClassLoader(); result = clsLoader != null ? clsLoader.getResource(clsAsResource) : ClassLoader.getSystemResource(clsAsResource); } return result.toString(); } }
码字不易,尊重原创:http://www.cnblogs.com/adeng/p/7596818.html
以上是关于类装载器ClassLoader的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段