基于Spring实现的Rmi, HttpInvoker, Hessian, Web services
基于Spring实现的Rmi, HttpInvoker, Hessian, Web services
因为要写Ronda的关系,所以就想了解下应该怎么实现Rmi协议。普通的Rmi必须要继承Remote
方法,但是在框架里肯定不会为每个类都继承Remote。
那么就来看看官网上怎么说的,然后再分析一下为什么这么实现就可以。
Spring使用不同的技术支持多种远程调用。目前支持四种:
Rmoete Method Invocation. 通过使用
RmiProxyFactoryBean
和RmiServiceExporter
支持了传统的RMI和透明的RMI远程调用。这里说传统的是继承了Remote
类和抛出异常java.rmi.RemoteException
,后一种说的是只要有接口就可以。Spring Http invoker. 只要类实现了接口,那么Spring就支持通过Http进行序列化传输。使用到的类是
Http
和
InvokerProxyFactoryBeanHttpInvokerServiceExporter
Hessian. 使用
HessianProxyFactoryBean
和HessianServiceExporter
你可以使用Hessian协议进行传输。Burlap. Burlap是对于Hessian的另外一个格式选择,即
XML
。使用BurlapProxyFactoryBean
和BurlapServiceExporter
JAX RPC. Spring通过JAX-RPC提供对web service的支持
JMS. 使用
JmsInvokerServiceExporter
和JmsInvokerProxyFactoryBean
进行实现
从上面的简介我们可以看到,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的代理: LocalJaxRpcServiceFactoryBean
和JaxRpcPortProxyFactoryBean
。前面的一个类返回的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接口,如果没有就给你生成一个继承的接口暴露出去。
上一篇: 清炒木耳做法非常简单,家常菜必备
下一篇: Impala的简单使用