Spring架构篇--2.6.1 远程通信基础--Rpc-Socket实战进阶篇--通过注解完成远程通信

前言:实战篇demo 实现了使用socket通过tcp 连接完成客户端与服务端接口的调用和 数据返回,但是实际上在spring 中我们通常使用注解的方式完成方法的调用;

1 服务端修改:

1.1 定义注解 RpcShopRemote,并在实现类上增加改注解标识 :

package com.example.springcloudshopservice.shop;

import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//  定义注解
@Component
@Target(ElementType.TYPE)// 定义注解范围,type代表类
@Retention(RetentionPolicy.RUNTIME)// 保留的时间
public @interface RpcShopRemote {
    
    
}

ShopService:

package com.example.springcloudshopservice.shop;

import org.example.service.ShopService;

//@Service
@RpcShopRemote
public class ShopServiceImpl implements ShopService {
    
    
    @Override
    public String getOneShop(String shopId) {
    
    

        return "苹果";
    }
}

1.2 需要在spring 容器启动之后对外发布服务:
1) 定义存放对外发布服务bean 的类:

package com.example.springcloudshopservice.shop;

import org.example.service.RpcRequest;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Mediator {
    
    
    // 放入bean 的实例
    public static Map<String, BeanMethod> map = new ConcurrentHashMap<>();

    private static volatile Mediator instance;
	// 单例实现
    public static Mediator getInstance() {
    
    
        if (null == instance) {
    
    
            synchronized (Mediator.class) {
    
    
                if (null == instance) {
    
    
                    instance = new Mediator();
                }
            }
        }
        return instance;
    }
	// 定义具体调用实现类的方法
    public Object process(RpcRequest request) {
    
    
        String key = request.getClassName() + "." + request.getMethodName();
        BeanMethod beanMethod = map.get(key);
        if (null == beanMethod) {
    
    
            return null;
        }
        Method method = beanMethod.getMethod();
        try {
    
    
            return method.invoke(beanMethod.getBean(), request.getArgs());
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        } catch (InvocationTargetException e) {
    
    
            e.printStackTrace();
        }
        return null;
    }

}

2)将带有RpcShopRemote 注解的发布类统一放入到Mediator 的map :

package com.example.springcloudshopservice.shop;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class InitMediator implements BeanPostProcessor {
    
    

    /**
     * bean 加载完成之后
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        // 只处理加了RpcShopRemote注解的bean
        if (bean.getClass().isAnnotationPresent(RpcShopRemote.class)) {
    
    
            Method[] methods = bean.getClass().getDeclaredMethods();
            for (Method method : methods) {
    
    
                Class<?>[] classes =  bean.getClass().getInterfaces();
                String key = bean.getClass().getInterfaces()[0].getName() + "." + method.getName();
                BeanMethod beanMethod = new BeanMethod(bean, method);
                Mediator.map.put(key, beanMethod);
            }
        }

        return bean;
    }


}

  1. spring 容器启动完成后发布服务:
package com.example.springcloudshopservice;

import com.example.springcloudshopservice.shop.ProcessHandler;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// spring 容器启动完成之后发布 ContextRefreshedEvent 事件
@Component
public class SocketServerInitial implements ApplicationListener<ContextRefreshedEvent>    {
    
    
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
        ServerSocket socket = null;
        try{
    
    
        	// 服务端建立连接
            socket = new ServerSocket(8080);
            while (true){
    
    
                Socket socket1 =socket.accept();
                // 有连接进入使用线程进行方法调用
                executorService.execute(new ProcessHandler(socket1));
            }

        }catch (Exception e){
    
    

        }finally {
    
    
            if (null != socket){
    
    
                try {
    
    
                    socket.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }
}

4) 具体方法的调用:

package com.example.springcloudshopservice.shop;

import org.example.service.RpcRequest;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.Socket;

public class ProcessHandler implements Runnable {
    
    
    private Socket socket;
    private Object service;

    public ProcessHandler(Socket socket, Object service) {
    
    
        this.socket = socket;
        this.service = service;
    }
    public ProcessHandler(Socket socket) {
    
    
        this.socket = socket;
    }


    @Override
    public void run() {
    
    
        ObjectInputStream inputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
    
    
            inputStream = new ObjectInputStream(socket.getInputStream());
            RpcRequest request = (RpcRequest) inputStream.readObject();
            // 反射调用方法
//            Object res = invokeMenthod(request);
            Mediator mediator = Mediator.getInstance();
            Object res = mediator.process(request);
            System.out.println("res = " + res);

            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(res);
            objectOutputStream.flush();

        } catch (Exception e) {
    
    
            if (null != inputStream) {
    
    
                try {
    
    
                    inputStream.close();
                } catch (IOException ioException) {
    
    
                    ioException.printStackTrace();
                }
            }
            if (null != objectOutputStream) {
    
    
                try {
    
    
                    objectOutputStream.close();
                } catch (IOException ioException) {
    
    
                    ioException.printStackTrace();
                }
            }
        }


    }

    private Object invokeMenthod(RpcRequest request) throws Exception {
    
    
        Class classz = Class.forName(request.getClassName());
        Method method = classz.getMethod(request.getMethodName(), request.getTypes());
        return method.invoke(service, request.getArgs());

    }
}

2 客户端:
1) 定义注解声明,并在controller 中 注入改bean:

import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Component
@Target(ElementType.FIELD)// 定义注解范围,FIELD代表属性
@Retention(RetentionPolicy.RUNTIME)// 保留的时间
public @interface ShopRefrence {
    
    
}

TestController:

import org.example.service.ShopService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    
    
    @ShopRefrence
    private ShopService shopService;


    @GetMapping("test")
    public String test() {
    
    
        return shopService.getOneShop("123");
    }

}

2) spring 容器在bean 初始化之前完成对类中增加ShopRefrence 注解的属性完成代理:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;

@Component
public class ReferenceInvokeProxy implements BeanPostProcessor {
    
    
	// 注入具体方法的实现
    @Autowired
    private RemoteInvocationHandler remoteInvocationHandler;
    /**
     * bean 初始化之前完成
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        Field [] fields = bean.getClass().getDeclaredFields();
        if (null == fields || 0== fields.length){
    
    
            return bean;
        }
        for (Field field : fields) {
    
    
            if (field.isAnnotationPresent(ShopRefrence.class)){
    
    
                Object proxy= Proxy.newProxyInstance(field.getType().getClassLoader(),new Class<?>[]{
    
    field.getType()},remoteInvocationHandler);
                try {
    
    
                    field.setAccessible(true);
                    // 设置属性的代理为具体的remoteInvocationHandler实现
                    field.set(bean,proxy);
                } catch (IllegalAccessException e) {
    
    
                    e.printStackTrace();
                }
            }
        }


        return bean;
    }
}

3) 代理的实现RemoteInvocationHandler:

扫描二维码关注公众号,回复: 14656027 查看本文章
import org.example.service.RpcRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

@Component
public class RemoteInvocationHandler implements InvocationHandler {
    
    
    @Value("${rpc.shop.host:localhost}")
    private String host;
    @Value("${rpc.shop.port:8080}")
    private int port;

//    public RemoteInvocationHandler(String host, int port) {
    
    
//        this.host = host;
//        this.port = port;
//    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        // 建立远程通信连接
        RpcNetTransport rpcNetTransport = new RpcNetTransport(host,port);
        RpcRequest request = new RpcRequest();
        request.setClassName(method.getDeclaringClass().getName());
        request.setMethodName(method.getName());
        request.setTypes(method.getParameterTypes());
        request.setArgs(args);

        return rpcNetTransport.send(request);


    }
}

4)连接的建立以及收发消息 RpcNetTransport:

import org.example.service.RpcRequest;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

public class RpcNetTransport {
    
    
    private String host;
    private int port;


    public RpcNetTransport(String host, int port) {
    
    
        this.host = host;
        this.port = port;
    }
    public Socket getSocket() throws IOException {
    
    
        return new Socket(host, port);

    }
    public Object send(RpcRequest request){
    
    
        Socket socket = null;
        ObjectInputStream inputStream = null;
        ObjectOutputStream objectOutputStream = null;
        try {
    
    
            socket = getSocket();

            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(request);
            objectOutputStream.flush();

            inputStream = new ObjectInputStream(socket.getInputStream());
            return inputStream.readObject();


        }catch (Exception ex){
    
    

            ex.printStackTrace();
        }finally {
    
    
            try {
    
    
                if (null != socket){
    
    
                    socket.close();
                }
            }catch (Exception e){
    
    

            }
        }
        return null;
    }

}

3 测试:
启动服务端和客户端后测试方法,完成方法的调用和结果的返回:
在这里插入图片描述
4 总结:

  • 服务端在ben 完成加载后,检查所有带有对外提供服务的注解类,并将具体实现类的bean 放入到map 中;
  • 服务端在spring容器启动之后,建立对特定端口的连接,如果有连接进入,则使用线程通过反射调用具体类中的方法;
  • 客户端通过在bean 初始化之前,完成对需要调用远程服务的注解扫描,客户端在调用远程服务时通过代理的方式,通过建立对应服务的socket 连接,发起方法的调用;

猜你喜欢

转载自blog.csdn.net/l123lgx/article/details/129150300