Spring远程访问

fmms 12年前

前言

对于不适用EJB的Java EE应用来说,构建分布式应用的常见方式就是RMI(remote method invocation,Java远程方式调用),他使的运行在一个JVM上的Java对象能被另一个JVM访问到。
RMI相当复杂,而EJB引入的一个地方就在与他对开发者隐藏了底层RMI的细节。
Spring提供了成为Spring Remoting的全面的特性集来简化分布式应用的创建。


Spring的远程访问支持:
RMI:RMI是J2SE的一部分,用来创建分布式应用。Spring对RMI的支持可以减少公开和访问RMI服务的代码量,同时还帮我们处理了RMI中的大多数繁文缛节,如RemoteException的处理等。Spring还集成了RMI和JNDI,这对于CORBA服务的公开和访问来说用处是非常大的。
JAX-RPC:即“Java API for XML-based Remote Procedure Calls”,他为访问和公开RPC风格的SOAP Web服务提供了标准的Java API。Spring提供了支持类以简化JAX-RPC客户端应用的创建和基于Servlet的服务端点的创建。就像很多Java XML API一样,JAX-RPC也拥有很多实现,其中最流行的是Apache Axis,他是完全兼容于JAX-RPC的SOAP栈。本章的JAX-RPC示例将使用Axis实现。
JAX-WS:JAX-WS 2.0是JAX-RPC的 1.1的继任者。他提供了用于公开和访问SOAP 1.2Web服务及创建客户端应用的API。本章 的JAX0WS示例将使用XFire实现。
HTTP Invoker:HTTP Invoker架构是Spring的本地远程架构,他使用标准的Java序列化和HTTP为远程组件的构建提供了简单的解决方案。HTTP Invoker使用服务器端的Servlet容器来存放远程服务。这么做的好处是可以使用HTTP认证方法增强远程服务的安全性。
Hessian:Hessian是有Caucho创建的一个二进制协议,用以简化Web服务的创建。他特定于任何特定的传输方式,但通常使用HTTP。 Spring为其提供了支持类以简化Hessian服务的创建,他使用HTTP进行传输并提供了代理支持,这样就可以透明的访问Hessian服务了。
Burlap:Burlap也是由Caucho创建的,他是一个基于XML的协议,对Hessian协议是个补充。除了协议的细节之外,Burlap的使用方式与Hessian一样。Spring对Burlap的支持与对Hessian的支持差不多,事实上,在需要交换这两个协议时,Burlap类可以轻松替换掉Hessian类。

 

 

Spring Remoting架构

Spring Remoting架构的核心是一个服务输出器(service exporter)和一个代理生成器(proxy generator)的概念。


一旦公开了一个远程服务,接下来就需要创建一个客户端来访问这个服务。通常这是构建分布应用最复杂的之一,因为你需要密切关注所选的远程架构。
借助于Spring,你可以通过一个代理生成器创建远程资源的代理,这样就可以使用简单的Java接口访问远程服务了。
这种方式不仅降低了客户端代码的复杂度(由Spring处理远程架构相关事宜),而且还将应用于所选的远程架构进行了解耦(Spring隐藏了所有的是实现细节)。


Spring所支持的五种远程架构中的四种都可以使用这两个组件。而JAX-RPC/JAX-WS却没有服务输出器,因为公开服务的方法取决于你使用的 JAX-RPC/JAX-WS实现。然而Spring提供了ServletEndpointSupport类来简化JAX-RPC服务点(service endpoint)的创建,将其公开为一个servlet。

 

 

远程访问调用

自从1.1版本开始,RMI就已经成为Java的一部分,而且在很多远程方案中他都处于中心位置。Java中的CORBA支持就是通过RMI实现的,EJB也将RMI作为bean通讯的底层机制。JAX-RPC构建在RMI的概念之上以将Java对象公开成Web服务。


Java中的CORBA支持也可以通过接口定义语言(interface definition language,IDL)来实现。
IDL是一种声明性语言,可以创建彼此交互的对象---这些对象可能是不同的编程语言所创建的。
然而当前RMI在Java中使用的更广泛,这是由于其更好的安全性和垃圾收集能力。


1、开放任意的服务
构建RMI服务,通常你需要为服务定义一个接口,该接口继承自java.rmi.Remote接口。然后你的RMI服务需要实现该接口,最好还继承java.rmi.server.UnicastRemoteObject类。
Spring Remoting,通过RmiServiceExporter类简化上面的操作。


demo:
我们声明了两个bean:helloWorldService bean(要被公开的bean)和serviceExporter bean(将helloWorldService bean公开的RMI服务)。
package cn.partner4java.remoting;      public interface HelloWorld {   public String getMessage();  }      package cn.partner4java.remoting;      public class SimpleHelloWorld implements HelloWorld {       public String getMessage() {    return "Hello World";   }      }          <?xml version="1.0" encoding="UTF-8"?>  <beans xmlns="http://www.springframework.org/schema/beans"   xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="              http://www.springframework.org/schema/beans              http://www.springframework.org/schema/beans/spring-beans.xsd              http://www.springframework.org/schema/tx              http://www.springframework.org/schema/tx/spring-tx.xsd              http://www.springframework.org/schema/aop              http://www.springframework.org/schema/aop/spring-aop.xsd">   <bean id="helloWorldService" class="cn.partner4java.remoting.SimpleHelloWorld" />   <bean id="serviceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter">    <!-- 服务名 -->    <property name="serviceName" value="HelloWorld" />        <property name="service" ref="helloWorldService" />        <property name="serviceInterface" value="cn.partner4java.remoting.HelloWorld" />    <!-- RMI registry的端口号 -->    <property name="registryPort" value="9000" />    <!-- 用户通讯的服务端口。默认为0,表示使用一个匿名端口号 -->    <property name="servicePort" value="9001" />   </bean>  </beans>
2、通过代理访问RMI服务
借助于代理生成器,Spring会生成实现远程接口的那个远程服务的代理,这样你就可以通过服务接口与远程服务进行交互了,就好像他是本地组件一样。Spring隐藏了所有RMI细节。
就像所有的代理生成器一样,RMI代理生成器也实现了FactoryBean接口,这样我们就可以通过声明的方式创建并配置代理,然后将其做为依赖注入到组件中了。
demo:
package cn.partner4java.remoting.rmi;      import cn.partner4java.remoting.HelloWorld;      public class HelloWorldClient {   private HelloWorld helloWorldService;      public void setHelloWorldService(HelloWorld helloWorldService) {    this.helloWorldService = helloWorldService;   }           public void run(){    System.out.println(helloWorldService.getMessage());   }  }          <?xml version="1.0" encoding="UTF-8"?>  <beans xmlns="http://www.springframework.org/schema/beans"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xmlns:util="http://www.springframework.org/schema/util"         xmlns:tx="http://www.springframework.org/schema/tx"         xmlns:aop="http://www.springframework.org/schema/aop"         xmlns:context="http://www.springframework.org/schema/context"         xsi:schemaLocation="          http://www.springframework.org/schema/beans          http://www.springframework.org/schema/beans/spring-beans-2.5.xsd          http://www.springframework.org/schema/context          http://www.springframework.org/schema/context/spring-context-2.5.xsd">          <bean id="helloWorldService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">       <!-- 指向了RMI服务上的代理 -->          <property name="serviceUrl" value="rmi://localhost:9000/HelloWorld" />          <!-- 告诉代理生成器生成的接口应该实现哪个接口 -->          <property name="serviceInterface" value="cn.partner4java.remoting.HelloWorld"/>      </bean>          <bean id="helloWorldClient" class="cn.partner4java.remoting.rmi.HelloWorldClient">          <property name="helloWorldService" ref="helloWorldService" />      </bean>  </beans>
3、开放CORBA服务
RMI一个很棒的特性是他使你可以通过Internet Inter-Orb Protocol(IIOP)公开服务,这样使用其他语言编写的CORBA组件就可以访问这些服务了。对于不同语言编写的组件之间的交互来说,CORBA 是一个流行的解决方案,同时很多程序语言也对其进行了扩展支持。
通过RMI并使用IIOP而不是默认的Java远程方法协议(Java remote method protocol,JRMP)公开Java服务只需做以下两件事:生成正确的代码桩并通过CORBA Object Request Broker(ORB)公开服务。当使用JRMP公开服务时,Spring会令你的组件与RMI基础设施解耦,同时也无需创建桩了。当公开CORBA服务时,尽管Spring负责用ORB来注册服务,但他的用处却不那么大。
事实上,Spring并没有使用类来公开CORBA服务以达到简化的目的,相反他是通过JNDI来公开并寻找远程服务。当处理CORBA组建时,使用JNDI进行服务查找是最有效的,因为应用可以通过JNDI与ORB交互。

 

其他的服务的具体实现可查看《Spring高级程序设计》