在JVM规范抽象架构中我们提到ClassLoader负责加载Java字节码交给JVM,为了给下面应用更进一步提供基础,我还是快速的介绍一下ClassLoader的工作机制。
ClassLoader常规工作机制如下:
ClassLoader常规工作机制
对上图进一步解释:
□ 每个java.lang.ClassLoader的子类都允许指定一个parent(父加载),如果不指定将直接使用系统级ClassLoader。
□ 每个java.lang.ClassLoader的子类在loadClass的时候都应该先检测自己的父加载器是否已经加载了要求加载的类,如果加载直接返回。
□ 如果没有加载,首先应该用自己父加载器的loadClass去加载,如果父加载器还没有加载成功,就直接调用自己的findClass方法来查找类。
□ 如果自己的findClass还加载不成功,抛出ClassNotFoundException。
一般情况下建议不要破坏这种树形加载机制,因为它维护比较完整的类加载器树,被认为是类加载策略的“最佳实践”,要自定义请 直接重写findClass方法即可。我们现实中常碰到的类加载问题(例如加载的类并不是你想要加载的;在同一个应用场景中同一个类多次被加载;在一些 Java EE应用服务器中没有充分共享应该共享的类库等)都是对常规加载机制的理解不到位或破坏了常规类加载机制造成的。但当碰到一些特殊需求时(例如在一个 JVM进程中一个类有不同的版本需要在父加载器和本身加载器中并存时),还是可以改变常规加载策略的,通常通过直接重写loadClass方法来完成。
好,我们回到ClassLoader的应用!通常对ClassLoader的应用有:
□ 热部署。
□ 软件保护。
□ 动态生成Java字节码。
□ 非常规的字节码加载。
下面我仅仅以Tomcat6的WEB应用ClassLoader为例介绍热部署,其它的留给大家通过过互联网资源去研究。
热部署(Hot Deployment)
热部署其实并没有什么神秘的,最差的热部署可以通过自动重启JVM。但我们一般意义上说的热部署指的是在不影响当前JVM中其它应用的前提下只对需要重新部署的程序进行局部更换。
其实热部署中采用ClassLoader主要是解决热部署中类重新加载的问题,而不是用ClassLoader来完全实现热部署。
我们以Tomcat6的Web应用(WAR)热部署为例看看其实现的基本原理。
在我剪辑代码说思路的时候,大家可以通过在线浏览源代码(http://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk )了解详细内容,当然你可以Check out到你的IDE(例如Eclipse,需要安装subclipse)。
注:在大家读代码时看到SecurityManager相关内容的时候可以跳过,仅仅带着它是进行安全管理的理解即可,在后面内容Java安全部分我们会详细介绍该内容。
Tomcat6的Web应用加载实现在包org.apache.catalina.loader中,主要通过类WebappLoader来实现。
我们谈Tomcat的Web应用热部署,就是谈一个WAR的变化驱动Tomcat如何重新加载Web应用,那至少应该有一个监听变化的线程吧?要搞清楚那个线程做到对Web应用变化的扫描,我们还不得不提一下Tomcat的内部架构(参阅http://tomcat.apache.org/tomcat-6.0-doc/architecture/overview.html):
Tomcat的内部架构
从上图我们可以看到一个Web应用肯定有一个Context维护它,一个Context肯定有一个Host维护它,一个 Host肯定有一个Engine维护它。那么监听Context变化的线程启动可能在Host和Engine里面,因为在Context级别启动一个线程 做热部署这件发生频率不是很高的事情实在有点浪费。那么我们先看看Host的标准实现类 org.apache.catalina.core.StandardHost的start方法:
好,我们发现线程启动是在StandardHost(Host的实现)中,追踪ContainerBackgroundProcessor类即可发现WebappLoader中如下的代码:
请注意红色下划线的代码,其中代码WebappLoader.class.getClassLoader默认返回的是 org.apache.catalina.loader.WebappClassLoader(注意:这里没有新建一个ClassLoader,而是直接 使用同一个ClassLoader,网上很多关于热部署的示例都是新建一个ClassLoader,这样操作简单,但是不科学的,重新发明轮子有什么意 义?很浪费!)。在此我们发现热部署就是重新设置了一下ClassLoader然后重新加载(reload)Context。其实事情就这么简单,我前面 说过热部署没有什么神秘的,但我也说过如何让变的东西重新加载,不变的东西不重新加载才是“高明”呢!那我们还是来看看 WebappClassLoader吧,从中学习一些“高明”的东西。
在研究WebappClassLoader之前我们自己先想想如何在不新建一个ClassLoader的前提下完成类的重新加载:
□ 在重新加载之前清除当前ClassLoader维护的所有已加载类的信息,这是最简单的方法。
□ 在监听变化的时候,把变化的信息进行缓存,在重新加载之前清除变化部分的已加载类信息,这种方法很复杂,需要程序的模块化程度相当高。
Tomcat的WebappClassLoader采用的是第一种策略,在需要热部署之前会调用WebappClassLoader的stop方法,如下图:
在WebappClassLoader的stop方法中两个地方很重要:
□ clearReferences(),清除引用。
□ resourceEntries.cleear(),清除已加载类的缓存。
清除已加载类的信息包括清除对已加载类的相关引用和清除已加载类的缓存两个主要环节。
我们前面说过,重写ClassLoader的loadClass方法是实现自定义类加载器的直接入口,下面我们看看WebappClassLoader的loadClass方法:
loadClass方法很重要的几点,我列出来,其它请大家直接浏览源代码:
□ loadClass方法的操作在一个同步块( )中。这几乎是我们重写类加载器必须的。
□ 首先通过代码 检索是否已经加载,而findLoadedClass0使自己重写的。这和我们前面提到的常规加载机制有所区别。请各位直接浏览 findLoadedClass0的代码,其中很重要的操作就是看看resourceEntries中是否已经有已经加载的类,如果你能和前面清除部分提 到的resourceEntries.clear()对应上的话,就好理解其加载机制。
□ 如果没有缓冲,再调用 看是否缓存。这步操作才回到常规类加载机制中的已加载类查找,其实按照逻辑,这一步仅仅把非Web应用程序中的类交给更上一层的类加载器来管理。
□ 第三步,如果前两步都没有找到类,首先调用 ,通过系统类加载器直接加载。这个很好玩,为什么?原因很简单!首先我们自己写的Web应用类库通常情况下不会被系统类加载器找见,换句话说系统类加载器 一定加载不到我们的类,如果加载到,那就有问题了!但先用系统类加载器恰恰解决了我们“胡乱”往我们的Web应用中扔其它系统包的问题,例如有些人直接把 编译期用到的servlet-api.jar扔到自己部署的Web应用中。通过这点,也提醒大家不要乱扔包。
□ 第四步,如果设置代理属性为true,直接用WebappClassLoader的父类加载器。这个也很好理解,例如我们想把Tomcat拿来直接嵌入到我们的应用程序,这时我们希望Tomcat先用调用它的应用程序类加载还是有必要的。
□ 第五步,调用WebappClassLoader重写的findClass,顺着findClass可以找见首先调用方 法findClassInternal,这一步才是真正Tomcat自定义如何加载和缓冲Web应用类的地方。请大家自己阅读 findClassInternal,主要关注defineClass和definePackage。
□ 第六步,等于一个迫不得已默认类加载,即交给父类加载器或者系统类加载器。
到此为止,就等于基本介绍完了Tomcat的热部署的基本实现。总结一下实现热部署的关键环节:
□ 首先所有的需要热部署的模块架构必须应该有明确的生命周期,例如start和stop。并且在start期间应该有明确的显式类加载器去加载模块类(这个很重要,不要写的代码没有模块的感觉还要热部署,那是给自己找麻烦)。
□ 其次,要动态监听模块的更新,需要一个低层线程来不断地检查。
□ 再次,自定义类加载器,自己维护模块类是否已经加载的判断和缓存。
□ 最后,在执行重新加载的时候记住要先停止(stop)当前的模块应用然后清除引用和缓存(至于如何维护当前模块的运行状态那是另外一件事情,但你还是要考虑),然后重新开始模块(start)。
------------------------------------------------------------------------------
借题发挥:熟练掌握了这块,你可以实战很多有趣的东西,例如你可以写一个插件模式的聊天服务器,并且支持插件的热部署;当然你可以去金蝶碰碰是否可以加入他们的应用服务器团队。
好,先说到这里。
上一篇:
《Java平台体系》 第二章 JVM——实战:用JBE修改Java字节码:http://qing.weibo.com/2494474521/94aea919330007vc.html
下一篇:
《Java平台体系》 第二章 JVM——实战:Java调用本地代码例子:http://qing.weibo.com/2494474521/94aea9193300082e.html
相关链接:
《Java平台体系》全书大纲:http://qing.weibo.com/2494474521/94aea919330006hz.html
http://qing.blog.sina.com.cn/tj/94aea919330007zp.html
相关推荐
在Java虚拟机(JVM)中,类加载器(ClassLoader)是至关重要的组成部分,它负责查找和加载类的字节码文件。理解ClassLoader的工作机制对于深入掌握Java应用程序的运行至关重要。这里我们将详细讨论ClassLoader的运行...
《深入理解ClassLoader工作机制》 Java虚拟机(JVM)中的ClassLoader是负责加载类到内存中的核心组件。它不仅承担着将字节码转换为可执行对象的重任,还参与了类生命周期的各个阶段,包括加载、验证、准备、解析、...
在Java中,Classloader是加载类的关键组件,它负责查找、加载和初始化字节码文件。自定义Classloader允许开发者根据特定需求定制类的加载逻辑,例如加密类文件、隔离不同版本的库或者动态加载代码。本文将深入探讨...
在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。本示例"ClassLoader小例子"将深入探讨这个概念,并通过一个具体的程序来演示其工作原理。下面我们...
Java ClassLoader是Java运行时系统的关键但经常被忽视的组件,负责在运行时查找和加载类文件。通过创建自定义ClassLoader,你可以定制JVM,使类文件的引入方式完全重新定义,这提供了很多实用和有趣的可能。这篇教程...
Java ClassLoader机制是Java虚拟机(JVM)中一个至关重要的组成部分,它的主要任务是将类的.class文件加载到JVM中,使得程序能够运行。ClassLoader不仅负责类的加载,还涉及类的验证、初始化等一系列过程。理解...
在Java编程语言中,`ClassLoader`是一个至关重要的组件,它负责加载类到JVM(Java虚拟机)中。本文将深入探讨`ClassLoader`的工作原理、加密解密应用程序以及如何防止类被反编译。 首先,让我们理解`ClassLoader`的...
在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作原理以及如何定制它,对于深入学习Java的运行机制和进行高级应用开发具有重要意义。本篇文章将...
在Java世界中,类加载器(ClassLoader)是关键的组件之一,它负责将类的字节码文件(.class)从文件系统或网络中加载到Java虚拟机(JVM)中,使得程序能够运行。本篇文章将深入探讨ClassLoader的关系网络以及如何...
《ClassLoader详解》 Java应用程序的运行离不开类的加载,而ClassLoader正是这个过程的关键角色。它负责将类的字节码加载到Java虚拟机(JVM)中并转换为可执行的Java对象。深入理解ClassLoader的工作原理对于优化...
Java ClassLoader机制是Java运行时环境中的核心组件之一,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。理解ClassLoader的工作原理对于优化应用性能、处理类加载问题以及实现自定义加载器至关重要。 首先...
在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中执行。本文将深入探讨ClassLoader的工作原理和类加载机制,帮助开发者理解这个至关重要的概念。 1. 类加载机制概述 Java的类加载...
在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中执行。这篇测试主要探讨了ClassLoader的工作原理及其在实际应用中的使用。通过阅读给出的博文链接,我们可以深入理解...
### Java ClassLoader与ClassPath详解 #### 一、概述 在Java编程中,类加载机制是十分关键的一个环节。类加载器(`ClassLoader`)负责将编译后的`.class`文件加载到Java虚拟机(JVM)中执行,而类路径(`ClassPath...
Java的类加载器(ClassLoader)体系结构是JVM(Java虚拟机)中至关重要的一部分,它负责将类的字节码转换为运行时的类实例。本文将深入探讨启动类加载器、扩展类加载器、系统类加载器以及用户自定义类加载器,同时还...
破解java加密的ClassLoader.java,在classloader植入破解代码