classloader做的一个热部署

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

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


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下的类文件.

下面这个就是测试类

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),当有连接信号后就重新加载类。
服务接口很简单,如下

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

[color=red]
总结:所有的customerClassLoader都要加载与之相关的类(比如:父类、包含的类)。如果你需要override loadclass(string, boolean)绕过findLoadedClass()检测,只能引发java.lang.LinkageError:duplicate class definition for name: "com/cxz/jiangyou/Sample"因此,比较通用的重新加载方式应该就是new一个用户定义的classloader
[/color]
另一个例子


import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

public class TestSuite {

private static final String dir = "D:\\depot\\dev\\";

public static Test suite() {
TestSuite suite = new TestSuite();
Class clz = loadClass("com.cxz.test.bs.XXXXTest");
suite.addTestSuite(clz);
return suite;
}

public static void bootstrap(String[] args) {
junit.textui.TestRunner.run(suite());
}

public static Class loadClass(String name) {
File file = new File(dir);
ClassLoader loader = null;
Class clz = null;
try {
loader = new URLClassLoader(new URL[] { file.toURL() });
clz = loader.loadClass(name);
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

//
return clz;
}

/**
* For test
*
* @param args
*/
public static void main(String[] args) {
loadClass("");
}
}

阅读更多

没有更多推荐了,返回首页