http://blog.csdn.net/java2000_wl/article/details/7407073
http://zcjl.iteye.com/blog/36336
一: 服务端 暴露服务
- package com.xx.service;
- /**
- * 定义远程服务接口
- * 1.可以不继承java.rmi.Remote接口
- * 2.方法可以不抛出java.rmi.RemoteException异常
- *
- */
- public interface ISayHelloService {
- public String doSayHello(String name);
- }
package com.xx.service;
/**
* 定义远程服务接口
* 1.可以不继承java.rmi.Remote接口
* 2.方法可以不抛出java.rmi.RemoteException异常
*
*/
public interface ISayHelloService {
public String doSayHello(String name);
}
- package com.xx.service.impl;
- import com.xx.service.ISayHelloService;
- /**
- * 远程接口实现
- */
- public class ChinaSayHelloServiceImpl implements ISayHelloService {
- public String doSayHello(String name) {
- return "hello " + name;
- }
- }
package com.xx.service.impl;
import com.xx.service.ISayHelloService;
/**
* 远程接口实现
*/
public class ChinaSayHelloServiceImpl implements ISayHelloService {
public String doSayHello(String name) {
return "hello " + name;
}
}
- package com.xx.service;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- /**
- * 服务端
- * 暴露远程服务
- */
- public class ServerMain {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext-server.xml"}, true);
- System.out.println("==============服务启动成功 ==============");
- }
- }
package com.xx.service;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 服务端
* 暴露远程服务
*/
public class ServerMain {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext-server.xml"}, true);
System.out.println("==============服务启动成功 ==============");
}
}
- spring配置文件 applicationContext-server.xml
- <?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:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
- <bean id="chinaSayHelloService" class="com.xx.service.impl.ChinaSayHelloServiceImpl" />
- <bean id="chinaSayHelloServiceRmi" class="org.springframework.remoting.rmi.RmiServiceExporter" >
- <property name="serviceName" value="chinaSayHelloService" />
- <property name="service" ref="chinaSayHelloService"/>
- <property name="serviceInterface" value="com.xx.service.ISayHelloService"/>
- <property name="registryPort" value="9999"/>
- </bean>
- </beans>
spring配置文件 applicationContext-server.xml
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="chinaSayHelloService" class="com.xx.service.impl.ChinaSayHelloServiceImpl" />
<bean id="chinaSayHelloServiceRmi" class="org.springframework.remoting.rmi.RmiServiceExporter" >
<property name="serviceName" value="chinaSayHelloService" />
<property name="service" ref="chinaSayHelloService"/>
<property name="serviceInterface" value="com.xx.service.ISayHelloService"/>
<property name="registryPort" value="9999"/>
</bean>
</beans>
二:客户端 远程方法调用
- package com.xx.service;
- import java.net.UnknownHostException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- /**
- * 客户端
- */
- public class ClientMain {
- public static void main(String[] args) throws UnknownHostException {
- ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext-client.xml");
- ISayHelloService object = applicationContext.getBean("chinaSayHelloServiceRmi", ISayHelloService.class);
- System.out.println(object.doSayHello("张三"));
- }
- }
package com.xx.service;
import java.net.UnknownHostException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 客户端
*/
public class ClientMain {
public static void main(String[] args) throws UnknownHostException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext-client.xml");
ISayHelloService object = applicationContext.getBean("chinaSayHelloServiceRmi", ISayHelloService.class);
System.out.println(object.doSayHello("张三"));
}
}
- spring配置文件 applicationContext-client.xml
- <?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:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
- <bean id="chinaSayHelloServiceRmi" class="org.springframework.remoting.rmi.RmiProxyFactoryBean" >
- <property name="serviceUrl" value="rmi://192.168.3.104:9999/chinaSayHelloService" />
- <property name="serviceInterface" value="com.xx.service.ISayHelloService"/>
- </bean>
- </beans>
看了《J2EE without EJB》的remote章节,忍不住写点代码试试,看看Spring的实现到底多巧妙。
1.先测试RMI服务的发布,测试代码如下:
Spring的context配置文件如下:
再写一个测试程序,如下:
运行TestSpringRmi,报错如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serviceExporter' defined in class path resource [spring-remote.xml]: Initialization of bean failed; nested exception is java.rmi.StubNotFoundException: Stub class not found: test.spring.remote.rmi.MyServiceImpl_Stub; nested exception is:
java.lang.ClassNotFoundException: test.spring.remote.rmi.MyServiceImpl_Stub
java.rmi.StubNotFoundException: Stub class not found: test.spring.remote.rmi.MyServiceImpl_Stub; nested exception is:
java.lang.ClassNotFoundException: test.spring.remote.rmi.MyServiceImpl_Stub
咦?Spring不是号称不需要自己生成stub么?怎么会出现“Stub class not found”呢?
祭出google,从spring官方论坛搜到一个帖子:http://forum.springframework.org/showthread.php?t=19185,里面有条回复是:
I found the answer:
The class org.springframework.remoting.rmi.RmiInvocationWrap per_Stub is present in spring.jar, but not in the source tree as a Java file. Since I was running against the compiled Spring Java files, rather than the jar, it did not find it.
晕倒,Spring不会这么弱智吧,难道我以后使用的时候还得把jar包解压到class目录下?
不甘心,再搜,找到这个帖子:http://forum.springframework.org/showthread.php?t=12685
在Juergen Hoeller的回复提示下,我再去看了jpetstore的配置文件,原来用以发布rmi的接口应该是pojo形式的MyBusinessInterface,而不是那个继承自Remote的MyService,修改自己的context配置文件:
再运行TestSpringRmi,成功了。console打印:
03-02 14:51:56 INFO [RmiServiceExporter.java:236] Binding RMI service 'myService' to registry at port '1199'
2.再继续测试客户端调用,先修改context配置如下:
再修改测试代码,添加客户端调用:
运行TestSpringRmi,报错如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'rmiService' defined in class path resource [spring-remote.xml]: Initialization of bean failed; nested exception is java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
java.net.ConnectException: Connection refused: connect
java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
java.net.ConnectException: Connection refused: connect
仔细检查,原来自己把生成rmi客户端的bean映射放到了发布rmi服务的serviceExporter之前了,调换一下顺序:
xml 代码
运行TestSpringRmi,结果如下:
03-02 15:01:24 INFO [RmiServiceExporter.java:236] Binding RMI service 'myService' to registry at port '1199'
03-02 15:01:24 INFO [RmiClientInterceptor.java:128] RMI stub [rmi://localhost:1199/myService] is an RMI invoker
MyServiceImpl.doSomething()
经过一番浅尝辄止,初步得出几个结论:
1.Spring对RMI的支持果然很不错,在Cglib等工具的支持下,使用RMI终于可以同Naming、rmic和stub告别了。
2.用以发布RMI的接口不能从java.rmi.Remote继承而来,否则就会出现“Stub class not found”的错误,原因有待深究。
3.Spring的BeanFactory创建bean实例是有序的,向RMI、JNDI、WebService等注册服务性质的应用,同一应用中的客户端要根据其依赖性调整配置顺序。
JNDI的使用方式
服务端注册
<bean class="org.springframework.remoting.rmi.JndiRmiServiceExporter">
<property name="service" ref="creditService" />
<property name="serviceInterface" value="com.common.CreditRemoteService" />
<property name="jndiName" value="CreditService" />
</bean>
客户端调用xml配置
<bean id="creditService" class="org.springframework.remoting.rmi.JndiRmiProxyFactoryBean"
scope="prototype" lazy-init="true">
<property name="serviceInterface" value="com.common.CreditRemoteService" />
<property name="lookupStubOnStartup" value="false"/>
<property name="refreshStubOnConnectFailure" value="true"/>
<property name="jndiName" value="CreditService" />
<property name="jndiEnvironment">
<props>
<prop key="java.naming.provider.url">${com.jndi.creditServiceUrl}</prop>
<prop key ="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
</props>
</property>
</bean>
Spring RMI 源码浅析-RmiServiceExporter 导出服务
Java Rmi
1.接口必须继承java.rmi.Remote接口
2.方法必须抛出java.rmi.RemoteException异常
Spring Rmi
1.可以不实现java.rmi.Remote接口
2.方法可以不抛出异常
问题:在Spring 内部是怎么实现的?
在Spring中 是通过org.springframework.remoting.rmi.RmiServiceExporte 在服务端导出一个服务
RmiServiceExporter定义
- public class RmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean {
- }
public class RmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean {
}
实现了 InitializingBean接口 Spring会在bean的实例化阶段 调用 InitializingBean 的afterPropertiesSet 方法
bean的实例化 会在什么时候触发 取决于配置 例如lazy-init
RmiServiceExporter afterPropertiesSet 方法实现
- public void afterPropertiesSet() throws RemoteException {
- prepare();
- }
public void afterPropertiesSet() throws RemoteException {
prepare();
}
prepare方法
- public void prepare() throws RemoteException {
- //检查配置中的 service对象 如果为null 抛出异常
- checkService();
- //检查服务名称
- if (this.serviceName == null) {
- throw new IllegalArgumentException("Property 'serviceName' is required");
- }
- // Check socket factories for exported object.
- // 略....
- // Determine RMI registry to use.
- if (this.registry == null) {
- //获得注册器
- this.registry = getRegistry(this.registryHost, this.registryPort,
- this.registryClientSocketFactory, this.registryServerSocketFactory);
- }
- // 获得要导出的服务对象
- // getObjectToExport方法 在父类RmiBasedExporter中定义
- // 1.如果实现了jdk Remote接口 那就是一个标准的RMI 类型转换后 直接返回
- // 2.没有实现jdk Remote接口 返回spring包装对象RmiInvocationWrapper调用器 RmiInvocationWrapper实现了jdk Remote接口
- // RmiInvocationWrapper 中有两个属性 1.wrappedObject 自己定义的远程对象[service属性]
- // 2.RmiBasedExporter 也就是当前导出对象 this 在客户端调用的时候 会触发invoke方法
- this.exportedObject = getObjectToExport();
- // 导出服务对象 jdk UnicastRemoteObject实现
- if (this.clientSocketFactory != null) {
- UnicastRemoteObject.exportObject(
- this.exportedObject, this.servicePort, this.clientSocketFactory, this.serverSocketFactory);
- }
- else {
- UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort);
- }
- // Bind RMI object to registry.
- // 把RMI远程服务对象和注册器绑定 jdk实现
- try {
- if (this.replaceExistingBinding) {
- //替换指定serviceName的远程对象
- this.registry.rebind(this.serviceName, this.exportedObject);
- }
- else {
- //绑定对象
- this.registry.bind(this.serviceName, this.exportedObject);
- }
- }
- catch (AlreadyBoundException ex) {
- // Already an RMI object bound for the specified service name...
- unexportObjectSilently();
- throw new IllegalStateException(
- "Already an RMI object bound for name '" + this.serviceName + "': " + ex.toString());
- }
- catch (RemoteException ex) {
- // Registry binding failed: let's unexport the RMI object as well.
- unexportObjectSilently();
- throw ex;
- }
- }
public void prepare() throws RemoteException {
//检查配置中的 service对象 如果为null 抛出异常
checkService();
//检查服务名称
if (this.serviceName == null) {
throw new IllegalArgumentException("Property 'serviceName' is required");
}
// Check socket factories for exported object.
// 略....
// Determine RMI registry to use.
if (this.registry == null) {
//获得注册器
this.registry = getRegistry(this.registryHost, this.registryPort,
this.registryClientSocketFactory, this.registryServerSocketFactory);
}
// 获得要导出的服务对象
// getObjectToExport方法 在父类RmiBasedExporter中定义
// 1.如果实现了jdk Remote接口 那就是一个标准的RMI 类型转换后 直接返回
// 2.没有实现jdk Remote接口 返回spring包装对象RmiInvocationWrapper调用器 RmiInvocationWrapper实现了jdk Remote接口
// RmiInvocationWrapper 中有两个属性 1.wrappedObject 自己定义的远程对象[service属性]
// 2.RmiBasedExporter 也就是当前导出对象 this 在客户端调用的时候 会触发invoke方法
this.exportedObject = getObjectToExport();
// 导出服务对象 jdk UnicastRemoteObject实现
if (this.clientSocketFactory != null) {
UnicastRemoteObject.exportObject(
this.exportedObject, this.servicePort, this.clientSocketFactory, this.serverSocketFactory);
}
else {
UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort);
}
// Bind RMI object to registry.
// 把RMI远程服务对象和注册器绑定 jdk实现
try {
if (this.replaceExistingBinding) {
//替换指定serviceName的远程对象
this.registry.rebind(this.serviceName, this.exportedObject);
}
else {
//绑定对象
this.registry.bind(this.serviceName, this.exportedObject);
}
}
catch (AlreadyBoundException ex) {
// Already an RMI object bound for the specified service name...
unexportObjectSilently();
throw new IllegalStateException(
"Already an RMI object bound for name '" + this.serviceName + "': " + ex.toString());
}
catch (RemoteException ex) {
// Registry binding failed: let's unexport the RMI object as well.
unexportObjectSilently();
throw ex;
}
}
checkService方法
- protected void checkService() throws IllegalArgumentException {
- if (getService() == null) {
- throw new IllegalArgumentException("Property 'service' is required");
- }
- }
protected void checkService() throws IllegalArgumentException {
if (getService() == null) {
throw new IllegalArgumentException("Property 'service' is required");
}
}
- protected Remote getObjectToExport() {
- //自定义的远程对象 实现了 jdk Remote
- if (getService() instanceof Remote &&
- (getServiceInterface() == null || Remote.class.isAssignableFrom(getServiceInterface()))) { return (Remote) getService();
- }
- else {
- // 没有实现 Remote接口 spring在此处包装了我们自定义的远程服务对象
- // getProxyForService方法 返回一个代理对象
- return new RmiInvocationWrapper(getProxyForService(), this);
- }
- }
protected Remote getObjectToExport() {
//自定义的远程对象 实现了 jdk Remote
if (getService() instanceof Remote &&
(getServiceInterface() == null || Remote.class.isAssignableFrom(getServiceInterface()))) { return (Remote) getService();
}
else {
// 没有实现 Remote接口 spring在此处包装了我们自定义的远程服务对象
// getProxyForService方法 返回一个代理对象
return new RmiInvocationWrapper(getProxyForService(), this);
}
}
RmiInvocationWrapper定义 实现了RmiInvocationHandler接口 而RmiInvocationHandler接口继承了Remote 接口
- class RmiInvocationWrapper implements RmiInvocationHandler {
- private final Object wrappedObject;
- private final RmiBasedExporter rmiExporter;
- public RmiInvocationWrapper(Object wrappedObject, RmiBasedExporter rmiExporter) {
- Assert.notNull(wrappedObject, "Object to wrap is required");
- Assert.notNull(rmiExporter, "RMI exporter is required");
- this.wrappedObject = wrappedObject;
- this.rmiExporter = rmiExporter;
- }
- public String getTargetInterfaceName() {
- Class ifc = this.rmiExporter.getServiceInterface();
- return (ifc != null ? ifc.getName() : null);
- }
- /***
- * 非标准的RMI调用远程方法的中转站
- * invocation封装了方法名 参数名
- */
- public Object invoke(RemoteInvocation invocation)
- throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
- //会在客户端调用远程方法时触发,chuwrappedObject是 我们定义的远程对象
- return this.rmiExporter.invoke(invocation, this.wrappedObject);
- }
- }
class RmiInvocationWrapper implements RmiInvocationHandler {
private final Object wrappedObject;
private final RmiBasedExporter rmiExporter;
public RmiInvocationWrapper(Object wrappedObject, RmiBasedExporter rmiExporter) {
Assert.notNull(wrappedObject, "Object to wrap is required");
Assert.notNull(rmiExporter, "RMI exporter is required");
this.wrappedObject = wrappedObject;
this.rmiExporter = rmiExporter;
}
public String getTargetInterfaceName() {
Class ifc = this.rmiExporter.getServiceInterface();
return (ifc != null ? ifc.getName() : null);
}
/***
* 非标准的RMI调用远程方法的中转站
* invocation封装了方法名 参数名
*/
public Object invoke(RemoteInvocation invocation)
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
//会在客户端调用远程方法时触发,chuwrappedObject是 我们定义的远程对象
return this.rmiExporter.invoke(invocation, this.wrappedObject);
}
}
RmiInvocationHandler接口继承了 jdk Remote
- public interface RmiInvocationHandler extends Remote {
- }
public interface RmiInvocationHandler extends Remote {
}
- protected Object getProxyForService() {
- //检查配置中的 service对象 如果为null 抛出异常
- checkService();
- //检查serviceInterface属性
- checkServiceInterface();
- ProxyFactory proxyFactory = new ProxyFactory();
- proxyFactory.addInterface(getServiceInterface());
- if (this.registerTraceInterceptor != null ?
- this.registerTraceInterceptor.booleanValue() : this.interceptors == null) {
- proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName()));
- }
- if (this.interceptors != null) {
- AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
- for (int i = 0; i < this.interceptors.length; i++) {
- proxyFactory.addAdvisor(adapterRegistry.wrap(this.interceptors[i]));
- }
- }
- proxyFactory.setTarget(getService());
- // 生成代理对象 到底是jdk实现 还是cglib实现 取决于 到底有没有实现接口
- return proxyFactory.getProxy(getBeanClassLoader());
- }
protected Object getProxyForService() {
//检查配置中的 service对象 如果为null 抛出异常
checkService();
//检查serviceInterface属性
checkServiceInterface();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addInterface(getServiceInterface());
if (this.registerTraceInterceptor != null ?
this.registerTraceInterceptor.booleanValue() : this.interceptors == null) {
proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName()));
}
if (this.interceptors != null) {
AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
for (int i = 0; i < this.interceptors.length; i++) {
proxyFactory.addAdvisor(adapterRegistry.wrap(this.interceptors[i]));
}
}
proxyFactory.setTarget(getService());
// 生成代理对象 到底是jdk实现 还是cglib实现 取决于 到底有没有实现接口
return proxyFactory.getProxy(getBeanClassLoader());
}
总结:1.spring 容器发布一个远程服务 是通过InitializingBean接口驱动起来的
2.spring 包装了JDK Rmi 也就是说 服务端是spring暴露 客户端也可以用Jdk rmi调用 没有任何问题
3,spring对没有实现Remote接口的远程服务 用RmiInvocationWrapper做了包装 RmiInvocationWrapper实现了Remote接口