`

classloader 热部署

    博客分类:
  • java
阅读更多
关键字: classloader 热部署
这两天一直在学习一些classloader的相关知识,看了一些文章,了解到classloader的作用之一就是实现热部署功能。于是就看了一个网络上的一个例子,然后自己实现了一个应用。虽然作出来了,但是说实话:不满意。因为在这个例子当中,只要热部署一次,就要重新new一个classloader,这样会引发什么问题我也不清楚,并且,classloader究竟实现了什么,以及一些底层的东西我还不是很了解,还要继续研究,目前的版本就是一个中间版本。以后还要优化,或者在我读完tomcat的classloader之后我在去仿照着写一个。

好了,下面介绍这个工程的构思、以及实现方式,设计思想:首先来说:这个工程至少需要是需要2个线程,一个是类似tomcat的服务线程,另外一个就是检测线程,检测变化,重新加载Class对象。我猜tomcat是采取了检测类,检测加载了的类文件变化。我没有那么实现,因为这种实现方式相对复杂,并且我的想集中解决热部署问题,而不是如何实现监控文件,所以我就采取了相对简单的方式:socket通知方式。也就是,在我重新编译一个class之后,利用socket通知检测线程,监测监测在监测到socket命令之后会自动的加载。

Java代码 
package com.cxz.classloader;   
  
import com.cxz.jiangyou.Say;   
  
import java.io.ByteArrayOutputStream;   
import java.io.File;   
import java.io.FileInputStream;   
import java.io.IOException;   
  
/**  
 * This classloader is like a template  
 * which includes pre-loadClass and after-loadClass  
 * @author Bernard  
 *  
 */  
public class ComplexClassLoader extends ClassLoader {   
  
    public ComplexClassLoader() {   
    }   
  
    public ComplexClassLoader(String defaultTargetDir) {   
        this.defaultTargetDir = defaultTargetDir;   
    }   
  
    private String defaultTargetDir = "D:\\hotdeploys\\";   
  
    public Class<?> findClass(String className) throws ClassNotFoundException {   
        byte[] classBytes = null;   
        try {   
            classBytes = loadByteCode(className);   
        } catch (IOException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        }   
        return super.defineClass(className, classBytes, 0, classBytes.length);   
    }   
  
    private byte[] loadByteCode(String className) throws IOException {   
        int ch = 0;   
        className = className.replaceAll("\\.", "\\\\") + ".class";   
        //The two slashes represent for meaning changing   
        File file = new File(defaultTargetDir + className);   
        FileInputStream in = null;   
        in = new FileInputStream(file);   
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();   
        while ((ch = in.read()) != -1) {   
            buffer.write(ch);   
        }   
        in.close();   
        return buffer.toByteArray();   
    }   
  
    public String getDefaultTargetDir() {   
        return defaultTargetDir;   
    }   
  
    public void setDefaultTargetDir(String defaultTargetDir) {   
        this.defaultTargetDir = defaultTargetDir;   
    }      
}  

package com.cxz.classloader;

import com.cxz.jiangyou.Say;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * This classloader is like a template
 * which includes pre-loadClass and after-loadClass
 * @author Bernard
 *
 */
public class ComplexClassLoader extends ClassLoader {

	public ComplexClassLoader() {
	}

	public ComplexClassLoader(String defaultTargetDir) {
		this.defaultTargetDir = defaultTargetDir;
	}

	private String defaultTargetDir = "D:\\hotdeploys\\";

	public Class<?> findClass(String className) throws ClassNotFoundException {
		byte[] classBytes = null;
		try {
			classBytes = loadByteCode(className);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return super.defineClass(className, classBytes, 0, classBytes.length);
	}

	private byte[] loadByteCode(String className) throws IOException {
		int ch = 0;
		className = className.replaceAll("\\.", "\\\\") + ".class";
		//The two slashes represent for meaning changing
		File file = new File(defaultTargetDir + className);
		FileInputStream in = null;
		in = new FileInputStream(file);
		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
		while ((ch = in.read()) != -1) {
			buffer.write(ch);
		}
		in.close();
		return buffer.toByteArray();
	}

	public String getDefaultTargetDir() {
		return defaultTargetDir;
	}

	public void setDefaultTargetDir(String defaultTargetDir) {
		this.defaultTargetDir = defaultTargetDir;
	}	
}


该类会自动加载d:\hotdeploys下的类文件.

下面这个就是测试类
Java代码
package com.cxz.classloader;   
  
import java.io.IOException;   
import java.net.ServerSocket;   
import java.net.Socket;   
  
import com.cxz.jiangyou.Say;   
  
public class MultiThreadTest implements Runnable {   
  
    private static final int portNum = 9090;   
  
    private static final int sleepCycle = 3000;   
  
    private Say sayer = null;   
  
    private ComplexClassLoader loader = new ComplexClassLoader();   
  
    private String delpoyee = "com.cxz.jiangyou.Sample";   
  
    public MultiThreadTest() {   
        hotDeploy(delpoyee);   
    }   
  
    public void startService() {   
        while (true) {   
            synchronized (sayer) {   
                sayer.say();   
            }   
            try {   
                Thread.sleep(sleepCycle);   
            } catch (InterruptedException e) {   
                // TODO Auto-generated catch block   
                e.printStackTrace();   
            }   
        }   
    }   
  
    public void hotDeploy(String name) {   
        try {   
            if (sayer != null) {   
                synchronized (sayer) {   
                    loader = new ComplexClassLoader();   
                    sayer = (Say) loader.loadClass(name).newInstance();   
                }   
                System.out.println("-------------->Hot deployment finished!");   
            } else {   
                sayer = (Say) loader.loadClass(name).newInstance();   
                System.out.println("-------------->Initialization finished!");   
            }   
  
        } catch (InstantiationException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        } catch (IllegalAccessException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        } catch (ClassNotFoundException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        }   
    }   
  
    @Override  
    public void run() {   
        ServerSocket server = null;   
        Socket socket = null;   
        try {   
            server = new ServerSocket(portNum);   
            while (true) {   
                socket = server.accept();   
                socket.close();   
                hotDeploy(delpoyee);   
            }   
        } catch (IOException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        }   
    }   
  
    public static void main(String[] args) {   
        // new MultiThreadTest().startService();   
        // new MultiThreadTest().run();   
        MultiThreadTest test = new MultiThreadTest();   
        Thread thread = new Thread(test);   
        thread.start();   
        try {   
            thread.sleep(sleepCycle);   
            //Waiting for the deployment Thread deploy the say obj.   
        } catch (InterruptedException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        }   
        test.startService();   
    }   
  
}  

package com.cxz.classloader;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import com.cxz.jiangyou.Say;

public class MultiThreadTest implements Runnable {

	private static final int portNum = 9090;

	private static final int sleepCycle = 3000;

	private Say sayer = null;

	private ComplexClassLoader loader = new ComplexClassLoader();

	private String delpoyee = "com.cxz.jiangyou.Sample";

	public MultiThreadTest() {
		hotDeploy(delpoyee);
	}

	public void startService() {
		while (true) {
			synchronized (sayer) {
				sayer.say();
			}
			try {
				Thread.sleep(sleepCycle);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public void hotDeploy(String name) {
		try {
			if (sayer != null) {
				synchronized (sayer) {
					loader = new ComplexClassLoader();
					sayer = (Say) loader.loadClass(name).newInstance();
				}
				System.out.println("-------------->Hot deployment finished!");
			} else {
				sayer = (Say) loader.loadClass(name).newInstance();
				System.out.println("-------------->Initialization finished!");
			}

		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		ServerSocket server = null;
		Socket socket = null;
		try {
			server = new ServerSocket(portNum);
			while (true) {
				socket = server.accept();
				socket.close();
				hotDeploy(delpoyee);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		// new MultiThreadTest().startService();
		// new MultiThreadTest().run();
		MultiThreadTest test = new MultiThreadTest();
		Thread thread = new Thread(test);
		thread.start();
		try {
			thread.sleep(sleepCycle);
			//Waiting for the deployment Thread deploy the say obj.
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		test.startService();
	}

}

main线程主要是服务线程,通过调用startService()不停的通过system.out来打印。支线程负责监听端口(9090),当有连接信号后就重新加载类。
服务接口很简单,如下
Java代码 
package com.cxz.jiangyou;   
public interface Say{   
    public void say();   
}  

package com.cxz.jiangyou;
public interface Say{
	public void say();
}



总结:所有的customerClassLoader都要加载与之相关的类(比如:父类、包含的类)。如果你需要override loadclass(string, boolean)绕过findLoadedClass()检测,只能引发java.lang.LinkageError:duplicate class definition for name: "com/cxz/jiangyou/Sample"因此,比较通用的重新加载方式应该就是new一个用户定义的classloader
分享到:
评论
1 楼 changqingonly 2009-08-10  
貌似你理解错了ClassLoader的用法了,不能每次都new一个新的ClassLoader来加载你的class文件,这样必然导致你的命名空间发生变更哈。

相关推荐

    classloader做的一个热部署

    eclipse工程格式 博文链接:https://aga.iteye.com/blog/200818

    美团IDEA插件实现Java应用的热部署实践

    热部署插件的实现原理主要是通过Agent字节码增强、Javassist、Classloader等技术来实现的。Agent字节码增强是指在Java字节码中插入一些用于热部署的代码,以便在运行时可以实现热部署。Javassist是一个Java库,提供...

    支持Java热部署的插件

    Java热部署主要涉及到JVM(Java虚拟机)和类加载器(ClassLoader)的工作机制。JVM在运行时会加载类文件到内存中,当类被修改后,热部署插件能够检测到这种变化,并替换内存中的旧版本类,而不需要停止服务。这个...

    JAVA类加载器分析--热部署的缺陷(有代码示例,及分析)

    这篇博文“JAVA类加载器分析--热部署的缺陷”探讨了Java类加载机制以及在热部署场景下可能遇到的问题。热部署允许开发者在不重启应用的情况下更新代码,提高开发效率,但同时也存在一些挑战。 首先,我们来理解类...

    深入探索 Java 热部署

    要实现热部署,关键在于自定义类加载器(ClassLoader)。通常,JVM 使用双亲委派模型加载类,即从顶层的 Bootstrap ClassLoader 开始,逐级向下查找直至找到合适的类加载器。自定义 ClassLoader 需要监控特定类的...

    STS热部署方法(springboot).docx

    本文将详细介绍如何在Spring Tool Suite (STS) 中为Spring Boot项目设置热部署,以及DevTools的工作原理。 首先,要在Spring Boot项目中启用热部署功能,需要在`pom.xml`文件中添加`spring-boot-devtools`依赖。这...

    关于java热部署知识.doc

    总之,Java热部署是通过自定义ClassLoader、监控文件变化以及在必要时重新加载类来实现的。虽然JVM规范不直接支持这一功能,但通过各种策略和工具,开发者可以享受到类似Rails那样的便捷开发体验。

    Java类热替换(热部署)源码

    Java类热替换技术通常涉及类加载器(ClassLoader)的自定义以及字节码处理。本资料将通过源码分析,讲解如何实现这一功能。 首先,我们需要理解Java的类加载机制。在Java中,类加载器负责查找并加载类的字节码文件...

Global site tag (gtag.js) - Google Analytics