RMI+Spring,实现远程调用

前言

上篇实现了rmi远程跨JVM调用service,但是感觉很鸡肋吧!不能每个接口都单独注册一个RMI路径呀,太累了。所以这篇呢,会将rmi和spring集成起来,通过rmi实现远程反射调用spring容器中的其他接口。

准备工作

修改pom文件

<!-- spring支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.30</version>
</dependency>

要实现的效果

消费者通过远程rmi协议调用提供者内部的UserService接口(提供者只暴露RmiService服务)

代码实现

通用接口

  • 新建RmiService接口
package com.zyu.service;

import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Map;

/**
 * rmi远程服务接口,可以远程调用提供者的内部方法
 */
public interface RmiService extends Remote, Serializable {
    final String REMOTE_URL = "rmi://127.0.0.1:9080/RmiService";
    final int port = 9080;

    /**
     * 远程调用的方法信息
     * @param info 对象,方法,参数
     * @return
     */
    Object remoteInvoke(Map<String,Object> info) throws RemoteException;
}

  • 新建UserService接口
package com.zyu.service;

public interface UserService {
    String echoUser(String username);
}

Provider端实现

  • 实现RmiService接口
package com.zyu.service;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Map;

public class RmiServiceImpl extends UnicastRemoteObject implements RmiService {
    protected RmiServiceImpl() throws RemoteException {
    }

    public Object remoteInvoke(Map<String, Object> info) throws RemoteException {
        return null;
    }
}

  • 实现UserService接口
package com.zyu.service;

public class UserServiceImpl implements UserService {
    public String echoUser(String username) {
        return "hello," + username;
    }
}

  • 反射工具类
package com.zyu.utils;

import org.springframework.context.ApplicationContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;

/**
 * 反射工具类
 */
public class InvokeUtils {

    /**
     * 根据传递的方法信息,反射调用spring容器中的方法
     *
     * @param info
     * @param app
     * @return
     */
    public static Object call(Map<String, Object> info, ApplicationContext app) {
        String targetName = (String) info.get("target");
        String methodName = (String) info.get("method");
        Object[] args = (Object[]) info.get("args");
        Class<?>[] argTypes = new Class[args.length];
        int i = 0;
        for (Object arg : args) {
            argTypes[i++] = arg.getClass();
        }
        return call(app.getBean(targetName), methodName,argTypes,args);
    }


    /**
     * java反射
     *
     * @param target 调用的目标对象
     * @param methodName 方法名
     * @param argTypes 方法参数列表类型
     * @param args 方法参数列表
     * @return
     */
    public static Object call(Object target, String methodName, Class<?>[] argTypes, Object[] args) {
        try {
            Method method = target.getClass().getMethod(methodName, argTypes);
            return method.invoke(target, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
}

  • 服务暴露
package com.zyu;

import com.alibaba.fastjson.JSON;
import com.zyu.service.RmiService;
import com.zyu.service.RmiServiceImpl;
import com.zyu.service.UserService;
import com.zyu.service.UserServiceImpl;
import com.zyu.utils.InvokeUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.Map;

/**
 * 服务提供者,通过rmi协议暴露spring中相关的服务
 */
public class Provider {
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Config.class);
        app.start();
        System.out.println("spring容器启动成功");
        initRmiProtocol(app);

        System.in.read();
    }

    /**
     * 开放rmi调用协议
     * @param app
     */
    public static void initRmiProtocol(final AnnotationConfigApplicationContext app) throws RemoteException {
        RmiService rmiService = new RmiServiceImpl() {
            @Override
            public Object remoteInvoke(Map<String, Object> info) throws RemoteException {
                Object result = InvokeUtils.call(info, app);
                System.out.println("方法调用成功,返回值:"+ JSON.toJSONString(result));
                return result;
            }
        };

        try {
            //绑定端口
            LocateRegistry.createRegistry(RmiService.port);
            //绑定url
            Naming.bind(RmiService.REMOTE_URL,rmiService);
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (AlreadyBoundException e) {
            e.printStackTrace();
        }

        System.out.println("开放rmi协议。。。");
    }

    @Configuration
    static class Config{
        @Bean
        public UserService userService(){
            return new UserServiceImpl();
        }
    }
}

Consumer端实现

两种调用方式:

  • 自己组装反射的调用信息
  • 使用静态代理,封装反射信息
package com.zyu;

import com.alibaba.fastjson.JSON;
import com.zyu.service.RmiService;
import com.zyu.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;

public class Consumer {
    public static void main(String[] args) {
        //启动spring容器
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Config.class);
        app.start();
        //获取远程调用服务
        RmiService rmiService = app.getBean(RmiService.class);
        //调用方式1:手动组装要调用的信息
        Map<String, Object> info = new HashMap<String, Object>();
        info.put("target", "userService");
        info.put("method", "echoUser");
        info.put("args", new Object[]{"zyufocus"});
        try {
            Object ret = rmiService.remoteInvoke(info);
            System.out.println(JSON.toJSONString(ret));
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        //调用方式2:静态代理封装
        UserService userService = getService(rmiService);
        String result = userService.echoUser("zyufocus2");
        System.out.println("static proxy remote invoke,result:" + result);
    }

    //使用静态代理,自动封装需要反射的参数信息
    private static UserService getService(final RmiService rmiService) {
        return new UserService() {
            public String echoUser(String username) {
                //组装要调用的信息
                Map<String, Object> info = new HashMap<String, Object>();
                info.put("target", "userService");
                info.put("method", "echoUser");
                info.put("args", new Object[]{username});
                try {
                    return (String) rmiService.remoteInvoke(info);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                return null;
            }
        };
    }

    static class Config {
        @Bean
        public RmiService rmiService() {
            RmiService rmiService = null;
            try {
                rmiService = (RmiService) Naming.lookup(RmiService.REMOTE_URL);
            } catch (NotBoundException e) {
                e.printStackTrace();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            return rmiService;
        }
    }
}

测试

  • 启动Provider
    在这里插入图片描述

  • 启动Consumer
    消费者
    在这里插入图片描述
    提供者
    在这里插入图片描述

结束语

感觉有点意思,开心
学无止境,诸君共勉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值