转:http://blog.csdn.net/zhangdaiscott/article/details/23378023
本文主要是根据classloader的特性,结合实际产品环境中遇到的问题,来探讨下JAVA应用中局部模块热部署的可行性。
我们知道,一些web应用提供了自动检测装载webapp的功能,但大部分的时候,就是相当于重新启动了一遍Webapp,存储在内存中的数据也会丢失,并不能灵活地满足需要。而OSGI框架,虽然也提供了模块的热部署,但为了用热部署而将应用限制在OSGI的框框中,有些时候得不偿失。于是想根据实际需要来定制classloader,灵活地指定哪些类重载,哪些类不需要。
言归正传,进行我们的实践,这里先简单介绍下JAVA的classloader机制:
从上图可以看出虚拟机中的Classloader的层次结构, 由最外层的Classloader去load全限定名指定的class,如果load不到,则委托给该classloader的父classloader,直至到root classloader。
- protected synchronized Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- // First, check if the class has already been loaded
- Class c = findLoadedClass(name);
- if (c == null) {
- try {
- if (parent != null) {
- c = parent.loadClass(name, false);
- } else {
- c = findBootstrapClass0(name);
- }
- } catch (ClassNotFoundException e) {
- // If still not found, then invoke findClass in order
- // to find the class.
- c = findClass(name);
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
- protected synchronized Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- // First, check if the class has already been loaded
- Class c = findLoadedClass(name);
- if (c == null) {
- try {
- if (parent != null) {
- c = parent.loadClass(name, false);
- } else {
- c = findBootstrapClass0(name);
- }
- } catch (ClassNotFoundException e) {
- // If still not found, then invoke findClass in order
- // to find the class.
- c = findClass(name);
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
这里我们的自定义Classloader可以通过重载loadClass方法,仅对指定package下的类进行加载,其余全部委托父Classloader来加载 , 这里需要用到classloader的defineClass方法,以便我们的classloader可以载入任意指定位置的class文件。
- public class MyClassLoader extends ClassLoader{
- public static ConcurrentHashMap<String, Class<?>> classes = new ConcurrentHashMap<String, Class<?>>();
- public static MqClassLoader instance = new MyClassLoader();
- //构造自定义Classloader, 并指定父Classloader
- public MyClassLoader() {
- super(Thread.currentThread().getContextClassLoader());
- }
- public Class<?> load(String name, byte[] data, boolean resolve) {
- Class<?> klass = defineClass(name, data, 0, data.length);
- if (resolve)
- resolveClass(klass);
- classes.put(name, klass);
- return klass;
- }
- public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- Object value = classes.get(name); // 检查缓存
- if (value != null && value != INVALID) {
- Class<?> klass = (Class<?>) value;
- if (resolve)
- resolveClass(klass);
- return klass;
- } else { // 缓存中不存在
- byte[] data = read(findClassFile(name)); // 读取类文件
- if (data == null)
- return super.loadClass(name, resolve); // 交由父classloader去load类文件
- else {
- try {
- lock.lock();
- Object cc = classes.get(name); // 检查缓存
- if (cc != null) {
- return (Class<?>) cc;
- } else
- return instance.load(name, data, resolve); // 自己load类文件
- } finally {
- lock.unlock();
- }
- }
- }
- }
- }
- public class MyClassLoader extends ClassLoader{
- public static ConcurrentHashMap<String, Class<?>> classes = new ConcurrentHashMap<String, Class<?>>();
- public static MqClassLoader instance = new MyClassLoader();
- //构造自定义Classloader, 并指定父Classloader
- public MyClassLoader() {
- super(Thread.currentThread().getContextClassLoader());
- }
- public Class<?> load(String name, byte[] data, boolean resolve) {
- Class<?> klass = defineClass(name, data, 0, data.length);
- if (resolve)
- resolveClass(klass);
- classes.put(name, klass);
- return klass;
- }
- public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- Object value = classes.get(name); // 检查缓存
- if (value != null && value != INVALID) {
- Class<?> klass = (Class<?>) value;
- if (resolve)
- resolveClass(klass);
- return klass;
- } else { // 缓存中不存在
- byte[] data = read(findClassFile(name)); // 读取类文件
- if (data == null)
- return super.loadClass(name, resolve); // 交由父classloader去load类文件
- else {
- try {
- lock.lock();
- Object cc = classes.get(name); // 检查缓存
- if (cc != null) {
- return (Class<?>) cc;
- } else
- return instance.load(name, data, resolve); // 自己load类文件
- } finally {
- lock.unlock();
- }
- }
- }
- }
- }
上面的代码描述了自定义classloader的载入逻辑,findClassFile() 就是自己定义的从哪里找需要的类文件方法。
defineClass 方法可以灵活地用来实现分布式动态计算,hadoop mapreduce应该就是使用了这一方法来保证服务器集群间的处理类的传输和运行。
- /**
- * 重新初始化,以便实现重载所指定的类
- */
- public static void reset() {
- instance = new MyClassLoader();
- classes.clear();
- }
- /**
- * 重新初始化,以便实现重载所指定的类
- */
- public static void reset() {
- instance = new MyClassLoader();
- classes.clear();
- }
其实每次reset都会产生一个新的classloader实例, 该实例会在所有这个实例装载的的类全部被回收后才被回收,这里比较奔放的全部reset掉,经过测试尚未发现内存溢出问题。
调用这些类的方法:
- public static void invoke(String method ,Object[] obj, Class<?>[] parameterTypes){
- try {
- Object cls = MyClassLoader.instance.loadClass("类全限定名", true).newInstance();
- cls.getClass().getMethod(method,parameterTypes).invoke(cls ,obj);
- } catch (Exception e) {
- logger.error("reloadable error " + method, e);
- }
- }
- public static void invoke(String method ,Object[] obj, Class<?>[] parameterTypes){
- try {
- Object cls = MyClassLoader.instance.loadClass("类全限定名", true).newInstance();
- cls.getClass().getMethod(method,parameterTypes).invoke(cls ,obj);
- } catch (Exception e) {
- logger.error("reloadable error " + method, e);
- }
- }
这里只能通过反射的方式调用,由于classloader的安全机制,同样的类,父classloader装载的类 和 子classloader装载的类不能互相转换。
需要强调的是,被重载的类重载后所有的变量都会被重新初始化,因此一些重要数据变量还是得交由父Classloader来管理。
该方式存在一些缺点,把核心逻辑归并到一起,将变量分离出去,从而影响了应用本身的结构。
目前这种方式正应用在一个消息服务中,主要避免由于一些微小的改动而重新启动服务,重启消息服务这对于大型系统来说是很麻烦的 ,是否值得还很难说,权当对java的深入学习吧。
相关推荐
这使得Java可以支持许多高级功能,例如插件系统、热部署和模块化系统(如Java 9的Jigsaw项目)。 `JAVA2深度历险.jpg`可能是关于Java 2 SDK的深度探索的图像资料,可能涵盖了ClassLoader的部分内容。而`CH_02.深入...
总之,Java 热部署的核心是自定义 ClassLoader 和字节码操作,如 ASM。通过监控类变更,重新加载并使用新版本的类,可以在不重启 JVM 的情况下更新应用程序的特定部分。这降低了开发过程中的重启成本,提高了开发...
对于标题“Java SE: ...- 模块化/插件化:对于需要模块化或插件化的应用程序,可以通过自定义ClassLoader实现模块的热加载或动态卸载。 深入理解ClassLoader机制对于开发大型、复杂或动态可扩展的Java应用非常重要。
- 自定义ClassLoader在插件系统、热部署、模块化系统等领域有广泛应用。 5. **类的生命周期** - 加载:通过ClassLoader找到对应的.class文件。 - 链接:验证、准备、解析三个步骤。 - 初始化:执行类的静态初始...
在热部署场景下,可以通过替换ClassLoader来实现不重启JVM就能更新代码;另外,对于加密的类文件,可以编写特定的ClassLoader解密后再加载。 了解ClassLoader的工作机制对于深入理解Java应用程序的运行过程至关重要...
3. 自定义ClassLoader: 开发者可以继承java.lang.ClassLoader类,创建自己的类加载器,实现特定的类查找和加载逻辑。这常用于动态加载插件、实现模块化系统或加载加密的类文件。 4. 双重检查锁定与类加载: 在多...
自定义ClassLoader是一种常见的需求,例如在插件系统、热部署等场景。自定义加载器通常需要重写`loadClass(String className)`方法,通过URL读取类的字节流并转换为Class对象。在这个过程中,我们需要注意类的缓存,...
Java类加载器(ClassLoader)是Java虚拟机(JVM)的核心组成部分,负责将类的字节码转换为可运行的Java对象。...通过自定义类加载器,开发者可以控制类的加载过程,满足特定需求,比如模块化系统、热部署等。
- 动态加载与热部署:在运行时加载新的类,无需重启应用,常用于插件系统。 - 安全隔离:不同加载器加载的类互不影响,可以用于实现安全沙箱环境。 - 版本控制:不同版本的类可以由不同的类加载器加载,实现版本...
- JRebel 是一款商业的热部署工具,能够实时更新代码,无需重启应用。 7. 类热加载注意事项 - 热加载可能导致程序状态不一致,尤其是在多线程环境下,需谨慎处理并发问题。 - 热加载可能会影响程序性能,因为...
Java URLClassLoader 是Java标准库中的一个类加载器,它允许我们动态地从指定的URL位置加载类和资源。...这种机制在很多高级应用场景,如模块化系统、服务器插件和自定义应用程序加载逻辑中都有广泛的应用。
总之,Java类加载器机制是Java平台灵活性和动态性的重要体现,它使得JVM能够在运行时加载和解析类,支持代码的热部署,增强了系统的可扩展性和可维护性。通过深入学习和理解这一机制,开发者能够更好地驾驭Java应用...
这在实现插件系统或热部署场景中非常有用。 6. 类加载器与异常: 在类加载过程中,可能会遇到`ClassNotFoundException`和`NoClassDefFoundError`等异常。前者通常是因为类加载器无法找到指定的类,后者则可能发生...
这在分布式系统、远程服务调用、热部署等场景下非常有用。实现网络类加载器通常需要以下步骤: 1. **自定义类加载器**:首先,我们需要创建一个继承自java.lang.ClassLoader的子类。在这个子类中,重写`findClass...
- **如何创建自定义类加载器**:通常通过继承`java.lang.ClassLoader`类并重写其方法来实现自定义类加载器。这涉及到如何读取类文件、验证类文件格式等技术细节。 ### 4. 扩展(Extensions) - **Java平台扩展机制**...
开发者在需要动态加载类,比如热部署、插件系统或隔离不同版本的库时,可能会编写自定义类加载器。自定义加载器需要重写`loadClass()`或`findClass()`方法,以实现特定的加载逻辑。 理解类加载机制对于Java开发者...
- 实现类加载器有助于理解类加载机制,例如通过自定义ClassLoader来实现热更新等。 23. **解释一下什么叫AOP(面向切面编程)** - AOP是一种编程范式,通过分离关注点(Separation of Concerns)来组织与实现横切...
- **自定义类加载器(User-defined ClassLoader)**:用户可以根据需求编写自己的类加载器,实现特定的加载逻辑。 3. **双亲委派模型** 类加载器之间存在层次结构,当一个类加载器接收到加载类的请求时,它会先...
开发者可以通过继承java.lang.ClassLoader来创建自己的类加载器,实现特定的加载逻辑,比如从网络、数据库或加密文件中加载类。这在模块化系统、动态加载插件等场景中非常有用。 5. **热部署与类重载** 在开发...
25 JAVA8 与元数据.................................................................................................................................25 2.4. 垃圾回收与算法 .................................