说明:本文为孙卫琴的《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:提供了动态创建数组,以及访问数组元素的静态方法。
二. 在远程方法调用中运用反射机制
让我们来看一个在远程调用方法中调用运用反射机制的例子。该例的服务端SimpleServer接收客户端SimpleClient发送的Call对象,该Call类型对象包括要调用的远程对象的类名、方法名、参数类型和参数值信息。而服务端SimpleServer在接收到该对象时,调用指定类名的指定方法,并加组装了返回值的Call类型对象返回给客户端SimpleClient。若不存在所指定的类或方法,则将异常放入Call类型对象的result属性中,返回给客户端。下面让我们来看看这个例子:
1. Call对象
Call对象包含类名、方法名、参数类型、参数值信息和返回结果信息,是用来在客户端和服务端进行信息交互的对象。其代码如下:
package remotecall;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
import java.io.Serializable;
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
publicclass Call implements Serializable
{
//类名或接口名
private String className;
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//方法名
private String methodName;
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//方法参数类型
private Class[] paramTypes;
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//方法参数值
private Object[] params;
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//返回方法的执行结果,正常执行时,存放返回值,发生异常时,result为该异常
private Object result;
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Call()
{
}
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
*构造函数.
*/
public Call(String className, String methodName,
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Class[] paramTypes, Object[] params)
{
this.className = className;
this.methodName = methodName;
this.paramTypes = paramTypes;
this.params = params;
}
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//省略className、methodName、paramTypes、params和result的set/get方法
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public String toString()
{
return"className=" + className + ",methodName=" + methodName;
}
}
2. 服务端SimpleServer
服务端建立一个ServerSocket,一直读取客户端发送来的消息,并传入参数到指定的方法,调用该方法,并将返回结果设置到Call类型对象的result属性中,若出现异常情况时,将异常放入result属性中,并将改变后的Call类型对象返回。其代码如下所示:
package remotecall;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
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;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
/** *//**
*服务端.
*/
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
publicclass SimpleServer
{
//存放远程对象的缓存
private Map<String, Object> remoteObjects = new HashMap<String, Object>();
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
*将一个远程对象加入缓存中.
*@paramclassNamemap中的key——类名
*@paramremoteObject远程对象
*/
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
publicvoid register(String className, Object remoteObject)
{
remoteObjects.put(className, remoteObject);
}
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
publicvoid service() throws Exception
{
ServerSocket serverSocket = new ServerSocket(8000);
System.out.println("服务器启动
");
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
while(true)
{
Socket socket = serverSocket.accept();
InputStream in = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(in);
OutputStream out = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//接收从客户端发送的Call对象
Call call = (Call) ois.readObject();
//调用call的toString()方法打出className和methodName
System.out.println(call);
//调用对象的相关方法
call = invoke(call);
//将放置了result值的对象放入输出中返回
oos.writeObject(call);
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//关闭相关资源
ois.close();
oos.close();
socket.close();
}
}
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
*调用远程方法的指定方法,并将返回值放入call对象的result中.
*@paramcall调用对象
*@return返回设置了result值的call对象
*/
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Call invoke(Call call)
{
Object result = null;
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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);
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (remoteObject == null)
{
thrownew Exception(className + "远程对象不存在!");
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else
{
//通过传入相应参数调用remoteObject的指定方法
//并将返回值放入result中.
result = method.invoke(remoteObject, params);
}
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} catch(Exception e)
{
result = e;
}
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//设置返回值
call.setResult(result);
return call;
}
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
*测试方法.
*/
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
/** *//**
*客户端类.
*/
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
publicclass SimpleClient
{
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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);
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//向服务器发送call对象
oos.writeObject(call);
//接收从服务端发送回的对象
call = (Call) ois.readObject();
//打印结果信息
System.out.println(call.getResult());
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//关闭资源
ois.close();
oos.close();
socket.close();
}
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
*测试方法.
*/
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
publicstaticvoid main(String[] args) throws Exception
{
SimpleClient client = new SimpleClient();
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
//构建一个正确Call对象
Call call = new Call();
call.setClassName("remotecall.HelloService");
call.setMethodName("echo");
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
call.setParamTypes(new Class[]
{String.class});
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
import java.util.Date;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
publicinterface HelloService
{
public String echo(String msg);
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
public Date getTime();
}
HelloServiceImpl的内容:
package remotecall;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
import java.util.Date;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
publicclass HelloServiceImpl implements HelloService
{
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public String echo(String msg)
{
return"echo: " + msg;
}
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
![](http://www.blogjava.net/Images/OutliningIndicators/None.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
/** *//**
*动态代理类.
*/
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
publicclass HelloServiceProxyFactory
{
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
publicstatic HelloService getHelloServiceProxy(final HelloService helloService)
{
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
InvocationHandler handler = new InvocationHandler()
{
public Object invoke(Object proxy, Method method, Object args[])
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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(),
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
new Class[]
{classType},
handler);
}
![](http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif)
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
*测试方法.
*/
![](http://www.blogjava.net/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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中也运到了动态代理机制,有兴趣的朋友可查找相关资料。
阅读(1033) | 评论(1) | 转发(0) |