posts - 297,  comments - 1618,  trackbacks - 0
      说明:本文为孙卫琴的《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.HelloServicemethodName=echo

className=remotecall.HelloEchomethodName=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,阿蜜果"));
    }

}


     运行后可看到这个代理类是动态生成的。在SpringAOP中也运到了动态代理机制,有兴趣的朋友可查找相关资料。

posted on 2007-09-19 13:21 阿蜜果 阅读(2742) 评论(3)  编辑  收藏 所属分类: Java


FeedBack:
# re: Java反射机制学习笔记(二)
2007-09-19 14:46 | 千里冰封
呵呵,不错,JAVA这方面确实挺强大的,可以在运行时得到有关类的一些信息
  回复  更多评论
  
# re: Java反射机制学习笔记(二)
2007-09-19 19:41 |
文章写的不错,人长得很好看
顶一下  回复  更多评论
  
# re: Java反射机制学习笔记(二)
2007-10-12 18:29 | 大妈
文章写的不错,人长得很好看 ……——……  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航:
 
<2007年9月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

      生活将我们磨圆,是为了让我们滚得更远——“圆”来如此。
      我的作品:
      玩转Axure RP  (2015年12月出版)
      

      Power Designer系统分析与建模实战  (2015年7月出版)
      
     Struts2+Hibernate3+Spring2   (2010年5月出版)
     

留言簿(262)

随笔分类

随笔档案

文章分类

相册

关注blog

积分与排名

  • 积分 - 2286196
  • 排名 - 3

最新评论

阅读排行榜

评论排行榜