Spring Remoting with RMI – 使用RMI的Spring Remoting

最后修改: 2017年 7月 11日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Java Remote Method Invocation allows invoking an object residing in a different Java Virtual Machine. It is a well-established technology yet a little cumbersome to use, as we can see in the official Oracle trail dedicated to the subject.

Java远程方法调用允许调用驻留在不同Java虚拟机中的对象。这是一项成熟的技术,但使用起来却有点麻烦,我们可以在专门讨论这个问题的官方Oracle线索中看到。

In this quick article, we’ll explore how Spring Remoting allows to leverage RMI in an easier and cleaner way.

在这篇文章中,我们将探讨Spring Remoting如何允许以更简单、更干净的方式利用RMI

This article also completes the overview of Spring Remoting. You can find details about other supported technologies in the previous installments: HTTP Invokers, JMS, AMQP, Hessian, and Burlap.

这篇文章也完成了对Spring Remoting的概述。你可以在之前的文章中找到关于其他支持的技术的细节。HTTP InvokersJMSAMQPHessian 和 Burlap

2. Maven Dependencies

2.Maven的依赖性

As we did in our previous articles, we’re going to set up a couple of Spring Boot applications: a server that exposes the remote callable object and a client that invokes the exposed service.

正如我们在以前的文章中所做的那样,我们将设置几个Spring Boot应用程序:一个暴露远程可调用对象的服务器和一个调用暴露服务的客户端。

Everything we need is in the spring-context jar – so we can bring it in using whatever Spring Boot helper we prefer – because our main goal is just to have the main libraries available.

我们需要的一切都在spring-context jar中–所以我们可以使用我们喜欢的任何Spring Boot帮助器将其引入–因为我们的主要目标只是让主要库可用。

Let’s now go forward with the usual spring-boot-starter-web – remembering to remove the Tomcat dependency to exclude the embedded web service:

现在让我们按照惯例spring-boot-starter-web进行操作–记住要删除Tomcat依赖关系,以排除嵌入式Web服务。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

3. Server Application

3.服务器应用

We’ll start declaring an interface that defines a service to book a ride on a cab, that will be eventually exposed to clients:

我们将开始声明一个接口,该接口定义了一个预订出租车的服务,该服务最终将被暴露给客户。

public interface CabBookingService {
    Booking bookRide(String pickUpLocation) throws BookingException;
}

Then we’ll define a bean that implements the interface. This is the bean that will actually execute the business logic on the server:

然后我们将定义一个实现该接口的Bean。这是一个将在服务器上实际执行业务逻辑的bean。

@Bean 
CabBookingService bookingService() {
    return new CabBookingServiceImpl();
}

Let’s continue declaring the Exporter that makes the service available to clients. In this case, we’ll use the RmiServiceExporter:

让我们继续声明Exporter,使服务对客户可用。在这种情况下,我们将使用RmiServiceExporter

@Bean 
RmiServiceExporter exporter(CabBookingService implementation) {
    Class<CabBookingService> serviceInterface = CabBookingService.class;
    RmiServiceExporter exporter = new RmiServiceExporter();
    exporter.setServiceInterface(serviceInterface);
    exporter.setService(implementation);
    exporter.setServiceName(serviceInterface.getSimpleName());
    exporter.setRegistryPort(1099); 
    return exporter;
}

Through setServiceInterface() we provide a reference to the interface that will be made remotely callable.

通过setServiceInterface(),我们提供了一个将被远程调用的接口的引用。

We should also provide a reference to the object actually executing the method with setService(). We then could provide the port of the RMI registry available on the machine where the server runs if we don’t want to use the default port 1099.

我们还应该用setService()提供一个实际执行该方法的对象的引用。然后,如果我们不想使用默认的1099端口,我们可以提供服务器运行的机器上可用的RMI注册表的端口。

We should also set a service name, that allows identifying the exposed service in the RMI registry.

我们还应该设置一个服务名称,以便在RMI注册表中识别暴露的服务。

With the given configuration the client will be able to contact the CabBookingService at the following URL: rmi://HOST:1199/CabBookingService.

通过给定的配置,客户端将能够联系CabBookingService,网址如下。rmi://HOST:1199/CabBookingService

Let’s finally start the server. We don’t even need to start the RMI registry by ourselves because Spring will do that automatically for us if such registry is not available.

最后我们来启动服务器。我们甚至不需要自己启动RMI注册表,因为如果没有这样的注册表,Spring会自动为我们做这个。

4. Client Application

4.客户应用

Let’s write now the client application.

现在我们来写一下客户端的应用程序。

We start declaring the RmiProxyFactoryBean that will create a bean that has the same interface exposes by the service running on the server side and that will transparently route the invocations it will receive to the server:

我们开始声明RmiProxyFactoryBean,它将创建一个具有与服务器端运行的服务所暴露的相同接口的bean,并将透明地将它所收到的调用路由到服务器。

@Bean 
RmiProxyFactoryBean service() {
    RmiProxyFactoryBean rmiProxyFactory = new RmiProxyFactoryBean();
    rmiProxyFactory.setServiceUrl("rmi://localhost:1099/CabBookingService");
    rmiProxyFactory.setServiceInterface(CabBookingService.class);
    return rmiProxyFactory;
}

Let’s then write a simple code that starts up the client application and uses the proxy defined in the previous step:

然后让我们写一段简单的代码,启动客户端应用程序并使用上一步定义的代理。

public static void main(String[] args) throws BookingException {
    CabBookingService service = SpringApplication
      .run(RmiClient.class, args).getBean(CabBookingService.class);
    Booking bookingOutcome = service
      .bookRide("13 Seagate Blvd, Key Largo, FL 33037");
    System.out.println(bookingOutcome);
}

It is now enough to launch the client to verify that it invokes the service exposed by the server.

现在只需启动客户端来验证它是否调用了服务器所暴露的服务。

5. Conclusion

5.结论

In this tutorial, we saw how we could use Spring Remoting to ease the use of RMI that otherwise will require a series of tedious tasks as, among all, spinning up a registry and define services using interfaces that make heavy use of checked exceptions.

在本教程中,我们看到了如何使用Spring Remoting来简化RMI的使用,否则将需要一系列繁琐的工作,其中包括启动注册表和使用大量使用检查异常的接口定义服务。

As usual, you’ll find the sources over on GitHub.

像往常一样,你可以在GitHub上找到源代码