天天看點

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
    RMI+Spring,實作遠端調用
  • 啟動Consumer

    消費者

    RMI+Spring,實作遠端調用
    提供者
    RMI+Spring,實作遠端調用

結束語

感覺有點意思,開心

學無止境,諸君共勉