关于我们
About Us
网站建设
Site Design
售后服务
Call Center
经典案例
Projects
技术资料
Source
公司产品
Products
留言板
FeedBack
网站维护
Maintenance
 
 首页>> 技术资料>>JSP  
Java反射机制学习笔记(二)

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

 

 
 
北京四方互动网络技术有限公司
© 版权所有 2001-2005
地址:北京市海淀区知春路豪景佳苑3号楼401室
邮编:100086 公司地图
业务专线:010-62568654 62106102
传  真:010-82611617
公司总机:62106100/1/2/3
售后服务:分机808、881、812