欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于Spring实现的Rmi, HttpInvoker, Hessian, Web services

程序员文章站 2022-07-11 17:55:28
...

基于Spring实现的Rmi, HttpInvoker, Hessian, Web services

因为要写Ronda的关系,所以就想了解下应该怎么实现Rmi协议。普通的Rmi必须要继承Remote
方法,但是在框架里肯定不会为每个类都继承Remote。

那么就来看看官网上怎么说的,然后再分析一下为什么这么实现就可以。

Spring使用不同的技术支持多种远程调用。目前支持四种:

  • Rmoete Method Invocation. 通过使用RmiProxyFactoryBeanRmiServiceExporter支持了传统的RMI和透明的RMI远程调用。这里说传统的是继承了Remote类和抛出异常java.rmi.RemoteException,后一种说的是只要有接口就可以。

  • Spring Http invoker. 只要类实现了接口,那么Spring就支持通过Http进行序列化传输。使用到的类是Http
    InvokerProxyFactoryBean
    HttpInvokerServiceExporter

  • Hessian. 使用HessianProxyFactoryBeanHessianServiceExporter你可以使用Hessian协议进行传输。

  • Burlap. Burlap是对于Hessian的另外一个格式选择,即XML。使用BurlapProxyFactoryBeanBurlapServiceExporter

  • JAX RPC. Spring通过JAX-RPC提供对web service的支持

  • JMS. 使用JmsInvokerServiceExporterJmsInvokerProxyFactoryBean进行实现

从上面的简介我们可以看到,Spring主要使用ServiceExporter进行服务的导出,使用ProxyFactoryBean进行生成代理的服务单对象,然后实现远程调用。

RMI

核心就是讲Service导出来,然后在客户端生成一个Service的代理对象

Server端:

<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
    <!-- does not necessarily have to be the same name as the bean to be exported -->
    <property name="serviceName" value="AccountService"/>
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
    <!-- defaults to 1099 -->
    <property name="registryPort" value="1199"/>
</bean>

Client端:

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

Hessian

Hessian协议使用通过HTTP进行通讯。所以使用DispatcherServlet进行暴露服务.

<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>
暴露服务
<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>

客户端

<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>

HTTP

HTTP其实是使用自己的协议进行HTTP交互,而Hessian、Burlap使用自己的序列化协议。

<bean name="/AccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
  <property name="service" ref="accountService"/>
  <property name="serviceInterface" value="example.AccountService"/>
</bean>
<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>

Web services

使用JAX-RPC进行暴露

Spring提供一个ServletEndPointSupport类来方便进行暴露web service服务,业务层的类需要继承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);
    }
}

访问web service

spring有两个工厂bean用来创建web service的代理: LocalJaxRpcServiceFactoryBeanJaxRpcPortProxyFactoryBean。前面的一个类返回的JAX-RPC服务,后一个返回全面的实现业务接口的类。

<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>

现在可以正常使用service,不过要处理RemoteException. 可以通过配置一个non-RMI的接口来去除。

客户端注册Bean Mappings

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));
   }
}

注册自定义的Handler

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 e) {
            throw new JAXRPCException(e);
        }

        return true;
    }
}
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 Handler [" + AccountHandler.class.getName() + "] on port " + port);
    }
}
<bean id="accountWebService" class="example.AccountHandlerJaxRpcPortProxyFactoryBean">
    <!-- ... -->
</bean>
Exposing web services using XFire

添加servlet

<servlet>
  <servlet-name>xfire</servlet-name>
  <servlet-class>
    org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
</servlet>
<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 mapping

<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>

JMS

通过JMS进行交互。 Spring支持JMS是很基本的,在同一个线程中进行通讯,不支持事务。

Server和Client都需要的接口

package com.foo;

public interface CheckingAccountService {

   void cancelAccount(Long accountId);
}

Server端实现:

package com.foo;

public class SimpleCheckingAccountService implements CheckingAccountService {

   public void cancelAccount(Long accountId) {
      System.out.println("Cancelling account [" + accountId + "]");
   }
}

Server和Client都有的配置文件:

<?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.0.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>

Server端的配置:

<?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.0.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>

Client端的配置:

<?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.0.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>

简单说下RMI其实还是走Java的那一套,就是判断你的service有没有继承Remote接口,如果没有就给你生成一个继承的接口暴露出去。