在远程方法调用中运用代理类
图 SimpleClient通过HelloService代理类调用远程对象的方法
如上图所示,SimpleClient客户端通过HelloService代理类来调用SimpleServer服务器端的 HelloServiceImpl对象的方法。客户端的HelloService代理类也实现了HelloService接口,这可以简化 SimpleClient客户端的编程。对于SimpleClient客户端而言,与远程服务器的通信的细节被封装到HelloService代理类中。 SimpleClient客户端可以按照以下方式调用远程服务器上的HelloServiceImpl对象的方法:
//创建HelloService代理类的对象
HelloService helloService1=new HelloServiceProxy(connector);
//通过代理类调用远程服务器上的HelloServiceImpl对象的方法
System.out.println(helloService1.echo("hello"));
从以上程序代码可以看出,SimpleClient客户端调用远程对象的方法的代码与调用本地对象的方法的代码很相似,由此可以看出,代理类简化了客户端的编程。
Connector类负责建立与远程服务器的连接,以及接收和发送Socket对象。
例 Connector.java
package proxy1;
import java.io.*;
import java.net.*;
import java.util.*;
public class Connector {
private String host;
private int port;
private Socket skt;
private InputStream is;
private ObjectInputStream ois;
private OutputStream os;
private ObjectOutputStream oos;
public Connector(String host,int port)throws Exception{
this.host=host;
this.port=port;
connect(host,port);
}
public void send(Object obj)throws Exception{//发送对象
oos.writeObject(obj);
}
public Object receive() throws Exception{//接收对象
return ois.readObject();
}
public void connect()throws Exception{//建立与远程服务器的连接
connect(host,port);
}
public void connect(String host,int port)
throws Exception{ //建立与远程服务器的连接
skt=new Socket(host,port);
os=skt.getOutputStream();
oos=new ObjectOutputStream(os);
is=skt.getInputStream();
ois=new ObjectInputStream(is);
}
public void close(){//关闭连接
try{
}finally{
try{
ois.close();
oos.close();
skt.close();
}catch(Exception e){
System.out.println("Connector.close: "+e);
}
}
}
}
HelloService代理类有两种创建方式:一种方式是创建一个HelloServiceProxy静态代理类,如例程10-18所示;还有一种方式 是创建HelloService的动态代理类,如例程10-19所示ProxyFactory类的静态getProxy()方法就负责创建 HelloService的动态代理类,并且返回它的一个实例。
例程10-18 HelloServiceProxy.java(静态代理类)
package proxy1;
import java.util.Date;
public class HelloServiceProxy implements HelloService{
private String host;
private int port;
public HelloServiceProxy(String host, int port){
this .host = host;
this .port = port;
}
public String echo(String msg) throws RemoteException{
Connector connector = null ;
try {
connector = new Connector(host,port);
Call call = new Call( " proxy1.HelloService " , " echo " ,
new Class[]{String. class }, new Object[]{msg});
connector.send(call);
call = (Call)connector.receive();
Object result = call.getResult();
if (result instanceof Throwable)
throw new RemoteException((Throwable)result);
else
return (String)result;
} catch (Exception e){
throw new RemoteException(e); // 把异常都转换为RemoteException
} finally { if (connector != null )connector.close();}
}
public Date getTime() throws RemoteException{
Connector connector = null ;
try {
connector = new Connector(host,port);
Call call = new Call( " proxy1.HelloService " ,
" getTime " ,
new Class[]{},
new Object[]{});
connector.send(call);
call = (Call)connector.receive();
Object result = call.getResult();
if (result instanceof Throwable)
throw new RemoteException((Throwable)result);
else
return (Date)result;
} catch (Exception e){
throw new RemoteException(e); // 把异常都转换为RemoteException
} finally { if (connector != null )connector.close();}
}
}
例程10-19 ProxyFactory.java(负责创建动态代理类及其实例)
package proxy1;
import java.lang.reflect. * ;
public class ProxyFactory {
public static Object getProxy( final Class classType, final String host, final int port){
InvocationHandler handler = new InvocationHandler(){
public Object invoke(Object proxy,Method method,Object args[])
throws Exception{
Connector connector = null ;
try {
connector = new Connector(host,port);
Call call = new Call(classType.getName(),
method.getName(),
method.getParameterTypes(),
args);
connector.send(call);
call = (Call)connector.receive();
Object result = call.getResult();
if (result instanceof Throwable)
throw new RemoteException((Throwable)result);
// 把异常都转换为RemoteException
else
return result;
} finally { if (connector != null )connector.close();}
}
};
return Proxy.newProxyInstance(classType.getClassLoader(),
new Class[]{classType},
handler);
}
}
无论HelloService的静态代理类还是动态代理类,都通过Connector类来发送和接收Call对象。ProxyFactory工厂类的 getProxy()方法的第一个参数classType指定代理类实现的接口的类型,如果参数classType的取值为 HelloService.class,那么getProxy()方法就创建HelloService动态代理类的实例。如果参数classType的取 值为Foo.class,那么getProxy()方法就创建Foo动态代理类的实例。由此可见,getProxy()方法可以创建任意类型的动态代理类 的实例,并且它们都具有调用被代理的远程对象的方法的能力。
如果使用静态代理方式,那么对于每一个需要代理的类,都要手工编写静态代理类的源代码;如果使用动态代理方式,那么只要编写一个动态代理工厂类,它就能自动创建各种类型的动态代理类,从而大大简化了编程,并且提高了软件系统的可扩展性和可维护性。
如例程10-20所示的SimpleClient类的main()方法中,分别通过静态代理类和动态代理类 去访问SimpleServer服务器上的HelloServiceImpl对象的各种方法。
例程10-20 SimpleClient.java
package proxy1;
import java.io.*;
import java.net.*;
import java.util.*;
public class SimpleClient {
public static void main(String args[])throws Exception {
//创建静态代理类实例
HelloService helloService1=new HelloServiceProxy("localhost",8000);
System.out.println(helloService1.echo("hello"));
System.out.println(helloService1.getTime());
//创建动态代理类实例
HelloService helloService2=(HelloService)ProxyFactory.
getProxy(HelloService.class,"localhost",8000);
System.out.println(helloService2.echo("hello"));
System.out.println(helloService2.getTime());
}
}
SimpleClient通过HelloService静态代理类和动态代理类访问远程HelloServiceImpl对象的echo()方法的时序图。