(远程方法的调用)对象序列化反射在网络编程的运用

Posted lazyli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(远程方法的调用)对象序列化反射在网络编程的运用相关的知识,希望对你有一定的参考价值。

对象的序列化与反序列化【p213页】
当两个进程进行远程通信时,彼此可以发送各种类型的数据,如文本、图
片、语音和视频等。无论不纯二进制序列的形式在网络上传送。当两个
java进程进行远程通信时,一个进程把一个java对象发送到另一个进程
中。不过发送方需要把这个java对象转换为字节序列,才能在网络上传
送;接收方则需要把自己序列在恢复为java对象。把java对象转换为字
节序列的过程称为对象序列化;把字节序列恢复为java对象的过程称为
对象称为对象的反序列化。
JDK类库中的序列化API
java.io.ObjectOutputStream代表对象的输出流,它的writeObject
(Object obj)方法将对象进行序列化输出。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法
从一个源输入流读取字节序列,在将字节序列转换为一个java对象。
只有实现了Serializable或Externalizable接口的类的对象才能被序列
化。JDK类库中部分类(String类,包装类和Date类等)都实现了
Serializable接口。
当属性值被transient修饰,该属性是不会被序列化和反序列的
对序列化进行加密处理,[p224]
一个类如果实现了Externalizable接口,那么它必须具有public类型的
不带参数的构造方法,否则这个类无法反序列化。
------------
java Reflection API(java 反射机制)
主要功能:
1、在运行时判断任意一个对象所属的类
2、在运行时构造任意一个类的对象
3、在运行时判断任意一个类所具有的成员变量和方法;
4、在运行时任意调用一个对象的方法;
5、生成动态代理。

public class ReflectDemo01 {

    public static void main(String[] args) throws 

ClassNotFoundException {    
        Class classzz = A.class;
        System.out.println(classzz);
        Method[] methods = classzz.getMethods();
        for(Method method : methods){
            System.out.println(method.toString

());
        }
    }
}

class A{
    public void say(){
        
    }
}

在远程方法中运用反射机制
客户端调用服务端的方法:Simple客户端如何调用服务端的

public interface HelloService {
    public String echo(String msg);
    public Date getTime();
}
public class HelloServiceImpl implements HelloService {

    @Override
    public String echo(String msg) {
        return "echo" + msg;
    }

    @Override
    public Date getTime() {
        // TODO Auto-generated method stub
        return new Date();
    }

}

HelloServiceImpl对象的getTime()和echo()方法。SimpleClient客
户端需要把调用的方法名、方法参数类型、方法参数值,以及方法所属的
类名或接口发送给SimpleServer,SimpleServer再调用相关对象的方法
,然后把方法的返回值发送给SimpleClient。
用Call类封装相关信息,它包括调用的类名或接口名、方法名、方法参
数类型、方法参数值和方法执行结果。

public class Call implements Serializable{

    private String className;//表示类名或接口
    private String methodName;//表示方法名
    private Class[] paramTypes; //表示方法参数类型
    private Object[] params; //表示方法参数值
    
    //表示方法执行结果
    //如果方法正常执行,result为方法返回值,如果方法抛出异

常,那么result为异常
    private Object result;
    
    public Call(){
        
    }

    public Call(String className, String methodName, 

Class[] paramTypes, Object[] params) {
        super();
        this.className = className;
        this.methodName = methodName;
        this.paramTypes = paramTypes;
        this.params = params;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Class[] getParamTypes() {
        return paramTypes;
    }

    public void setParamTypes(Class[] paramTypes) {
        this.paramTypes = paramTypes;
    }

    public Object[] getParams() {
        return params;
    }

    public void setParams(Object[] params) {
        this.params = params;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }
    
    public String toString(){
        return "className="+className+" 

methodName="+methodName;
    }
    
}

SimpleClient调用SimpleServer端的HelloServerImpl对象echo()方法
流程:
1、SimpleClient创建一个Call对象,它包含了调用HelloService接口
的echo()方法的信息。
2、SimpleClient通过对象输出流把Call对象发送给SimpleServer。
3、SimpleServer通过对象输入流读取Call对象,运用反射机制调用
HelloServiceImpl对象的echo()方法,把echo()方法的执行结果保存到
Call对象中。
4、SimpleServer通过对象输出流把包含了方法执行结果的Call对象发送
给SimpleClient。
5、SimpleClient通过对象输入流读取Call对象,从中获得方法执行结果

public class SimpleServer {

    private Map remoteObjects = new HashMap<>();
    //把一个远程对象放入到缓存中
    public void register(String className,Object 

remoteObject){
        remoteObjects.put(className, remoteObject);
    }
    
    public void service() throws Exception{
        ServerSocket serverSocket = new 

ServerSocket(8088);
        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) ois.readObject

();//接收客户端发送的call对象
            System.out.println(call);
            call = invoke(call);//调用相关对象的

方法
            oos.writeObject(call);//向客户端发送

包含了执行结果的Call对象
            
            ois.close();
            oos.close();
            socket.close();
        }
    }
    
    public Call invoke(Call call){
        Object result = null;
        try {
            String className = 

call.getClassName();
            String methodName = 

call.getMethodName();
            Object[] params = call.getParams

();//表示方法参数值
            Class classType = Class.forName

(className); //表示方法参数类型
            Class[] paramTypes = 

call.getParamTypes();
            Method method = classType.getMethod

(methodName, paramTypes);
            Object remoteObject = 

remoteObjects.get(className);
            if(remoteObject == null){//判断服务端

是否有该类存在
                throw new Exception

(className + "的远程对象不存在");
            }else{
                result = method.invoke

(remoteObject, params);
            }
        } catch (Exception e) {
            result = e;
        }
        call.setResult(result);
        return call;
    }
    
    public static void main(String[] args) throws 

Exception {
        SimpleServer server = new SimpleServer();
        //把事先创建的HelloServiceImpl对象加入到服务器

的缓存中,
        //"com.lazy.socket.reflect.HelloService"类的

全限定名
        server.register

("com.lazy.socket.reflect.HelloService", new 

HelloServiceImpl());
        server.service();
    }
}

客户端

public class SimpleClient {
    
    public void invoke() throws Exception{
        Socket socket = new Socket("localhost", 

8088);//连接服务端
        OutputStream out = socket.getOutputStream();
        ObjectOutputStream oos = new 

ObjectOutputStream(out);
        InputStream in = socket.getInputStream();
        ObjectInputStream ois = new 

ObjectInputStream(in);
        
        Call call = new Call

("com.lazy.socket.reflect.HelloService",
                "getTime",new Class[]

{String.class},new Object[]{"hello"});
        oos.writeObject(call);//向服务端发送call对象
        call = (Call)ois.readObject();//接收包含了方

法执行结果的Call对象
        System.out.println(call.getResult()+"---");
        System.out.println(call.toString());
        
        ois.close();
        oos.close();
        socket.close();
    }
    
    public static void main(String[] args) throws 

Exception {
        new SimpleClient().invoke();
    }
    
}
注:Dubbo是调用服务端方法的。

 

以上是关于(远程方法的调用)对象序列化反射在网络编程的运用的主要内容,如果未能解决你的问题,请参考以下文章

第十一章 对象的生命周期

Java创建对象的几种方式

学习笔记Java中生成对象的5中方法

反射和序列化

JAVA创建对象有哪几种方式 ???

RMI反序列化漏洞分析