使用Spring进行远程访问与Web服务[转]

Spring为各种远程访问技术的集成提供了整合类。Spring使得开发具有远程访问功能的服务变得相当容易,而这些远程访问服务由普通Spring POJO实现。目前,Spring支持四种远程技术:

  • 远程方法调用(RMI)。通过使用 RmiProxyFactoryBeanRmiServiceExporter,Spring同时支持传统的RMI(使用java.rmi.Remote接口和java.rmi.RemoteException)和通过RMI调用器实现的透明远程调用(支持任何Java接口)。

  • Spring的HTTP调用器。Spring提供了一种允许通过HTTP进行Java串行化的特殊远程调用策略,它支持任意Java接口(就像RMI调用器)。相对应的支持类是 HttpInvokerProxyFactoryBeanHttpInvokerServiceExporter

  • Hessian。通过 HessianProxyFactoryBeanHessianServiceExporter,可以使用Caucho提供的基于HTTP的轻量级二进制协议来透明地暴露服务。

  • Burlap。 Burlap是Caucho基于XML用来替代Hessian的项目。Spring提供了诸如 BurlapProxyFactoryBeanBurlapServiceExporter 的支持类。

  • JAX RPC。Spring通过JAX-RPC(J2EE 1.4's wweb service API)为Web services提供远程服务支持。

  • JAX-WS. Spring通过(在Java EE 5和Java 6中引入的JAX-RPC继承)为远程Web Services提供支持。

  • JMS. 通过JmsInvokerServiceExporterJmsInvokerProxyFactoryBean使用JMS做为底层协议提供远程服务.

在讨论Spring对远程访问的支持时,我们将使用下面的域模型和对应的服务:

public class Account implements Serializable{

    private String name;

    public String getName();

    public void setName(String name) {
      this.name = name;
    }
}
public interface AccountService {

    public void insertAccount(Account account);

    public List getAccounts(String name);
}
public interface RemoteAccountService extends Remote {

    public void insertAccount(Account account) throws RemoteException;

    public List getAccounts(String name) throws RemoteException;
}
// 该实现目前什么事情也不做
public class AccountServiceImpl implements AccountService {

    public void insertAccount(Account acc) {
        //  做一些事情……
    }
  
    public List getAccounts(String name) {
        // 做一些事情……
    }
}

我们将从使用RMI把服务暴露给远程客户端开始,同时探讨RMI的一些缺点。然后我们将演示一个使用Hessian的例子。

17.2. 使用RMI暴露服务

使 用Spring的RMI支持,你可以通过RMI基础设施透明的暴露你的服务。设置好Spring的RMI支持后,你会看到一个和远程EJB接口类似的配 置,只是没有对安全上下文传递和远程事务传递的标准支持。当使用RMI调用器时,Spring对这些额外的调用上下文提供了钩子,你可以在此插入安全框架 或者定制的安全证书。

17.2.1. 使用RmiServiceExporter暴露服务

使用RmiServiceExporter,我们可以把AccountService对象的接口暴露成RMI对象。可以使用 RmiProxyFactoryBean 或者在传统RMI服务中使用普通RMI来访问该接口。RmiServiceExporter 显式地支持使用RMI调用器暴露任何非RMI的服务。

当然,我们首先需要在Spring容器中设置我们的服务:

<bean id="accountService" class="example.AccountServiceImpl">
    
    <!--其他属性,或者一个DAO对象?-->

</bean>

正如你所见,我们覆盖了RMI注册的端口号。通常你的应用服务器也会维护RMI注册,最好不要和它冲突。更进一步来说,服务名是用来绑定服务的。所以本例中,服务绑定在 rmi://HOST:1199/AccountService。在客户端我们将使用这个URL来链接到服务。

Note:servicePort 属性被省略了(它的默认值为0).这表示在与服务通信时将使用匿名端口.

17.2.2. 在客户端链接服务

我们的客户端是一个使用AccountService来管理account的简单对象:

public class SimpleObject {

    private AccountService accountService;

    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }
}

为了把服务连接到客户端上,我们将创建一个单独的Spring容器,包含这个简单对象和链接配置位的服务:

<bean class="example.SimpleObject">
    <property name="accountService" ref="accountService"/>
</bean>

<bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
    <property name="serviceUrl" value="rmi://HOST:1199/AccountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

这就是我们在客户端为支持远程account服务所需要做的。Spring将透明的创建一个调用器并且通过RmiServiceExporter使得account服务支持远程服务。在客户端,我们用RmiProxyFactoryBean连接它。

17.3. 使用Hessian或者Burlap通过HTTP远程调用服务

Hessian提供一种基于HTTP的二进制远程协议。它是由Caucho开发的,可以在 http://www.caucho.com 找到更多有关Hessian的信息。

17.3.1. 为Hessian和co.配置DispatcherServlet

Hessian使用一个特定的Servlet通过HTTP进行通讯。使用Spring在Web MVC中就常用的 DispatcherServlet原理,可以很容易的配置这样一个Servlet来暴露你的服务。首先我们要在你的应用里创建一个新的Servlet(下面来自web.xml文件):

<servlet>
    <servlet-name>remoting</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>remoting</servlet-name>
    <url-pattern>/remoting/*</url-pattern>
</servlet-mapping>

你可能对Spring的DispatcherServlet很熟悉,这样你将需要在'WEB-INF'目录中创建一个名为'remoting-servlet.xml' (在你的servlet名称后) 的Spring容器配置上下文。这个应用上下文将在下一节中里使用。

另外,可以考虑使用Spring中简单的HttpRequestHandlerServlet。这允许你在根应用上下文(默认是'WEB-INF/applicationContext.xml')中插入远程exporter定义。每个servlet定义指向特定的exporter bean。在这种情况下,每个servlet的名称需要和目标exporter bean的名称相匹配。

17.3.2. 使用HessianServiceExporter暴露你的bean

在新创建的 remoting-servlet.xml 应用上下文里,我们将创建一个HessianServiceExporter来暴露你的服务:

<bean id="accountService" class="example.AccountServiceImpl">
    <!-- any additional properties, maybe a DAO? -->
</bean>

<bean name="/AccountService" class="org.springframework.remoting.caucho.HessianServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

现在,我们准备好在客户端连接服务了。不必显示指定处理器的映射,只要使用BeanNameUrlHandlerMapping把URL请求映射到服务上:所以,这个服务将在由bean名称指明的URL http://HOST:8080/remoting/AccountService 位置进行暴露。

另外一种选择, 在你的根应用上下文中创建一个 HessianServiceExporter (比如在'WEB-INF/applicationContext.xml'中):

<bean name="accountExporter" class="org.springframework.remoting.caucho.HessianServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

在后一情况下, 在'web.xml'中为exporter定义一个相应的servlet,也能得到同样的结果:这个exporter映射到request路径/remoting/AccountService。注意这个servlet名称需要与目标exporter bean的名称相匹配。

<servlet>
    <servlet-name>accountExporter</servlet-name>
    <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>accountExporter</servlet-name>
    <url-pattern>/remoting/AccountService</url-pattern>
</servlet-mapping>

17.3.3. 在客户端连接服务

使用HessianProxyFactoryBean,我们可以在客户端连接服务。同样的方式对RMI示例也适用。我们将创建一个单独的bean工厂或者应用上下文,而后简单地指明下面的bean SimpleObject将使用AccountService来管理accounts:

<bean class="example.SimpleObject">
    <property name="accountService" ref="accountService"/>
</bean>

<bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
    <property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

17.3.4. 使用Burlap

我们将不会详细讨论Burlap,它是一个基于XML的Hessian替代方案。它的配置和构建方法和上述Hessian的一样。只要把 Hessian 换成 Burlap 就行了。

17.3.5. 对通过Hessian或Burlap暴露的服务使用HTTP Basic认证

Hessian和Burlap的一个优势是我们可以容易的使用HTTP Basic认证,因为二者都是基于HTTP的。例如,普通HTTP Server安全机制可以通过使用 web.xml 安全特性来应用。通常,你不会为每个用户都建立不同的安全证书,而是在Hessian/BurlapProxyFactoryBean级别共享安全证书(类似一个JDBC DataSource)。

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="interceptors" ref="authorizationInterceptor"/>
</bean>

<bean id="authorizationInterceptor" 
      class="org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor">
    <property name="authorizedRoles" value="administrator,operator"/>
</bean>

这个例子里我们显式使用了BeanNameUrlHandlerMapping,并设置了一个拦截器,只允许管理员和操作员调用这个应用上下文中提及的bean。

17.4. 使用HTTP调用器暴露服务

与 使用自身序列化机制的轻量级协议Burlap和Hessian相反,Spring HTTP调用器使用标准Java序列化机制来通过HTTP暴露业务。如果你的参数或返回值是复杂类型,并且不能通过Hessian和Burlap的序列化 机制进行序列化,HTTP调用器就很有优势(参阅下一节,选择远程技术时的考虑)。

实际上,Spring可以使用J2SE提供的标准功能或Commons的HttpClient来实现HTTP调用。如果你需要更先进,更容易使用的功能,就使用后者。你可以参考 jakarta.apache.org/commons/httpclient

17.4.1. Exposing the service object

为服务对象设置HTTP调用器和你在Hessian或Burlap中使用的方式类似。就象为Hessian支持提供的 HessianServiceExporter,Spring的HTTP调用器提供了 org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter

为了在Spring Web MVC DispatcherServlet中暴露AccountService (如上所述), 需要在dispatcher的应用上下文中使用以下配置:

<bean name="/AccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

和在Hessian章节讲的一样,这个exporter定义将通过 DispatcherServlet标准的映射工具暴露出来。

做为可选项, 在你的根应用上下文中(比如'WEB-INF/applicationContext.xml')创建一个HttpInvokerServiceExporter :

<bean name="accountExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

另外,在'web.xml'中为这个exporter定义一个相应的servlet,其名称与目标exporter bean的名称相匹配:

<servlet>
    <servlet-name>accountExporter</servlet-name>
    <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>accountExporter</servlet-name>
    <url-pattern>/remoting/AccountService</url-pattern>
</servlet-mapping>

17.4.2. 在客户端连接服务

同样,从客户端连接业务与你使用Hessian或Burlap时所做的很相似。使用代理,Spring可以将你调用的HTTP POST请求转换成被暴露服务的URL。

<bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
    <property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

就象上面说的一样,你可以选择使用你想使用的HTTP客户端。缺省情况下,HttpInvokerProxy使用J2SE的HTTP功能,但是你也可以通过设置httpInvokerRequestExecutor属性选择使用Commons HttpClient

<property name="httpInvokerRequestExecutor">
    <bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor"/>
</property>

17.8. 在选择这些技术时的一些考虑

这里提到的每种技术都有它的缺点。你在选择一种技术时,应该仔细考虑你的需要和所暴露的服务及你在远程访问时传送的对象。

当使用RMI时,通过HTTP协议访问对象是不可能的,除非你用HTTP包裹RMI流。RMI是一种重量级协议,因为它支持整个对象的序列化,当要求网络 上传输复杂数据结构时这是非常重要的。然而,RMI-JRMP只能绑定到Java客户端:它是一种Java-to-Java的远程访问解决方案。

如 果你需要基于HTTP的远程访问而且还要求使用Java序列化,Spring的HTTP调用器是一个很好的选择。它和RMI调用器使用相同的基础设施,仅 仅使用HTTP作为传输方式。注意HTTP调用器不仅只能用在Java-to-Java的远程访问,而且在客户端和服务器端都必须使用Spring。 (Spring为非RMI接口提供的RMI调用器也要求客户端和服务器端都使用Spring)

在使用服务集群和需要JMS代理(JMS broker)来处理负载均衡及发现和自动-失败恢复服务时JMS是很有用的。缺省情况下,在使用JMS远程服务时使用Java序列化,但是JMS提供者也可以使用不同的机制例如XStream来让服务器用其他技术。

最后但不仅限于此,相对于RMI,EJB有一个优点是它支持标准的基于角色的认证和授权,以及远程事务传递。用RMI调用器或HTTP调用器来支持安全上下文的传递是可能的,虽然这不由核心Spring提供:Spring提供了合适的钩子来插入第三方或定制的解决方案。

原文:http://shouce.jb51.net/spring/remoting.html

原文地址:https://www.cnblogs.com/birkhoff/p/5476858.html