听上去很高端,其实一般自定义类加载器不需要用户去实现解析的过程,只要负责实现获取类对应的.class字节流部分就ok了,摘录深入理解Java虚拟机的一段话

虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”

实现类加载器需要继承ClassLoader这个抽象类,用户只要实现其中的findClass方法即可。在该类的javadoc中给出了这样一个示例:

      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
}
}

即用户这个自定义的类加载器的类实现文件在网络的某个位置而不是本地的文件。defineClass是ClassLoader类中的一个函数,底层调用了本地方法用来做真正的类解析工作。用户定义的findClass方法会在已有加载器无法加载指定名称的类时被调用。不同的类加载器即使加载(defineClass执行者不同,如果一个自定义的类加载器最后还是调用了系统的类加载器加载那么和直接使用系统加载器加载的效果是一致的)相同的类在JVM看来也属于不一样。具体在loadClass方法中:

    protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
} if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name); // this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

我们继承的ClassLoader中含有一个parent ClassLoader的成员,如果它不为null,那么类先会让它去尝试着加载,当它找不到指定的类时再调用用户定义的findClass过程来加载。

Parents Delegation Model(双亲委派模型)

上述先让类加载器中存在parent类加载器,并且先让parent类加载器尝试类加载的过程涉及到类加载器的“双亲委派模型”。双亲其实就是一个祖先(parents对应的中文而已)即当前ClassLoader中的parent成员,委派就是先让parent加载器代理类加载过程,不行再用自己的类加载过程。按照深入理解JVM的说法有系统提供的三种类加载器它们的层次如下:

|———————————————|

|    Bootstrap ClassLoader       |

|———————————————|

|

|----------------------------------|

|   Extension ClassLoder        |

|----------------------------------|

|

|----------------------------------|

|   Application ClassLoder      |

|----------------------------------|

Bootstrap ClassLoader: 负责加载存放在JAVA_HOME/lib目录中的或者被-Xbootclasspath参数所指定的,并且是按名称被虚拟机识别的(如rt.jar,名字不符不会识别)

Extension ClassLoader: 负责加载JAVA_HOME/lib/ext目录中的或者被java.ext.dirs系统变量指定的路径中的所有类库

Application ClassLoader: 负责加载用户路径(classpath)上所指定的类库,一般情况下这个就是程序中默认的类加载器

bootstrap classloader不能被直接获取,如下代码将输出null,代表Integer这个类是由bootstrap加载的,也可以从ClassLoader的loadClass判断过程看出如果parent==null就采用bootstrap去加载。

System.out.println(Integer.class.getClassLoader());

application classloader可以通过ClassLoader.getSystemClassLoader()静态方法获取,如下代码将返回true,klass是一个用户定义的类的class属性

System.out.println(ClassLoader.getSystemClassLoader() == klass.getClassLoader());

自己定义的类加载器默认情况下将ApplicationClassLoader作为parent类加载器。另外线程体也可以设置上下文类加载器:

        Thread.currentThread().setContextClassLoader(cl);
Thread.currentThread().getContextClassLoader();

应用

如果我们需要将jetty嵌入到已有的Java代码中,在初始化Jetty服务器前先实例化一个全局配置类。如果一个类在启动Jetty前的ClassLoader的搜索目录和Jetty的webroot/inf中都存在(但是对应的资源文件可能只有在前者对应的目录中存在),可以设置优先从parentClassLoader中进行载入。jetty除了碰到一些语言级别的类会从parent类加载器获取,其他都先从自己的类加载器获取(否则会使得各个容器下的app可以相互影响)。当使用嵌入式jetty时,一般就运行一个app所以,可以把这个选项打开,如下:

appContext.setParentLoaderPriority(true);

下面是jetty的WebAppClassLoader继承了URLClassLoader:

    @Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
Class<?> c= findLoadedClass(name);
ClassNotFoundException ex= null;
boolean tried_parent= false; boolean system_class=_context.isSystemClass(name);
boolean server_class=_context.isServerClass(name); if (system_class && server_class)
{
return null;
} if (c == null && _parent!=null && (_context.isParentLoaderPriority() || system_class) && !server_class)
{
tried_parent= true;
try
{
c= _parent.loadClass(name);
if (LOG.isDebugEnabled())
LOG.debug("loaded " + c);
}
catch (ClassNotFoundException e)
{
ex= e;
}
} if (c == null)
{
try
{
c= this.findClass(name);
}
catch (ClassNotFoundException e)
{
ex= e;
}
} if (c == null && _parent!=null && !tried_parent && !server_class )
c= _parent.loadClass(name); if (c == null)
throw ex; if (resolve)
resolveClass(c); if (LOG.isDebugEnabled())
LOG.debug("loaded " + c+ " from "+c.getClassLoader()); return c;
}

其中的serverClass和systemClass见WebAppContext:

    public final static String[] __dftSystemClasses =
{
"java.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
"javax.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
"org.xml.", // needed by javax.xml
"org.w3c.", // needed by javax.xml
"org.apache.commons.logging.", // TODO: review if special case still needed
"org.eclipse.jetty.continuation.", // webapp cannot change continuation classes
"org.eclipse.jetty.jndi.", // webapp cannot change naming classes
"org.eclipse.jetty.plus.jaas.", // webapp cannot change jaas classes
"org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension
"org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension
"org.eclipse.jetty.websocket.WebSocketServlet", // webapp cannot change WebSocketServlet
"org.eclipse.jetty.servlet.DefaultServlet" // webapp cannot change default servlets
} ; // Server classes are classes that are hidden from being
// loaded by the web application using system classloader,
// so if web application needs to load any of such classes,
// it has to include them in its distribution.
public final static String[] __dftServerClasses =
{
"-org.eclipse.jetty.continuation.", // don't hide continuation classes
"-org.eclipse.jetty.jndi.", // don't hide naming classes
"-org.eclipse.jetty.plus.jaas.", // don't hide jaas classes
"-org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension
"-org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension
"-org.eclipse.jetty.websocket.WebSocketServlet", // don't hide WebSocketServlet
"-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
"-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners
"org.eclipse.jetty." // hide other jetty classes
} ;

不懂啊!待续

Java:类加载器(ClassLoader)的更多相关文章

  1. 深入理解Java类加载器(ClassLoader)

    深入理解Java类加载器(ClassLoader) Java学习记录--委派模型与类加载器 关于Java类加载双亲委派机制的思考(附一道面试题) 真正理解线程上下文类加载器(多案例分析) [jvm解析 ...

  2. 深入理解Java类加载器(ClassLoader) (转)

    转自: http://blog.csdn.net/javazejian/article/details/73413292 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Ja ...

  3. Java 类加载器(ClassLoader)

    类加载器 ClassLoader 什么是类加载器? 通过一个类的全限定名来获取描述此类的二进制字节流这个动作放到Java虚拟机外部去实现, 以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代 ...

  4. 浅析java类加载器ClassLoader

    作为一枚java猿,了解类加载器是有必要的,无论是针对面试还是自我学习. 本文从JDK提供的ClassLoader.委托模型以及如何编写自定义的ClassLoader三方面对ClassLoader做一 ...

  5. 潜水 java类加载器ClassLoader

    类加载器(class loader)用于装载 Java 类到 Java 虚拟机中.一般来说.Java 虚拟机使用 Java 类的方式例如以下:Java 源程序(.java 文件)在经过 Java 编译 ...

  6. java类加载器——ClassLoader

    Java的设计初衷是主要面向嵌入式领域,对于自定义的一些类,考虑使用依需求加载原则,即在程序使用到时才加载类,节省内存消耗,这时即可通过类加载器来动态加载. 如果你平时只是做web开发,那应该很少会跟 ...

  7. Java类加载器ClassLoader总结

    JAVA类装载方式,有两种: 1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中. 2.显式装载, 通过class.forname()等方法,显 ...

  8. Java类加载器(ClassLoader)

    类加载的机制的层次结构 每个编写的”.java”拓展名类文件都存储着需要执行的程序逻辑,这些”.java”文件经过Java编译器编译成拓展名为”.class”的文件,”.class”文件中保存着Jav ...

  9. Java类加载器(死磕5)

    Java类加载器(  CLassLoader )  死磕5:  自定义一个文件系统classLoader 本小节目录 5.1. 自定义类加载器的基本流程 5.2. 入门案例:自定义文件系统类加载器 5 ...

  10. Java类加载器( 死磕9)

    [正文]Java类加载器(  CLassLoader ) 死磕9:  上下文加载器原理和案例 本小节目录 9.1. 父加载器不能访问子加载器的类 9.2. 一个宠物工厂接口 9.3. 一个宠物工厂管理 ...

随机推荐

  1. C#取得控制台应用程序的根目录方法

    如有雷同,不胜荣幸,若转载,请注明 取得控制台应用程序的根目录方法1:Environment.CurrentDirectory 取得或设置当前工作目录的完整限定路径2:AppDomain.Curren ...

  2. Performs the analysis process on a text and return the tokens breakdown of the text

    Analyzeedit Performs the analysis process on a text and return the tokens breakdown of the text. Can ...

  3. AFNetworking 3.1.0 使用中某些知识点讲解

    # POST / GET  请求 /*! 首先要知道,POST请求不能被缓存,只有 GET 请求能被缓存.因为从数学的角度来讲,GET 的结果是 幂等 的,就好像字典里的 key 与 value 就是 ...

  4. 50.RocketMQ (quickstart)

    要多给下属表功,绝不能抢功. 1.订阅消息 /** * Copyright (C) 2010-2013 Alibaba Group Holding Limited * * Licensed under ...

  5. Ribbon是什么?

    学而时习之,不亦说乎!                              --<论语> Ribbon使用版本2.2.2 Ribbon是什么? 开始接触Ribbon的时候,网上以及很 ...

  6. 【文档】六、Mysql Binlog版本

    binlog文件格式有以下几种: v1:用于3.23版本 v3:用于4.0.2到4.1版本 v4:用于5.0及以上版本 v2版本只在4.0.x版本中使用,目前已经不再支持了. 处理binlog的程序必 ...

  7. ubuntu 配置 samba服务器

    samba配置的安装: sudo apt-get install samba smbfs smbclient 二. 创建共享目录: mkdir /home/komy/sharesudu chmod 7 ...

  8. mongo 与 传统mysql语法对比

    MongoDB语法                                  MySql语法 db.test.find({'name':'foobar'})<==> select ...

  9. 虚拟机实现finally语句块

    1.ret.jsr.jsr_w与returnAddress指令实现finally语句块 当class文件的版本号等于或高于51.0,jsr和jsr_w这两个操作码也不能出现在code数组中. 所有re ...

  10. Linux 操作系统常用的三种流012

    Linux 操作系统常用的三种流: 0 标准输入流 1 标准输出流 2 标准错误流 通常在写脚本启动程序,写log时候,会出现如下写法: nohup commod > log.txt 2> ...