`
henxingliwang
  • 浏览: 11397 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

Spring与RMI结合技术

阅读更多

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

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

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

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

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

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 . 通过JmsInvokerServiceExporter 和JmsInvokerProxyFactoryBean 使用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>然后我们要使用RmiServiceExporter 来暴露我们的服务:

<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
    <!-- 不一定要与要输出的bean同名-->


    <property name="serviceName" value="AccountService"/>
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
    <!--  默认为1199-->


    <property name="registryPort" value="1199"/>
</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。

  Note
当然,这个例子没有演示灵活的安全设施。考虑更多有关安全的问题时,请参阅 http://acegisecurity.sourceforge.net Acegi Security System for Spring

。 

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
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.5. Web Services
Spring为标准Java web服务API提供了全面的支持:

使用 JAX-RPC暴露web服务

使用JAX-RPC访问web服务

使用JAX-WS暴露 web服务

使用JAX-WS访问web服务

  Note
为什么有2个标准的Java web服务APIs?

JAX-RPC 1.1 在J2EE 1.4 中是标准的web服务API。正像其名称所示,它关注于RPC绑定而且在最近几年越来越不流行。最终被Java EE 5中的JAX-WS 2.0所取代,JAX-WS 2.0不但在绑定方面更灵活,而且也是完全基于annotation的。JAX-WS 2.1也被包含在Java 6中(更详细的说是在Sun JDK 1.6.0_04和更高版本中,低版本的Sun JDK 1.6.0包含JAX-WS 2.0),它与JDK内置的HTTP服务器集成。

Spring 同时支持两个标准Java web服务API。选择谁主要看运行平台:在JDK 1.4 / J2EE 1.4上,唯一的选择是JAX-RPC。在Java EE 5 / Java 6上显然应该选JAX-WS。运行Java 5的J2EE 1.4环境上,你可以选择插入一个JAX-WS provider;请查看你的J2EE服务器文档。
 

除了在Spring Core中支持JAX-RPC and JAX-WS,Spring portfolio也提供了一种特性Spring Web Services ,一个为优先授权和文档驱动的web服务所提供的方案 - 非常建议用来创建高级并具有前瞻性的web服务。XFire 是最后但不是唯一的Spring 内置支持可以让你将Spring管理的bean暴露为web服务的方式。

17.5.1. 使用JAX-RPC暴露基于 servlet的web服务
Spring为JAX-RPC servlet的端点实现提供了一个方便的基类 - ServletEndpointSupport . 未来暴露我们的 AccountService 我们扩展Spring的ServletEndpointSupport 类并在这里实现了我们的业务逻辑,通常将调用交给业务层。

/**
 * JAX-RPC compliant RemoteAccountService implementation that simply delegates
 * to the AccountService implementation in the root web application context.
 *
 * This wrapper class is necessary because JAX-RPC requires working with dedicated
 * endpoint classes. If an existing service needs to be exported, a wrapper that
 * extends ServletEndpointSupport for simple application context access is
 * the simplest JAX-RPC compliant way.
 *
 * This is the class registered with the server-side JAX-RPC implementation.
 * In the case of Axis, this happens in "server-config.wsdd" respectively via
 * deployment calls. The web service engine manages the lifecycle of instances
 * of this class: A Spring application context can just be accessed here.
 */

import org.springframework.remoting.jaxrpc.ServletEndpointSupport;

public class AccountServiceEndpoint extends ServletEndpointSupport implements RemoteAccountService {
   
    private AccountService biz;
 
    protected void onInit() {
        this.biz = (AccountService) getWebApplicationContext().getBean("accountService");
    }

    public void insertAccount(Account acc) throws RemoteException {
        biz.insertAccount(acc);
    }
 
    public Account[] getAccounts(String name) throws RemoteException {
        return biz.getAccounts(name);
    }
}AccountServletEndpoint需要在Spring中同一个上下文的web应用里运行,以获得对Spring的访问能力。如果使用Axis,把AxisServlet 定义复制到你的'web.xml' 中,并且在'server-config.wsdd' 中设置端点(或使用发布工具)。参看JPetStore这个例子中OrderService 是如何用Axis发布成一个Web服务的。

17.5.2. 使用JAX-RPC访问web服务
Spring提供了两个工厂bean用来创建Web服务代理,LocalJaxRpcServiceFactoryBean 和 JaxRpcPortProxyFactoryBean 。前者只返回一个JAX-RPC服务类供我们使用。后者是一个全功能的版本,可以返回一个实现我们业务服务接口的代理。本例中,我们使用后者来为前面段落中暴露的AccountService 端点创建一个代理。你将看到Spring对Web服务提供了极好的支持,只需要很少的代码 - 大多数都是通过类似下面的Spring配置文件:

<bean id="accountWebService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
    <property name="serviceInterface" value="example.RemoteAccountService"/>
    <property name="wsdlDocumentUrl" value="http://localhost:8080/account/services/accountService?WSDL"/>
    <property name="namespaceUri" value="http://localhost:8080/account/services/accountService"/>
    <property name="serviceName" value="AccountService"/>
    <property name="portName" value="AccountPort"/>
</bean>serviceInterface 是我们客户端将使用的远程业务接口。 wsdlDocumentUrl 是WSDL文件的URL. Spring需要用它作为启动点来创建JAX-RPC服务。 namespaceUri 对应.wsdl文件中的targetNamespace。 serviceName 对应.wsdl文件中的服务名。 portName 对应.wsdl文件中的端口号。

现在我们可以很方便的访问web服务,因为我们有一个可以将它暴露为RemoteAccountService 接口的bean工厂。我们可以在Spring中这样使用:

<bean id="client" class="example.AccountClientImpl">
    ...
    <property name="service" ref="accountWebService"/>
</bean>从客户端代码上看,除了它抛出RemoteException ,我们可以把这个web服务当成一个普通的类进行访,。

public class AccountClientImpl {

    private RemoteAccountService service;
   
    public void setService(RemoteAccountService service) {
        this.service = service;
    }
   
    public void foo() {
        try {
            service.insertAccount(...);
        }
        catch (RemoteException ex) {
            // ouch


        }
    }
}
我们可以不检查受控异常RemoteException ,因为 Spring将它自动转换成相应的非受控异常RemoteException 。这也需要我们提供一个非RMI的接口。现在配置文件如下:

<bean id="accountWebService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
    <property name="serviceInterface" value="example.AccountService"/>
    <property name="portInterface" value="example.RemoteAccountService"/>
</bean>我们的serviceInterface 变成了非 RMI接口。我们的RMI接口现在使用portInterface 属性来定义。我们的客户端代码可以避免处理异常java.rmi.RemoteException :

public class AccountClientImpl {

    private AccountService service;
   
    public void setService(AccountService service) {
        this.service = service;
    }
   
    public void foo() {
        service.insertAccount(...);
    }
}请注意你也可以去掉"portInterface"部分并指定一个普通业务接口作为"serviceInterface"。这样JaxRpcPortProxyFactoryBean 将自动切换到JAX-RPC "动态调用接口", 不使用固定端口存根来进行动态调用。这样做的好处是你甚至不需要使用一个RMI相关的Java接口(比如在非Java的目标web服务中);你只需要一个匹配的业务接口。查看JaxRpcPortProxyFactoryBean 的javadoc来了解运行时实行的细节。

17.5.3. 注册 JAX-RPC Bean映射
T为了传递类似Account 等复杂对象,我们必须在客户端注册bean映射。

  Note
在服务器端通常在'server-config.wsdd' 中使用Axis进行bean映射注册。
 

我们将使用Axis在客户端注册bean映射。为此,我们需要通过程序注册这个bean映射:

public class AxisPortProxyFactoryBean extends JaxRpcPortProxyFactoryBean {

    protected void postProcessJaxRpcService(Service service) {
        TypeMappingRegistry registry = service.getTypeMappingRegistry();
        TypeMapping mapping = registry.createTypeMapping();
        registerBeanMapping(mapping, Account.class, "Account");
        registry.register("http://schemas.xmlsoap.org/soap/encoding/", mapping);
    }

    protected void registerBeanMapping(TypeMapping mapping, Class type, String name) {
        QName qName = new QName("http://localhost:8080/account/services/accountService", name);
        mapping.register(type, qName,
                new BeanSerializerFactory(type, qName),
                new BeanDeserializerFactory(type, qName));
    }
}17.5.4. 注册自己的JAX-RPC 处理器
本节中,我们将注册自己的javax.rpc.xml.handler.Handler 到Web服务代理,这样我们可以在SOAP消息被发送前执行定制的代码。Handler 是一个回调接口。jaxrpc.jar 中有个方便的基类javax.rpc.xml.handler.GenericHandler 供我们继承使用:

public class AccountHandler extends GenericHandler {

    public QName[] getHeaders() {
        return null;
    }

    public boolean handleRequest(MessageContext context) {
        SOAPMessageContext smc = (SOAPMessageContext) context;
        SOAPMessage msg = smc.getMessage();
        try {
            SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
            SOAPHeader header = envelope.getHeader();
            ...
        }
        catch (SOAPException ex) {
            throw new JAXRPCException(ex);
        }
        return true;
    }
}我们现在要做的就是把AccountHandler注册到JAX-RPC服务,这样它可以在消息被发送前调用 handleRequest(..) 。Spring目前对注册处理方法还不提供声明式支持,所以我们必须使用编程方式。但是Spring中这很容易实现,我们只需覆写专门为此设计的 postProcessJaxRpcService(..) 方法:

public class AccountHandlerJaxRpcPortProxyFactoryBean extends JaxRpcPortProxyFactoryBean {

    protected void postProcessJaxRpcService(Service service) {
        QName port = new QName(this.getNamespaceUri(), this.getPortName());
        List list = service.getHandlerRegistry().getHandlerChain(port);
        list.add(new HandlerInfo(AccountHandler.class, null, null));
        logger.info("Registered JAX-RPC AccountHandler on port " + port);
    }
}最后,我们要记得更改Spring配置文件来使用我们的工厂bean:

<bean id="accountWebService" class="example.AccountHandlerJaxRpcPortProxyFactoryBean">
    ...
</bean>17.5.5. 使用JAX-WS暴露基于servlet的web服务
Spring为JAX-WS servlet端点实现提供了一个方便的基类 - SpringBeanAutowiringSupport 。要暴露我们的AccountService 接口,我们可以扩展Spring的SpringBeanAutowiringSupport 类并实现我们的业务逻辑,通常把调用交给业务层。我们将简单的使用Spring 2.5的@Autowired 注解来声明依赖于Spring管理的bean。

/**
 * JAX-WS compliant AccountService implementation that simply delegates
 * to the AccountService implementation in the root web application context.
 *
 * This wrapper class is necessary because JAX-WS requires working with dedicated
 * endpoint classes. If an existing service needs to be exported, a wrapper that
 * extends SpringBeanAutowiringSupport for simple Spring bean autowiring (through
 * the @Autowired annotation) is the simplest JAX-WS compliant way.
 *
 * This is the class registered with the server-side JAX-WS implementation.
 * In the case of a Java EE 5 server, this would simply be defined as a servlet
 * in web.xml, with the server detecting that this is a JAX-WS endpoint and reacting
 * accordingly. The servlet name usually needs to match the specified WS service name.
 *
 * The web service engine manages the lifecycle of instances of this class.
 * Spring bean references will just be wired in here.
 */

import org.springframework.web.context.support.SpringBeanAutowiringSupport;

@WebService(serviceName="AccountService")
public class AccountServiceEndpoint extends SpringBeanAutowiringSupport {

    @Autowired
    private AccountService biz;

    @WebMethod
    public void insertAccount(Account acc) {
       biz.insertAccount(acc);
    }

    @WebMethod
    public Account[] getAccounts(String name) {
       return biz.getAccounts(name);
    }
}为了能够让Spring上下文使用Spring设施,我们的AccountServletEndpoint 类需要运行在同一个web应用中。在Java EE 5环境中这是默认的情况,它使用JAX-WS servlet端点安装标准契约。详情请参阅Java EE 5 web服务教程。

17.5.6. 使用JAX- WS暴露单独web服务
Sun JDK 1.6提供的内置JAX-WS provider 使用内置的HTTP服务器来暴露web服务。Spring的SimpleJaxWsServiceExporter 类检测所有在Spring应用上下文中配置的l@WebService 注解bean,然后通过默认的JAX-WS服务器(JDK 1.6 HTTP服务器)来暴露它们。

在这种场景下,端点实例将被作为 Spring bean来定义和管理。它们将使用JAX-WS来注册,但其生命周期将一直跟随Spring应用上下文。这意味着Spring的显示依赖注入可用于端点实例。当然通过@Autowired 来进行注解驱动的注入也可以正常工作。

<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter">
    <property name="baseAddress" value="http://localhost:9999/"/>
</bean>

<bean id="accountServiceEndpoint" class="example.AccountServiceEndpoint">
    ...
</bean>

...AccountServiceEndpoint 类可能源自Spring 的 SpringBeanAutowiringSupport 类,也可能不是。因为这里的端点是由Spring完全管理的bean。这意味着端点实现可能像下面这样没有任何父类定义 - 而且Spring的@Autowired 配置注解仍然能够使用:

@WebService(serviceName="AccountService")
public class AccountServiceEndpoint {

    @Autowired
    private AccountService biz;

    @WebMethod
    public void insertAccount(Account acc) {
       biz.insertAccount(acc);
    }

    @WebMethod
    public Account[] getAccounts(String name) {
       return biz.getAccounts(name);
    }
}17.5.7. 使用Spring支持的JAX-WS RI来暴露服务
Sun的JAX-WS RI被作为GlassFish项目的一部分来开发,它使用了Spring支持来作为JAX-WS Commons项目的一部分。这允许把JAX-WS端点作为Spring管理的bean来定义。这与前面章节讨论的单独模式类似 - 但这次是在Servlet环境中。注意这在Java EE 5环境中是不可迁移的,建议在没有EE的web应用环境如Tomcat中嵌入JAX-WS RI。

与标准的暴露基于servlet的端点方式不同之处在于端点实例的生命周期将被Spring管理。这里在web.xml 将只有一个JAX-WS servlet定义。在标准的Java EE 5风格中(如上所示),你将对每个服务端点定义一个servlet,每个服务端点都代理到Spring bean (通过使用@Autowired ,如上所示)。

关于安装和使用详情请查阅https://jax-ws-commons.dev.java.net/spring/

17.5.8. 使用JAX-WS访问web服务
类似JAX-RPC支持,Spring提供了 2个工厂bean来创建JAX-WS web服务代理,它们是LocalJaxWsServiceFactoryBean 和JaxWsPortProxyFactoryBean 。前一个只能返回一个JAX-WS服务对象来让我们使用。后面的是可以返回我们业务服务接口的代理实现的完整版本。这个例子中我们使用后者来为AccountService 端点再创建一个代理:

<bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
    <property name="serviceInterface" value="example.AccountService"/>
    <property name="wsdlDocumentUrl" value="http://localhost:8080/account/services/accountService?WSDL"/>
    <property name="namespaceUri" value="http://localhost:8080/account/services/accountService"/>
    <property name="serviceName" value="AccountService"/>
    <property name="portName" value="AccountPort"/>
</bean>serviceInterface 是我们客户端将使用的远程业务接口。 wsdlDocumentUrl 是WSDL文件的URL. Spring需要用它作为启动点来创建JAX-RPC服务。 namespaceUri 对应.wsdl文件中的targetNamespace。 serviceName 对应.wsdl文件中的服务名。 portName 对应.wsdl文件中的端口号。

现在我们可以很方便的访问web服务,因为我们有一个可以将它暴露为AccountService 接口的bean工厂。我们可以在Spring中这样使用:

<bean id="client" class="example.AccountClientImpl">
    ...
    <property name="service" ref="accountWebService"/>
</bean>从客户端代码上我们可以把这个web服务当成一个普通的类进行访问:

public class AccountClientImpl {

    private AccountService service;

    public void setService(AccountService service) {
        this.service = service;
    }

    public void foo() {
        service.insertAccount(...);
    }
}注意: 上面被稍微简化了,因为JAX-WS需要端点接口及实现类来使用@WebService , @SOAPBinding 等注解。 这意味着你不能简单的使用普通的Java接口和实现来作为JAX-WS端点,你需要首先对它们进行相应的注解。这些需求详情请查阅JAX-WS文档。

17.5.9. 使用XFire来暴露Web服务
XFire是一个Codehaus提供的轻量级SOAP库。暴露XFire是通过XFire自带的context,这个context将和RemoteExporter风格的bean相结合,后者需要被加入到在你的WebApplicationContext 中。对于所有让你来暴露服务的方法,你需要创建一个DispatcherServlet 类并有相应的WebApplicationContext 来封装你将要暴露的服务:

<servlet>
    <servlet-name>xfire</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>你还必须链接XFire配置。这是通过增加一个context文件到由ContextLoaderListener (或者ContextLoaderServlet )加载的 contextConfigLocations 参数中。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:org/codehaus/xfire/spring/xfire.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>在你加入一个Servlet映射后(映射/* 到上面定义的XFire Servlet),你只需要增加一个额外的bean来使用XFire暴露服务。例如,在 'xfire-servlet.xml' 中增加如下配置:

<beans>

    <bean name="/Echo" class="org.codehaus.xfire.spring.remoting.XFireExporter">
        <property name="serviceInterface" value="org.codehaus.xfire.spring.Echo"/>
        <property name="serviceBean">
            <bean class="org.codehaus.xfire.spring.EchoImpl"/>
        </property>
        <!-- the XFire bean is defined in the xfire.xml
 file -->


        <property name="xfire" ref="xfire"/>
  </bean>

</beans>XFire处理了其他的事情。它检查你的服务接口并产生一个WSDL文件。这里的部分文档来自XFire 网站,要了解更多有关XFire Spring的集成请访问 docs.codehaus.org/display/XFIRE/Spring 。

17.6. JMS
使用JMS来作为底层的通信协议透明暴露服务也是可能的。Spring框架中对JMS的远程支持也很基础 - 它在同一线程 和同一个非事务 Session 上发送和接收,这些吞吐量将非常依赖于实现。

The following interface is used on both the server and the client side.

下面的接口可同时用在服务端和客户端。

package com.foo;

public interface CheckingAccountService {

    public void cancelAccount(Long accountId);
}对于上面接口的使用在服务的端简单实现如下。

package com.foo;

public class SimpleCheckingAccountService implements CheckingAccountService {

    public void cancelAccount(Long accountId) {
        System.out.println("Cancelling account [" + accountId + "]");
    }
}这个包含JMS设施的bean的配置文件可同时用在客户端和服务端。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      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-2.5.xsd">

    <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://ep-t43:61616"/>
    </bean>

    <bean id="queue" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg value="mmm"/>
    </bean>

</beans>17.6.1. 服务端配置
在服务端你只需要使用JmsInvokerServiceExporter 来暴露服务对象。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      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-2.5.xsd">

    <bean id="checkingAccountService"
          class="org.springframework.jms.remoting.JmsInvokerServiceExporter">
        <property name="serviceInterface" value="com.foo.CheckingAccountService"/>
        <property name="service">
            <bean class="com.foo.SimpleCheckingAccountService"/>
        </property>
   </bean>

   <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
       <property name="connectionFactory" ref="connectionFactory"/>
       <property name="destination" ref="queue"/>
       <property name="concurrentConsumers" value="3"/>
       <property name="messageListener" ref="checkingAccountService"/>
   </bean>

</beans>package com.foo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Server {

    public static void main(String[] args) throws Exception {
        new ClassPathXmlApplicationContext(new String[]{"com/foo/server.xml", "com/foo/jms.xml"});
    }
}17.6.2. 客户端配置
客户端仅仅需要创建一个客户端代理来实现上面的接口(CheckingAccountService )。根据后面的bean定义创建的结果对象可以被注入到其它客户端对象中,而这个代理会负责通过JMS将调用转发到服务端。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      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-2.5.xsd">

    <bean id="checkingAccountService"
          class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean">
        <property name="serviceInterface" value="com.foo.CheckingAccountService"/>
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="queue" ref="queue"/>
    </bean>

</beans>package com.foo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

    public static void main(String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                new String[] {"com/foo/client.xml", "com/foo/jms.xml"});
        CheckingAccountService service = (CheckingAccountService) ctx.getBean("checkingAccountService");
        service.cancelAccount(new Long(10));
    }
}你可能也希望研究Lingo 项目提供的支持,它(引用到主页) “... 是一个基于轻量级POJO的远程核消息代码库,它使用并扩展了Spring框架的远程代码库以支持JMS。 ”

17.7. 对远程接口不提供自动探测实现
对远程接口不实现自动探测的主要原因是防止产生太多的远程调用。目标对象有可能实现的是类似InitializingBean 或者DisposableBean 这样的内部回调接口,而这些是不希望暴露给调用者的。

提供一个所有接口都被目标实现的代理通常和本地情况无关。但是当暴露一个远程服务时,你应该只暴露特定的用于远程使用的服务接口。除了内部回调接口,目标有可能实现了多个业务接口,而往往只有一个是用于远程调用的。出于这些原因,我们 要求 指定这样的服务接口。

这是在配置方便性和意外暴露内部方法的危险性之间作的平衡。总是指明服务接口并不要花太大代价,并可以让你控制需暴露方法从而更加安全。

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提供了合适的钩子来插入第三方或定制的解决方案。

分享到:
评论

相关推荐

    spring和RMI分布式整合源码

    具体到这个压缩包“springRMI”,我们可以推测它可能包含了以下几个部分: 1. **配置文件**:如`applicationContext.xml`,其中定义了Spring容器中的Bean,可能包括RMI服务的接口和实现。 2. **远程接口**:`.java`...

    spring+rmi非本地实现

    当我们结合Spring与RMI时,可以构建分布式系统,使得服务可以在网络中的不同节点上运行并交互。 在这个“spring+rmi非本地实现”的项目中,我们有两个主要部分:客户端(client)和服务端(rmiserver)。让我们深入...

    rmi与spring整合实例

    当我们谈论“RMI与Spring整合实例”时,我们通常是指将RMI技术与Spring框架结合使用,以便构建可扩展的、分布式的Java应用程序。下面将详细解释这两个概念以及如何将它们整合在一起。 1. RMI基础: - RMI的核心...

    Spring RMI小例子

    Spring RMI(Remote Method Invocation)是Java平台上的远程方法调用技术,结合Spring框架,能够帮助开发者轻松地创建分布式应用程序。在这个"Spring RMI小例子"中,我们将深入理解Spring如何简化RMI的使用,以及...

    如何在Spring框架中使用RMI技术.zip

    RMI结合了Spring的强大功能,可以构建分布式系统,使得组件之间的通信更加便捷。本压缩包提供了两个Java工程示例,帮助开发者了解如何在Spring环境中集成并使用RMI。 首先,让我们理解RMI的基本概念。RMI是Java提供...

    spring rmi 集成

    本篇将深入探讨Spring如何与RMI结合,以及客户端和服务器端的实现细节。 ### 一、Spring RMI 服务器端集成 1. **定义远程接口**:首先,我们需要创建一个Java接口,该接口将定义远程服务的方法。例如,我们可以...

    java Spring+RMI入门程序源代码

    Java Spring 框架与 Remote Method Invocation (RMI) 的结合使用是构建分布式应用程序的一种常见方式。本项目提供了一个入门级的源代码示例,帮助开发者理解如何在 Spring 环境下集成 RMI 技术。以下是关于这两个...

    spring+rmi实现非本地部署

    将Spring与RMI结合,可以实现非本地部署的服务,使得客户端能够跨网络调用服务端的方法。以下是对这个主题的详细阐述: 首先,我们需要理解Spring框架的核心概念。Spring通过依赖注入(Dependency Injection,DI)...

    Spring(RMI)实例

    将Spring与RMI结合使用,可以实现分布式服务的灵活构建。 首先,我们需要理解RMI的基本原理。RMI允许一个Java对象调用位于不同JVM中的另一个对象的方法。这涉及到三个主要步骤:导出远程对象、注册远程对象和调用...

    spring RMI 实用分享

    Spring RMI(Remote Method Invocation)是Java平台上的远程方法调用技术,允许程序在不同的Java虚拟机(JVM)之间进行通信,实现分布式系统。在本文中,我们将深入探讨Spring框架如何集成RMI,以及如何创建和使用...

    spring RMI简单例子

    Spring RMI(Remote Method Invocation)是Java平台上的远程方法调用技术,结合Spring框架的特性,使得在分布式系统中调用远程服务变得更加便捷。在这个简单的例子中,我们将深入理解Spring RMI的工作原理以及如何...

    spring整合RMI小例子

    在本篇中,我们将深入探讨如何将Spring框架与Remote Method Invocation (RMI)技术整合,以便在分布式环境中实现远程服务调用。RMI是Java提供的一种机制,它允许对象在不同的JVM之间进行通信,而Spring框架则为构建可...

    java spring rmi

    4. **RMI与Spring AOP的结合**: Spring的面向切面编程(AOP)可以用于RMI服务,如添加日志、性能监控、安全性检查等横切关注点。 5. **RMI与Spring Boot的整合**: 在Spring Boot项目中,通过配置`server.port=0...

    spring整合RMI

    Spring整合RMI(Remote Method Invocation)是将Spring框架与Java RMI技术相结合,实现远程服务调用的一种方式。RMI允许在分布式环境中调用对象的方法,就像调用本地对象一样,极大地增强了应用的可扩展性和灵活性。...

    spring rmi 简单应用

    Spring RMI(Remote Method Invocation)简单应用主要涉及的是在Java中使用Spring框架来实现远程方法调用的技术。RMI是Java提供的一种分布式计算能力,它允许一个Java对象调用网络另一端的Java对象的方法,实现了...

    rmi实例(Spring整合)

    Spring框架作为Java企业级应用的基石,它提供了与RMI集成的能力,使得开发者可以方便地利用RMI来构建高可扩展性的系统。本文将深入探讨Spring整合RMI的相关知识点。 首先,理解RMI的基本概念是关键。RMI允许一个...

    Spring RMI

    **Spring RMI(远程方法调用)**是Java平台上的一个技术,允许对象在不同的Java虚拟机(JVM)之间进行交互。它结合了Spring框架的强大功能,为分布式应用程序提供了便利。Spring RMI通过简化RMI的配置和管理,使得...

    spring rmi应用

    标题“spring rmi应用”指的是在Spring框架中使用Remote Method Invocation (RMI)技术进行分布式服务调用的应用场景。RMI是Java平台提供的一种机制,它允许一个Java对象调用远程计算机上的另一个Java对象的方法,...

Global site tag (gtag.js) - Google Analytics