这两天一直在学习一些classloader的相关知识,看了一些文章,了解到classloader的作用之一就是实现热部署功能。于是就看了一个网络上的一个例子,然后自己实现了一个应用。虽然作出来了,但是说实话:不满意。因为在这个例子当中,只要热部署一次,就要重新new一个classloader,这样会引发什么问题我也不清楚,并且,classloader究竟实现了什么,以及一些底层的东西我还不是很了解,还要继续研究,目前的版本就是一个中间版本。以后还要优化,或者在我读完tomcat的classloader之后我在去仿照着写一个。
1,使用CLass.forName()可以动态的得到一个.class的类。
2,Thread.currentThread().getContextClassLoader())得到当前的classloader。然后就可以得到classpath下面任何包中的类
好了,下面介绍这个工程的构思、以及实现方式,设计思想:首先来说:这个工程至少需要是需要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;
- }
- }
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();
- }
- }
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();
- }
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