说明:本文为孙卫琴的《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中也运到了动态代理机制,有兴趣的朋友可查找相关资料。
|