说明:本文为孙卫琴的《Java网络编程精解》第10
章的学习笔记。
Java反射机制主要提供了如下功能:
l 在运行时判断任何一个对象所属的类;
l 在运行时构造任意一个类的对象;
l 在运行时判断任何一个类所具有的成员变量和方法;
l 在运行时调用任何一个对象的方法;
l 生成动态代理。
一. Java Reflection API简介
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:
l Class类:代表一个类;
l Field类:代表类的成员变量;
l Method类:代表类的方法;
l Constructor:代表类的构造方法;
l Array:提供了动态创建数组,以及访问数组元素的静态方法。
至于它们的使用,请参见我先前的一篇文章:Java反射机制学习笔记(一),在此不再赘述。
二. 在远程方法调用中运用反射机制
让我们来看一个在远程调用方法中调用运用反射机制的例子。该例的服务端SimpleServer接收客户端SimpleClient发送的Call对象,该Call类型对象包括要调用的远程对象的类名、方法名、参数类型和参数值信息。而服务端SimpleServer在接收到该对象时,调用指定类名的指定方法,并加组装了返回值的Call类型对象返回给客户端SimpleClient。若不存在所指定的类或方法,则将异常放入Call类型对象的result属性中,返回给客户端。下面让我们来看看这个例子:
1. Call对象
Call对象包含类名、方法名、参数类型、参数值信息和返回结果信息,是用来在客户端和服务端进行信息交互的对象。其代码如下:
package remotecall;
import java.io.Serializable;
publicclass Call implements Serializable {
//类名或接口名
private String className;
//方法名
private String methodName;
//方法参数类型
private Class[] paramTypes;
//方法参数值
private Object[] params;
//返回方法的执行结果,正常执行时,存放返回值,发生异常时,result为该异常
private Object result;
public Call() {
}
/** *//**
*构造函数.
*/
public Call(String className, String methodName,
Class[] paramTypes, Object[] params) {
this.className = className;
this.methodName = methodName;
this.paramTypes = paramTypes;
this.params = params;
}
//省略className、methodName、paramTypes、params和result的set/get方法
public String toString() {
return"className=" + className + ",methodName=" + methodName;
}
}
2. 服务端SimpleServer
服务端建立一个ServerSocket,一直读取客户端发送来的消息,并传入参数到指定的方法,调用该方法,并将返回结果设置到Call类型对象的result属性中,若出现异常情况时,将异常放入result属性中,并将改变后的Call类型对象返回。其代码如下所示:
package remotecall;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/** *//**
*服务端.
*/
publicclass SimpleServer {
//存放远程对象的缓存
private Map<String, Object> remoteObjects = new HashMap<String, Object>();
/** *//**
*将一个远程对象加入缓存中.
*@paramclassNamemap中的key——类名
*@paramremoteObject远程对象
*/
publicvoid register(String className, Object remoteObject) {
remoteObjects.put(className, remoteObject);
}
publicvoid service() throws Exception {
ServerSocket serverSocket = new ServerSocket(8000);
System.out.println("服务器启动");
while(true) {
Socket socket = serverSocket.accept();
InputStream in = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(in);
OutputStream out = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
//接收从客户端发送的Call对象
Call call = (Call) ois.readObject();
//调用call的toString()方法打出className和methodName
System.out.println(call);
//调用对象的相关方法
call = invoke(call);
//将放置了result值的对象放入输出中返回
oos.writeObject(call);
//关闭相关资源
ois.close();
oos.close();
socket.close();
}
}
/** *//**
*调用远程方法的指定方法,并将返回值放入call对象的result中.
*@paramcall调用对象
*@return返回设置了result值的call对象
*/
public Call invoke(Call call) {
Object result = null;
try {
//取出对象中的各参数
String className = call.getClassName();
String methodName = call.getMethodName();
Class[] paramTypes = call.getParamTypes();
Object[] params = call.getParams();
//获取类
Class classType = Class.forName(className);
//获取方法
Method method = classType.getMethod(methodName, paramTypes);
//将className作为key在map中取出远程对象
Object remoteObject = remoteObjects.get(className);
if (remoteObject == null) {
thrownew Exception(className + "远程对象不存在!");
} else {
//通过传入相应参数调用remoteObject的指定方法
//并将返回值放入result中.
result = method.invoke(remoteObject, params);
}
} catch(Exception e) {
result = e;
}
//设置返回值
call.setResult(result);
return call;
}
/** *//**
*测试方法.
*/
publicstaticvoid main(String[] args) throws Exception {
SimpleServer server = new SimpleServer();
//存放对象到remoteObjects这个map中
server.register("remotecall.HelloService", new HelloServiceImpl());
server.service();
}
}
3. 客户端SimpleClient
客户端发送组装好的Call对象给服务端,并读取指定方法的返回结果。其完整代码如下:
package remotecall;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
/** *//**
*客户端类.
*/
publicclass SimpleClient {
publicvoid invoke(Call call) throws Exception {
Socket socket = new Socket("localhost", 8000);
OutputStream out = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
InputStream in = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(in);
//向服务器发送call对象
oos.writeObject(call);
//接收从服务端发送回的对象
call = (Call) ois.readObject();
//打印结果信息
System.out.println(call.getResult());
//关闭资源
ois.close();
oos.close();
socket.close();
}
/** *//**
*测试方法.
*/
publicstaticvoid main(String[] args) throws Exception {
SimpleClient client = new SimpleClient();
//构建一个正确Call对象
Call call = new Call();
call.setClassName("remotecall.HelloService");
call.setMethodName("echo");
call.setParamTypes(new Class[]{String.class});
call.setParams(new Object[]{"Hello,阿蜜果"});
client.invoke(call);
//构建一个错误的Call对象(不存在所指定的类)
call.setClassName("remotecall.HelloEcho");
client.invoke(call);
}
}
4. 远程类HelloService及其实现类HelloServiceImpl
为了测试上面的功能,还需要模拟一个远程对象所属的类,本例的HelloService接口具有两个方法,echo()和getTime()。两者的内容如下:
HelloService的内容:
package remotecall;
import java.util.Date;
publicinterface HelloService {
public String echo(String msg);
public Date getTime();
}
HelloServiceImpl的内容:
package remotecall;
import java.util.Date;
publicclass HelloServiceImpl implements HelloService {
public String echo(String msg) {
return"echo: " + msg;
}
public Date getTime() {
returnnew Date();
}
}
在测试时,我们首先运行服务端SimpleServer,将服务端启动起来,接着将客户端SimpleClient启动,可在控制台看到如下信息:
客户端的信息如下:
echo: Hello,阿蜜果
java.lang.ClassNotFoundException: remotecall.HelloEcho
服务端的信息如下:
服务器启动...
className=remotecall.HelloService,methodName=echo
className=remotecall.HelloEcho,methodName=echo
三.代理模式
代理模式是常用的Java设计模式,它的特征是代理类和委托类有相同的接口。代理类主要负责为委托类预处理消息、过滤信息、把消息转发给委托类,以及事后处理信息等。代理类和委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
根据代理类的创建时期,可将其分为两类:
l 静态代理类:由程序员创建或由特定工具自动生成源代码;
l 动态代理类:在程序运行时,运用反射机制创建而成。
1. 静态代理类
请参考代理模式的一些实现实例,在此不再详述。
2. 动态代理类
动态代理类不仅简化了编程工作,而且提高了软件系统的扩展性,因为Java反射机制可以生成任意类型的动态代理类。java.lang.reflect类和InvocationHandler接口提供了生成动态代理类的能力。与之相关的方法是:getProxyClass()和newProxyInstance()方法。下面让我们来看一个动态代理类的简单例子:
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/** *//**
*动态代理类.
*/
publicclass HelloServiceProxyFactory {
publicstatic HelloService getHelloServiceProxy(final HelloService helloService) {
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object args[])
throws Exception {
System.out.println("before calling " + method);
Object result = method.invoke(helloService, args);
System.out.println("after calling " + method);
return result;
}
};
Class classType = HelloService.class;
return (HelloService) Proxy.newProxyInstance(classType.getClassLoader(),
new Class[]{classType},
handler);
}
/** *//**
*测试方法.
*/
publicstaticvoid main(String[] args) {
HelloService helloService = new HelloServiceImpl();
HelloService helloServiceProxy = HelloServiceProxyFactory.getHelloServiceProxy(
helloService);
System.out.println("代理类名字:" + helloServiceProxy.getClass().getName());
System.out.println(helloService.echo("Hello,阿蜜果"));
}
}
运行后可看到这个代理类是动态生成的。在Spring的AOP中也运到了动态代理机制,有兴趣的朋友可查找相关资料。
posted on 2007-09-19 13:21
阿蜜果 阅读(2742)
评论(3) 编辑 收藏 所属分类:
Java