在Spring中使用远程服务

概述

本章主要讲述了如何在Spring中使用远程服务,如RMI,Caucho的Hessian和Burlap,HttpInvoker,Web Service等。

进阶阅读:java 几种远程服务调用协议的比较

一、Spring远程调用概览

        远程调用是客户端应用和服务端应用之间的会话。在客户端,它所需要的一些功能并不在该应用的实现范围之内,所以应用要向能提供这些功能的其他系统寻求帮助。而远程应用通过远程服务暴露这些功能。 
        RPC(remote-procedure call. RPC)远程过程调用: 就是执行流从一个应用传递给另一个应用,理论上另一个应用部署在跨网络的一台远程机器上。 
        Spring支持的多种不同的RPC模型

RPC模型使用场景
远程方法调用(RMI)不考虑网络限制时(例如防火墙),访问/发布基于Java的服务
Hessian或Burlap考虑网络限制时,通过HTTP访问/发布基于Java的服务。Hessian是二进制协议.而Burlap是基于XML的
HTTP invoker 考虑网络限制,并希望使用基于XML或专有的序列化机制实现Java序列化时,访问/发布基于Spring的服务
JAX-RPC和JAX-WS的WebService访问/发布平台独立的、基于SOAP的Web服务

    注:SOAP为简单对象访问协议,是基于XML的简易协议,可使应用程序在Http之上进行信息交换。即用于访问网络服务的协议。


    二、使用RMI
    
   1、服务端发布RMI服务

        如果你曾经创建过RMI服务,应该会知道这会涉及如下几个步骤:

  • 编写一个服务实现类,类中的方法必须抛出java.rmi.RemoteException异常;
  • 创建一个继承于java.rmi.Remote的服务接口;
  • 运行RMI编译器(rmic),创建客户端stub类和服务端skeleton类;
  • 启动一个RMI注册表,以便持有这些服务;
  • 在RMI注册表中注册服务。

        以传统方式发布RMI服务不仅需要做上诉那么多的工作,而且会抛出相当多的RemoteException和MalformedURLException异常。而这些异常通常是无法修复的,但是我们仍然需要写很多的样板代码去catch它。幸运的是,Spring提供了更简单的方式来发布RMI服务,不用再编写那些需要抛出RemoteException异常的特定RMI类,只需要简单地编写实现服务功能的POJO就可以了,Spring会处理剩余的事项。 

1、1 首先我们在服务端创建一个需要发布的服务接口:

public interface IHelloService {
    String getMsg();
}

1、2 该服务接口的实现类:

import org.springframework.stereotype.Service;

@Service
public class HelloServiceImpl implements IHelloService {
    @Override
    public String getMsg() {
        return "Hello World.(From RMI server)";
    }
}

1、3 配置RmiServiceExporter(RmiServiceExporter 可以用来把任意Spring管理的bean发布为RMI服务)

        RmiServiceExporter的实现原理: 通过把bean包装在一个适配器类中,然后适配器类被绑定到RMI注册表中,并且代理到服务类的请求——在本例中服务类也就是HelloServiceImpl.

 配置RmiServiceExporter的具体细节如下:

import com.scb.h2demo.service.HelloServiceImpl;
import com.scb.h2demo.service.IHelloService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.rmi.RmiServiceExporter;

@Configuration
public class RMIConfig {

    //服务端使用RMI发布服务配置
    @Bean
    public RmiServiceExporter helloServiceExporter(HelloServiceImpl helloService){
        RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
        //将服务端需要发布的服务接口实现类配置到RMI服务中
        rmiServiceExporter.setService(helloService);
        //设置RMI服务名(客户端依据此服务名来获取远程服务)
        rmiServiceExporter.setServiceName("HelloService");
        //将远程接口设置为RMI服务接口
        rmiServiceExporter.setServiceInterface(IHelloService.class);
        //为RMI服务端远程对象注册表设置端口
        rmiServiceExporter.setRegistryPort(9090);
        return rmiServiceExporter;
    }
}

这就是我们使用Spring把某个bean转变为RMI服务所需要做的全部工作。接下来,让我们看看客户端如何装配RMI服务。

2、客户端装配RMI服务

2、1 我们首先需要把服务端发布的服务接口类Copy到我们的项目下

public interface IHelloService {
    String getMsg();
}

2、2 配置RmiProxyFactoryBean(Spring的RmiProxyFactoryBean是一个工厂bean,该bean可以为RMI服务创建代理。)

下图展示了客户端和RMI的交互: (RmiProxyFactoryBean生成一个代理对象,该对象代表客户端来负责与远程的RMI服务进行通讯。客户端通过服务的接口与代理进行交互——所以需要2、1步骤,就如同远程服务就是一个本地的POJO)

RmiProxyFactoryBean 生成一个代理对象,该对象代表客户端来负责与远程的RMI服务进行通信。客户端通过服务的接口与代理进行交互,就如同远程服务就是一个本地的POJO

配置RmiProxyFactoryBean的具体细节如下:

import com.spring.security.springsecurity.service.IHelloService;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;

@Configuration
public class RMIConfig {
    @Bean
    public RmiProxyFactoryBean helloService(){
        RmiProxyFactoryBean rmiProxyFactoryBean=new RmiProxyFactoryBean();
        //url书写规则: rmi://registryHost:registryPort/ServiceName
        //registryHost为服务端项目运行地址,如果在本地跑,就是localhost
        //registryPort为服务端的远程对象注册表的端口号
        //ServiceName为服务端发布的服务名
        rmiProxyFactoryBean.setServiceUrl("rmi://localhost:9090/HelloService");
        //这里相当于使用接口注入,把远程的HelloServiceImpl实现类注入到客户端的IHelloService接口中。
        //这样我们就可以通过操作客户端的服务接口,来间接调用服务端的具体服务。
        rmiProxyFactoryBean.setServiceInterface(IHelloService.class);
        //当连接失败时是否刷新远程调用stub
        rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);
        return rmiProxyFactoryBean;
    }
}

2、3 使用服务

    @Autowired
    IHelloService helloService;

接着直接调用IHelloService中的方法即可。

    @GetMapping(value = "/RmiClient")
    @ResponseBody
    public String rmiMsg(){
        return helloService.getMsg();
    }

3、如何发布/装配多个服务

3、1 服务端发布多个服务

发布多个服务其实只需要配置多个RmiServiceExporter即可。如:下面例子在项目使用H2Database+Druid连接池+Spring Data JPA+Ehcache实现CRUD操作的基础上,将CRUD功能发布为服务。

    @Bean
    public RmiServiceExporter staffServiceExporter(StaffServiceImpl staffService){
        RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
        //将远程接口实现类对象设置到RMI服务中
        rmiServiceExporter.setService(staffService);
        //设置RMI服务名,RMI客户端依据此服务名来获取远程接口实现类对象引用
        rmiServiceExporter.setServiceName("StaffService");
        //将远程接口设置为RMI服务接口
        rmiServiceExporter.setServiceInterface(IStaffService.class);
        //为RMI服务端远程对象注册表设置端口
        rmiServiceExporter.setRegistryPort(9090);
        return rmiServiceExporter;
    }

3、2 客户端装配多个服务

同理,客户端装配多个服务也只需要配置多个RmiProxyFactoryBean(当然,IStaffService还是需要复制到客户端的,这个所放路径可随意)。

    @Bean
    public RmiProxyFactoryBean staffService(){
        RmiProxyFactoryBean rmiProxyFactoryBean=new RmiProxyFactoryBean();
        rmiProxyFactoryBean.setServiceUrl("rmi://localhost:9090/StaffService");
        rmiProxyFactoryBean.setServiceInterface(IStaffService.class);
        rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);
        return rmiProxyFactoryBean;
    }

需要注意的是,如果客户端在装配远程服务时,需要远程服务的pojo类,需要把服务端的POJO类复制到客户端的相同路径下。比如此处的Staff对象。

三、使用Hessian和Burlap

1、使用Hessian和Burlap的所需依赖包

        <!-- Hessian依赖包 -->
        <dependency>
            <groupId>com.caucho</groupId>
            <artifactId>hessian</artifactId>
        </dependency>
        <!-- Burlap依赖包 -->
        <dependency>
            <groupId>com.caucho</groupId>
            <artifactId>burlap</artifactId>
        </dependency>

2、服务端发布服务(以Hessian为例子)

package com.scb.h2demo.config;

import com.scb.h2demo.service.HessianServiceImpl;
import com.scb.h2demo.service.IHessianService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.caucho.HessianServiceExporter;

@Configuration
public class HessianConfig {
    /**
     * 服务端配置
     * @param hessianService
     * @return
     */
    @Bean("/hessian.service")
    public HessianServiceExporter hessianServiceExporter(HessianServiceImpl hessianService){
        HessianServiceExporter exporter=new HessianServiceExporter();
        exporter.setService(hessianService);
        exporter.setServiceInterface(IHessianService.class);
        return exporter;
    }
}

与RmiServiceExporter不同的是,Hessian和Burlap不需要配置serviceName属性。

@Bean("/hessian.service"):加斜杠会被SpringBoot自动暴露服务地址并发布服务。

3、客户端装配服务

package com.spring.security.springsecurity.config;

import com.spring.security.springsecurity.service.IHessianService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.caucho.HessianProxyFactoryBean;

@Configuration
public class HessianConfig {
    @Bean
    public HessianProxyFactoryBean hessianProxyFactoryBean(){
        HessianProxyFactoryBean hessianProxyFactoryBean=new HessianProxyFactoryBean();
        hessianProxyFactoryBean.setServiceUrl("http://localhost:8080/hessian.service");
        hessianProxyFactoryBean.setServiceInterface(IHessianService.class);
        return hessianProxyFactoryBean;
    }
}

四、使用Spring的HttpInvoker

其实在Spring中使用远程服务都是大同小异的,我们使用HttpInvoker也是这样,服务端发布服务配置如下:

package com.scb.h2demo.config;

import com.scb.h2demo.service.HttpInvokerServiceImpl;
import com.scb.h2demo.service.IHttpInvokerService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;

@Configuration
public class HttpInvokerConfig {
    @Bean("/httpInvoker.service")
    public HttpInvokerServiceExporter httpInvokerServiceExporter(HttpInvokerServiceImpl service){
        HttpInvokerServiceExporter exporter=new HttpInvokerServiceExporter();
        exporter.setService(service);
        exporter.setServiceInterface(IHttpInvokerService.class);
        return exporter;
    }
}

客户端装配服务配置如下:

package com.spring.security.springsecurity.config;

import com.spring.security.springsecurity.service.IHttpInvokerService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;

@Configuration
public class HttpInvokerConfig {
    @Bean
    public HttpInvokerProxyFactoryBean httpInvokerProxyFactoryBean(){
        HttpInvokerProxyFactoryBean proxyFactoryBean=new HttpInvokerProxyFactoryBean();
        proxyFactoryBean.setServiceUrl("http://localhost:8080/httpInvoker.service");
        proxyFactoryBean.setServiceInterface(IHttpInvokerService.class);
        return proxyFactoryBean;
    }
}

五、发布和使用Web Service

Web Service是一种跨编程语言和跨操作系统平台的远程调用技术。

相关阅读:WebService的相关概念            关于WebService 的用法及Demo

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值