类装载器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());
    }
View Code

  输出:  

current loader:sun.misc.Launcher$AppClassLoader@18b4aac2
parent loader:sun.misc.Launcher$ExtClassLoader@66d3c617
grandparent loader:null
View Code

  说明:

  根装载器在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>
View Code

  工具类:

  在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();
    }

}
View Code

 

码字不易,尊重原创:http://www.cnblogs.com/adeng/p/7596818.html

 

 

  

 

以上是关于类装载器ClassLoader的主要内容,如果未能解决你的问题,请参考以下文章

ClassLoader原理

Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段

JVM——三个ClassLoader详解

Tomcat学习之ClassLoader

Android中的类装载器DexClassLoader

Java类加载器ClassLoader总结