听上去很高端,其实一般自定义类加载器不需要用户去实现解析的过程,只要负责实现获取类对应的.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. [ActionScript 3.0] 通过as3操作web内容

    package com.controls.utils { import flash.net.*; import flash.utils.ByteArray; import flash.external ...

  2. [CISCO] Telete/SSH 之 Port 绑定/端口安全

    [网络] Telete/SSH 之 Port 绑定/端口安全 一.前言 之前写完了网络] DHCP 之 Mac 绑定,CiSCO 交换机配置 SSH 登陆.这次我们再试试能不能挖的在深入些. (1) ...

  3. gitlab的简单使用

    mkdir folder //创建文件夹 cd folder/ git clone git连接 git status //git当前的状态 ls //当前目录下的所有文件 cd gitFolder 进 ...

  4. 3.2 Multi-Master Replication

    摘要: 出处:黑洞中的奇点 的博客 http://www.cnblogs.com/kelvin19840813/ 您的支持是对博主最大的鼓励,感谢您的认真阅读.本文版权归作者所有,欢迎转载,但请保留该 ...

  5. SUSE Linux Enterprise Server设置IP地址、网关、DNS

    说明: ip:202.118.83.247 子网掩码:255.255.255.0 网关:202.118.83.2 dns:8.8.8.8 / 8.8.4.4 1.设置ip $ vi /etc/sysc ...

  6. (二)Audio子系统之new AudioRecord()

    在上一篇文章<(一)Audio子系统之AudioRecord.getMinBufferSize>中已经介绍了AudioRecord如何获取最小缓冲区大小,接下来,继续分析AudioReco ...

  7. Android多媒体之view,SurfaceView,GLSurfaceView

    1.相关概念 不用画布,直接在窗口上进行绘图叫做无缓冲绘图. 用了一个画布,将所有内容都先画到画布上,在整体绘制到窗口上,就该叫做单缓冲绘图, 那个画布就是一个缓冲区.用了两个画布,一个进行临时的绘图 ...

  8. JavaScript数据结构-10.字典

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. 解决图片浮动调节不了的问题(使用vertical-align属性)

    vertical-align: middle; vertical-align 属性设置元素的垂直对齐方式. baseline  默认.元素放置在父元素的基线上.sub 垂直对齐文本的下标.super ...

  10. Hudson-ci/Using Hudson/Installing Hudson/Installing Hudson RPM--官方文档

    < Hudson-ci‎ | Using Hudson‎ | Installing Hudson(Redirected from Hudson-ci/Installing Hudson RPM) ...