反射 socket RPC

Posted 你错了我来改

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反射 socket RPC相关的知识,希望对你有一定的参考价值。

反射:

反射机制是在 运行状态中

对于任意一个类,都能够知道这个类的属性和方法

对于任意一个对象,都能够调用它的任意一个方法和属性


反射提供的功能:

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判断任意一个类所具有的成员变量和方法

在运行时调用任意一个对象的方法

生成动态代理



获得反射对象 (反射入口):Class

1.Class.forName(全类名)  Class<?> perClazz = Class.forName("包名.类名")

2.类名.class  Class<?>  perClazz2 = Person.class

3.对象.getClass() 

Person per = new Person()

Class<?> perClazz3 = per.getClass();




获取所有的公共的方法 (1.本类 父类 以及接口中的所有方法  2. 符合访问修饰符规律)

Method[] methods = perClazz.getMethods()


获取所有的接口

Class<?> [] interface = perClazz.getInterfaces()


获取所有父类

Class<?> superClass = perClazz.getSuperclass()


获得所有的构造方法

Constructor<?>[] constructors = perClazz.gerConstructors()


获取所有的公共属性

Field[] fields = perClazz.getFields()


获取当前类的所有方法(1.只能是当前类 2. 忽略访问修饰符限制)

Method [] declaredMethods = perClazz.gerDeclaredMethods()


获取所有属性

Field [] declaredFields = perClazz.gerDeclaredFields()


获取当前凡是所代表类(接口)的对象(实例)

Object instance = perClazz.newInstance();




通过反射获取对象的实例,并操作该对象

Object instance = perClazz.newInstance();

Person per = (Person) instance;

per.setName("zs")

per.setAge(36)


操作属性

Person per = (Person)perClazz.newInstance();

Field idField = perClazz.getDeclaredField("id")

idField.set(per,1) // 相当于per.setId(1)


修改属性的访问权限 使用反射时,如果因为访问修饰符限制造成异常 可以通过Field/Method的setAccessible(true)来解决。

idField.setAccessible(true)


//操作方法 无参

Method method = perClazz

.getDeclaredMethod("privateMethod",null);

method.invoke(per,null) // 方法的调用  invoke()


//操作方法 有参

Method method2 = perClazz

.getDeclaredMethod("privateMethod2",String.class);

method2.setAccessible(true)

method2.invoke(per,"zs");


//拿本类全部的构造方法

Constructor<?>[] constructors = perClazz.gerDeclaredConstructors();


获取指定的构造方法

Constructor<?> constructor = perClazz.getConstructor(int.class)


Constructor<?> constructor2 = perClazz

.getDeclaredConstructor(String.class);


通过构造方法生成对象 

Person per = (Person)constructor.newInstance(12);


constructor2.setAccessible(true);

Person per2 = (Person)constructor2.newInstance("zs");



运行之前不知道,运行之中才知道  弄一个class.txt文件

里面有className =  要访问的类名

      methodName = 要使用的方法名

  

Properties prop = new Properties();//读取属性文件

prop.load(new FileReader("class.txt"));

String className = prop.getProperty("className");

String methodName = prop.getProperty("methodName");


Class<?> perClazz = null;

perClazz = Class.forName(className);


perClazz.getMethod(methodName);//参数是可变参数,可以不写,这里不知道,就没有写 只有构造方法会重名 不用担心只给方法名 回重名的问题,重名看参数

method.invoke(perClazz.newInstance())//同理 这里没有写参数  是可以不写的 



反射可以 越过泛型检查

虽然可以通过法反射访问private等访问修饰符不允许访问的属性/方法,也可以越过泛型检查,但实际开发中不推荐这么做

ArrayList<Integer> list = new ArrayList<>();

Class<?> listClazz = list.getClass();//获取反射入口

Method method = listClazz.getMethod("add",Object.class);

method.invoke(list,"zs.....");


可以建一个工具类  做一个万能的set方法

//obj.setPropertyName(value);

public void setProperty(Object obj,String propertyName,Object value){

Class<?> clazz = obj.getClass();

Field field = clazz.getDeclaredField(propertyName);

field.setAccessible(true);

field.set(obj,value);

}


使用反射操作动态的一维数组

public static void array1(){

Scanner input = new Scanner(System.in);

sout("请输入数组的类型:")

String type = input.next();

sout("请输入数组的个数:);

int num = input.nextInt();

Class<?> c = Class.forName(type);

Object arr = Array.newInstance(c,num);


//给数组的一个元素赋值

Array.set(arr,0,"zs")'

sout(Array.get(arr,0));

}


使用反射操作动态的二维数组

public static void array1(){

Scanner input = new Scanner(System.in);

sout("请输入数组的类型:")

String type = input.next();

int dim[] = new int[2];

sout("请输入数组的行数 列数:);

dim[0] = input.nextInt();

dim[1] = input.nextInt();

Class<?> c = Class.forName(type);

Object arr = Array.newInstance(c,dim);

//从二维数组中获取一行(第二行),一行就是一个数组(一维的)arr2 代表的是第二行的一维数组

Object arr2 = Array.get(arr,2)

//给数组的一个元素赋值 (第二行 第一列 的值为369)

Array.set(arr2,1,369)'

sout(Array.get(arr2,1));

}






socket:

服务端和客户端交互 从网络上来说 有两种方式:

基于tcp的socket传输

基于udp的

1 服务端要发布服务时需要绑定一个端口号 ServerSocket(端口号)

3 服务端ServerSocket accept()用于监听是否有科幻段访问

4 服务端通过OutputStream() server发送

5 客户端通过InputStream()接受

6 客户端通过OutputStream() client发送

7 服务端通过InputStream()接受

8 close


publie class MyServer{

main{

//绑定服务的端口,其ip为本机ip

ServerSocker server = new ServerSocker(9999);

while(true){

Socket socket = server.accept();

//下载的线程

MyDownload download = new Mydownload(socket);

//Runable -> Thread

Thread downLoadThread = new Thread(download);

downLoadThread.start();

//上面的优化写法

new Thread(new Mydownload(socket)).start();

}

/**

//发送字符串

String info = "hello";//变量在内存中 可以直接拿到

out.write(info.getBytes());

**/

}

}


public class MyClient{

main{

//客户端 连接Server发布的服务

Socket socket = new Socket("127.0.0.1",9999);

//接受服务端发送的消息

InputStream in = socket.getInputStream():

/**

//接收字符串

byte[] bs = new Byte[100];

in.read(bs);//读取发送来的数据

sout(new String(bs));

**/

/**

//客户端向服务端做出反馈

OutputStream out = socket.getOutputStream();

String info = "world";

out.write(info.getBytes());

**/

//接受每次发来的文件切片

byte[] bs = new Byte[100];

int len = -1;

OutputStream fileOut = new FileOutputStream("E:\aaa.rar");

while((len = in.read(bs)) != -1){

fileOut = write(bs,0,len);

}

fileOut.close();

in.close()

out.close()

socket.close();

}

}

反射 socket RPC

//处理下载线程

public class MyDownload implements Runnable{


privare Socket socket;

public MyDownload(Socket socket){

this.socket = socket'

}

@override

run(){

//服务端向客户端发送消息

OutputStream out = socket.getOutputStream();

//准备要发送的文件

File file = new File("E:\nginx-1.4.7.rar");

//将此文件 从硬盘读入到内存

InputStream fileIn = new FileInputStream(file);

int len = -1;

byte[] fileBytes = new byte[100];//定义每次发送的文件大小

//因为文件较大 不能一次发送完毕 因此需要通过循环来分次发送

while((len = fileIn.read(fileBytes)) != -1){

out.write(fileBytes,0,len)

}

/**

//接受客户端消息

InputStream in = socket.getInputStream():

byte[] bs = new Byte[100];

in.read(bs);//读取发送来的数据

sout(new String(bs));

**/

fielIn.close()

in.close()

out.close()

socket.close()

server.close()

}

}






rpc


反射:客户端需要传递一个字符串,而服务端需要通过该字符串解析出 

该字符串代表的接口的一切信息 通过反射技术


客户端-服务端通信 socket技术


服务端需要根据客户端的不同请求,返回不同的接口类型。

客户端就要接受到不同的接口类型   通过动态代理。


服务中心ServerCenter要干的事情:

start  stop  register

//(标准的先写接口,再实现)

public class ServerCenter implements Server{

//map:服务端所有可供客户端访问的接口,都注册到该map中

//key:接口的名字  value:真正的接口实现

private static HashMap<String,Class> serviceRegister = 

                                                                            new HashMap<>();


private static int port;

//线程池:每一个线程可以处理一个客户请求

        //获得处理器的个数 创建线程池

private static ExecutorService executor = Executors

.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

private static boolean isRunning = false;

//访问服务端的端口号通过构造方法传进来

public ServerCenter(int port){

this.port = port;

}

public void register(Class service,Class serviceImpl){

serviceRegister.put(service.getName(),serviceImpl);

}


public void start(){

ServerSocket server = new ServerSocker();

server.bind(new InetSocketAddress(port));

isRunning = true;//服务已经开启

while(true){

sout("start server");

//等待客户端链接 客户端每发出一次请求 

                                        则服务端从线程池,中获取一个线程对象去处理

Socket socket = server.accept();


//启动线程 去处理客户请求

executor.execute(new ServiceTask(socket));

}

}

public void stop(){//关闭服务

isRunning = false;

executor.shutdown();

}

private static  class ServiceTask implements Runable{

private Socket socket;

public ServiceTask(){}

public ServiceTask(Socket socket){

this.socket = socket;

}

@Override

public void run(){//线程做的事

ObjectInputStream input = null;

ObjectOutputStream output = null;

//接受客户端连接 处理请求

input = new ObjectInputStream

                                                                        (socket.getInputStream());

//因为ObjectInputStream对 发送数据的顺序 严格要求,

                                                        因此需要按照发送的顺序 逐个接受

String serviceName = input.readUTF();

String methodName = input.readUTF();

Class[] parameterTypes = (Class[])input.readObject();

Object[] arguments = (Object[])input.readObject();

//根据客户请求,找到具体的接口

Class ServiceClass = serviceRegister.get(serviceName);

//找到具体的方法

Method method = ServiceClass

                                        .gerMethod(methodName,parameterTypes);

//执行方法

Object result = method

                                    .invoke(ServiceClass.newInstance(),arguments);

//向客户端 将方法执行完毕的返回值  传给客户端

output = new ObjectOutputStream

                                                                    (socket.getOutputStream);

output.writeObject(result);

//最后该关的都关了 

if(output!= null) output.close();

......

}

}

}



public class Client{

//获取代表服务器接口的动态代理对象

//serviceInterface:请求的接口

//addr:带请求服务器的ip:端口

public static <T> T getRemoteProxyObj(Class serviceInterface,

InetSocketAddress address,){

/**

newProxyInstance(a,b,b)

a:类加载器:需要哪个代理类,就需要将他的类加载器传进去

b:需要代理的对象,具备哪些方法------也就是接口

**/

return         (T)Proxy.newProxyInstance(serviceInterface.getClassLoader(),

new Class<?>[]{serviceInterface},new InvocationHandler(){

//proxy:代理对象 method:哪个方法  args:参数列表

@Override

public Object invoke(Object proxy,

                                                           Method method,Object[] args){

//客户端向服务端发送请求,请求某一个具体的接口

Socket socket = new Socket();

socket.connect(addrress);

ObjectOutputStream output = new

ObjectOutputStream(socket.getOutputStream());//发送序列化流(对象流)

//接口名 方法名 :writeUTF

output.writeUTF(serviceInterface.getName());

output.writeUTF(me.getName());

//方法参数的类型  方法参数 Object

output.writeObject(method.getParameterTypes());

output.writeObject(args);

//等待服务端处理

//接收服务端处理过的返回值

ObjectInputStream input = new 

                                        ObjectInputStream(socket.getInputStream());

return input.readObject();

//该关的关了

......

}

});

}

}



rpc总结

客户端通过socket请求服务端,并且通过字符串形式/Class形式 将需要请求的接口发送给服务端


服务端将可以提供的接口注册到 服务中心(通过map保存  key:接口的名字 value:接口的实现类)


服务端接受到客户端的请求后 通过请求的接口名 在服务中心的map中寻找对应接口的实现类


通过动态代理:发送 接口名 方法名等


服务端找到之后 解析刚才服务端发送来的 接口名 方法名,解析完后

通过反射技术  将该方法执行  执行完毕后  再将该方法的返回值返回给客户端









以上是关于反射 socket RPC的主要内容,如果未能解决你的问题,请参考以下文章

使用Socket&反射&Java流操作进行方法的远程调用(模拟RPC远程调用)

java 的注解 反射 http 协议 socket

Pythonday7:Python学习(面向对象进阶反射socket介绍)

[Java]Java 网络编程(Socket)和反射

python开发学习-day07(面向对象之多态类的方法反射新式类and旧式类socket编程)

W7_staticmethod_classmethod_property_反射_exception_socket