自定义了个ClassLoader,用socket从一个server上获取class文件内容,然后创建一个类。tcp server是用erlang写的,只负责从硬盘读数据,然后传到自定义ClassLoader。特别注意的是,要想erlang socket跟java socket通信,gen_tcp:listen(Port,[binary,{packet,0}]),这里packet一定要为0,这个是erlang数据包的包头,通信的时候客户端跟服务端加的额外数据,跟java通信,是不能加的,否则会出现error msgsize这类的错误。
-module(class_file_server).
-export([start/0,start/1,process_request/1]).
-define(PORT,7777).
-define(CLASS_NOT_FOUND,"class_not_found_exception").
start() ->
start(?PORT).
start(Port) ->
case gen_tcp:listen(Port,[binary,{packet,0},{active,true}]) of
{ok,Socket} -> process_request(Socket);
{error,Reason} -> io:format("Fail to create socket: ~s ~n",[Reason])
end.
process_request(Socket) ->
case gen_tcp:accept(Socket) of
{ok,From} ->
spawn(class_file_server,process_request,[Socket]),
receive_data_then_send_file(From);
{error,closed} ->
io:format("Failed to accept, for {error,closed}~n");
{error,Reason} ->
io:format("Failed to accept: ~s~n",[Reason]);
Other ->
io:format("accept Other ~p~n",[Other])
end.
receive_data_then_send_file(FromSocket) ->
receive
{tcp,FromSocket,Bin} ->
io:format("receive some raw data: ~p ~n from ~p~n",[Bin,inet:peername(FromSocket)]),
send_file(FromSocket,binary_to_list(Bin)),
gen_tcp:close(FromSocket);
{tcp_closed,FromSocket} ->
io:format("processed one request:~p~n",[FromSocket]),
gen_tcp:close(FromSocket);
Other ->
io:format("Invalid data: ~p~n",[Other])
end.
send_file(FromSocket,FileName) ->
case file:read_file(FileName) of
{ok,Bin} ->
gen_tcp:send(FromSocket,Bin);
{error,Why} ->
gen_tcp:send(FromSocket,?CLASS_NOT_FOUND),
io:format("Failed to load class: ~p ~n because ~p~n",[FileName,Why])
end.
package classloader;
public class RemoteClassLoader extends ClassLoader {
protected Class<?> findClass(String name) throws ClassNotFoundException {
DownloadManager manager = new DownloadManager(7777);
try {
byte[] data = manager.download(name);
return defineClass(name, data, 0, data.length);
} catch (ClassNotFoundException e) {
return super.findClass(name);
}
}
@SuppressWarnings("unchecked")
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
RemoteClassLoader loader = new RemoteClassLoader();
Thread.currentThread().setContextClassLoader(loader);
// Class clazz = loader.loadClass("com.kingdee.eas.LoadedClazz");
Class clazz = Class.forName("com.kingdee.eas.LoadedClazz",true,loader);
clazz.newInstance();
// clazz.newInstance();
}
}
package classloader;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Arrays;
public class DownloadManager {
private static final String CLASS_NOT_FOUND = "class_not_found_exception";
private static final byte[] BYTES_CLASS_NOT_FOUND = CLASS_NOT_FOUND.getBytes();
private static final int SIZE = 204800;
private int port;
private byte[] result = new byte[SIZE * 10];
private byte[] buffer = new byte[SIZE];
public DownloadManager(int port) {
super();
this.port = port;
}
public byte[] download(String name) throws ClassNotFoundException {
// E:\\develop\\workspaces\\workspace-java\\jdbcdriver\\test\\com\\kingdee\eas\LoadedClazz.java
String classFile = "E:\\develop\\workspaces\\workspace-java\\jdbcdriver\\bin\\" + name.replaceAll("\\.", "/")
+ ".class";
Socket socket = null;
OutputStream out = null;
InputStream in = null;
int count = 0, totalCount = 0;
try {
socket = new Socket("192.168.18.27", this.port);
// write to socket
out = new BufferedOutputStream(socket.getOutputStream());
out.write(classFile.getBytes());
out.flush();
// read from socket
in = new BufferedInputStream(socket.getInputStream());
while ((count = in.read(buffer)) > 0) {
if (isEqual(BYTES_CLASS_NOT_FOUND, buffer, count)) {
throw new ClassNotFoundException(classFile);
}
System.arraycopy(buffer, 0, result, totalCount, count);
totalCount += count;
}
for (int i = 0; i < totalCount; i++) {
System.out.print(String.format("%02X", result[i]));
if ((i + 1) % 100 == 0)
System.out.println();
}
System.out.println();
return Arrays.copyOfRange(result, 0, totalCount);
} catch (UnknownHostException e) {
throw new ClassNotFoundException(classFile);
} catch (IOException e) {
throw new ClassNotFoundException(classFile);
} finally {
try {
if (out != null)
out.close();
if (in != null)
in.close();
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static boolean isEqual(byte[] dest, byte[] buffer, int size) {
if (dest.length != size)
return false;
for (int i = 0; i < size; i++) {
if (dest[i] != buffer[i])
return false;
}
return true;
}
/**
* @param args
* @throws IOException
* @throws UnknownHostException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws UnknownHostException, IOException, ClassNotFoundException {
DownloadManager manager = new DownloadManager(7777);
manager.download("");
}
}
这里写死了类路径的,你也可自己写自己的。另外需要指明的是,自定义的类装载的的双亲装载器是在ClassLoader类的默认构造函数里设置的,通过getSystemClassLoader()得到的AppClassLoader。而自定义的ClassLoader只实现findClass方法,可以保留loadClass方法原先的双亲委派模型,不用自己去加载所有的类。