`

关于spring remoting的rmi服务2.5版本和3.0版本不兼容的解决办法

阅读更多

           这两天在研究一个其他组的人提供的服务,服务器端使用spring的remote进行rmi接口暴露的,客户端调用rmi接口。发现一直报下面的错误。

 

Exception in thread "main" java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
     java.lang.ClassNotFoundException: org.springframework.remoting.rmi.RmiInvocationWrapper_Stub (no security manager: RMI class loader disabled)
     at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
     at java.rmi.Naming.lookup(Unknown Source)
     at snippet.Snippet.main(Snippet.java:11)
Caused by: java.lang.ClassNotFoundException: org.springframework.remoting.rmi.RmiInvocationWrapper_Stub (no security manager: RMI class loader disabled)
     at sun.rmi.server.LoaderHandler.loadClass(Unknown Source)
     at sun.rmi.server.LoaderHandler.loadClass(Unknown Source)
     at java.rmi.server.RMIClassLoader$2.loadClass(Unknown Source)
     at java.rmi.server.RMIClassLoader.loadClass(Unknown Source)
     at sun.rmi.server.MarshalInputStream.resolveClass(Unknown Source)
     at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
     at java.io.ObjectInputStream.readClassDesc(Unknown Source)
     at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
     at java.io.ObjectInputStream.readObject0(Unknown Source)
     at java.io.ObjectInputStream.readObject(Unknown Source)
     ... 3 more

 

 

      从错误信息来看,是这个类org.springframework.remoting.rmi.RmiInvocationWrapper_Stub没有,从网上也发现了,这个是由于spring2和spring3的rmi方式调用方式不同引起的,通过查阅相关文档后发现,spring3不在需要生成skeleton和stub了,所以把这个类从spring-context中删除了,解决办法就是想办法将它再加进来,知道了病根就知道了怎么治病了,下面给出药方:

 

   就是将RmiInvocationWrapper_Stub类从spring2里面解压出来,然后再生成一个包。

   这边给出来解决的办法:

   1. 下载spring-context的2.5.6版本的程序,将其解压,解压命令如下:

 

jar -xvf modules/spring-context.jar org/springframework/remoting/rmi/RmiInvocationWrapper_Stub.class

   2. 将解压的RmiInvocationWrapper_Stub.class生成到一个新的的jar包里面,比如spring-2.5.6-rmi-compatibility.jar

 

   

jar -cvf spring-2.5.6-rmi-compatibility.jar org/springframework/remoting/rmi

   3. 由于我们系统使用了maven,这个包可以加入到maven的依赖里面,具体的使用scope为System就可以:

 

    

<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-rmi-compatibility</artifactId>
			<version>2.5.6</version>
			<scope>system</scope>  
   			<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/spring-2.5.6-rmi-compatibility.jar</systemPath>  
		</dependency>

 

  关于spring3为什么不需要这个类了,我也比较好奇,于是就看了下RmiProxyFactoryBean 的实现。

pring Rmi 客户端是通过 RmiProxyFactoryBean 和它的父类来完成 查找远程对象  生成代理对象 方法调用

 RmiProxyFactoryBean 定义

public class RmiProxyFactoryBean extends RmiClientInterceptor implements FactoryBean, BeanClassLoaderAware {  
}  

 父类RmiClientInterceptor 定义

public class RmiClientInterceptor extends RemoteInvocationBasedAccessor implements MethodInterceptor {

	//spring容器 bean实例化阶段  是否要 查找远程对象 预查找 
	private boolean lookupStubOnStartup = true;

	//查找过的 远程对象是否进行缓存  
	private boolean cacheStub = true;

	//如果连接失败 是否刷新远程调用stub  
	private boolean refreshStubOnConnectFailure = false;

	//rmi客户端 套接字工厂  
	private RMIClientSocketFactory registryClientSocketFactory;

	//缓存远程调用对象
	private Remote cachedStub;

	//查找远程对象时用到的监控器  
	private final Object stubMonitor = new Object();
	
	//.....略
}

 一:查找远程服务对象

  RmiProxyFactoryBean 是InitializingBean接口的实现   Spring容器在bean的实例化(getBean)阶段  回调afterPropertiesSet 来查找远程对象  然后 生成远程代理对象

	public void afterPropertiesSet() {
		//父类RmiClientInterceptor检查serviceUrl是否配置
		//父类RmiClientInterceptor 查找远程对象 
		super.afterPropertiesSet();
		//远程调用接口检查
		if (getServiceInterface() == null) {
			throw new IllegalArgumentException("Property 'serviceInterface' is required");
		}
		//创建代理对象
		//因为父类RmiClientInterceptor实现了 MethodInterceptor 接口  所以this
		this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
	}

 父类RmiClientInterceptor的 afterPropertiesSet 方法 干了两件事

1.验证是否配置了serviceUrl  如果没有 抛出异常 

2.查找远程对象

	public void afterPropertiesSet() {
		//检查serviceUrl 属性是否为空 如果为空直接抛出异常
		super.afterPropertiesSet();
		//查找远程对象 
		prepare();
	}

 

	public void prepare() throws RemoteLookupFailureException {
		//预查找远程对象 默认为true
		if (this.lookupStubOnStartup) {
			//通过标准Api  查找远程对象
			Remote remoteObj = lookupStub();
			//是否对stub进行缓存
			if (this.cacheStub) {
				this.cachedStub = remoteObj;
			}
		}
	}

 通过java API查找远程对象

	protected Remote lookupStub() throws RemoteLookupFailureException {
		try {
			Remote stub = null;
			if (this.registryClientSocketFactory != null) {
				...略			}
			else {
				//TODO 通过客户端配置 serviceUrl查找对象
				stub = Naming.lookup(getServiceUrl());
			}
			return stub;
		}
		catch (MalformedURLException ex) {
			throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);
		}
		catch (NotBoundException ex) {
			throw new RemoteLookupFailureException(
					"Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex);
		}
		catch (RemoteException ex) {
			throw new RemoteLookupFailureException("Lookup of RMI stub failed", ex);
		}
	}

 

二:返回代理对象

  RmiProxyFactoryBean是FactoryBean接口的实现 其返回的是getObject方法 返回的对象

	/**
	 * 返回远程代理对象
	 * 创建代理对象 是在afterPropertiesSet 方法完成
	 */
	public Object getObject() {
		return this.serviceProxy;
	}

 

三:调用方法

 父类实现了MethodInterceptor接口  在客户端调用方法时会被拦截

	public Object invoke(MethodInvocation invocation) throws Throwable {
		//获取远程对象  如果配置了缓存cacheStub=true  从缓存中获取  缓存中没有 现在立刻查找 
		Remote stub = getStub();
		try {
			//TODO 客户端调用远程方法时  拦截处理
			return doInvoke(invocation, stub);
		}
		catch (RemoteConnectFailureException ex) {
			return handleRemoteConnectFailure(invocation, ex);
		}
		catch (RemoteException ex) {
			//如果是连接失败异常
			if (isConnectFailure(ex)) {
				//处理连接失败. 是否需要刷新
				return handleRemoteConnectFailure(invocation, ex);
			}
			else {
				throw ex;
			}
		}
	}

 

方法调用,如果是标准的Rmi 通过反射调用,非标准的交给doInvoke方法处理

	protected Object doInvoke(MethodInvocation invocation, Remote stub) throws Throwable {
		//spring RmiInvocationHandler包装的远程对象   非实现Remote接口的
		if (stub instanceof RmiInvocationHandler) {
			try {
				//不是标准的Rmi
				return doInvoke(invocation, (RmiInvocationHandler) stub);
			}
			//....略
		}
		else {
			//标准的java Rmi
			try {
				//直接通过反射调用 
				return RmiClientInterceptorUtils.invokeRemoteMethod(invocation, stub);
			}
			//....略
		}
	}

 

标准Rmi方法调用处理 RmiClientInterceptorUtils的  invokeRemoteMethod方法

public static Object invokeRemoteMethod(MethodInvocation invocation, Object stub)
			throws InvocationTargetException {

		Method method = invocation.getMethod();
		try {
			if (method.getDeclaringClass().isInstance(stub)) {
				// directly implemented
				return method.invoke(stub, invocation.getArguments());
			}
			else {
				// not directly implemented
				Method stubMethod = stub.getClass().getMethod(method.getName(), method.getParameterTypes());
				return stubMethod.invoke(stub, invocation.getArguments());
			}
		}
		catch (InvocationTargetException ex) {
			throw ex;
		}
		catch (NoSuchMethodException ex) {
			throw new RemoteProxyFailureException("No matching RMI stub method found for: " + method, ex);
		}
		catch (Throwable ex) {
			throw new RemoteProxyFailureException("Invocation of RMI stub method failed: " + method, ex);
		}
	}

 

非标准Rmi处理 方法名 参数封装成InvocationHandler 通过中转站方法调用目标方法

	protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler)
	    throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		// 如果是toString方法 
		if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
			return "RMI invoker proxy for service URL [" + getServiceUrl() + "]";
		}
		//invocationHandler spring包装过的Rmi远程对象   服务端在暴露服务时包装
		//createRemoteInvocation方法 返回RemoteInvocation实例  封装了方法调用相关信息  例如:参数, 方法名
		//Rmi服务端接受到信息后  会通过RemoteInvocation封装的信息 进行调用
		return invocationHandler.invoke(createRemoteInvocation(methodInvocation));
	}

 

RemoteInvocation定义 

public class RemoteInvocation implements Serializable {

	private String methodName;

	private Class[] parameterTypes;

	private Object[] arguments;

	private Map attributes;

	public RemoteInvocation(MethodInvocation methodInvocation) {
		this.methodName = methodInvocation.getMethod().getName();
		this.parameterTypes = methodInvocation.getMethod().getParameterTypes();
		this.arguments = methodInvocation.getArguments();
	}
}

 

总结一下:

  1. 标准的Rmi 即实现了jdk Remote接口的   直接使用反射机制调用
  2. 非标准的Rmi  spring暴露服务时包装成自己的对象[RmiInvocationHandler]  当客户段调用的时候   被拦截器拦截  封装方法名  参数等信息 最后调用RmiInvocationHandler的invoke方法  invoke方法类似中转站(泛化调用)   只要非标准Rmi 方法调用都会经过它调用目标方法。
  3. Spring对RMI的支持果然很不错,在Cglib等工具的支持下,使用RMI终于可以同Naming、rmic和stub告别了。
  4. 用以发布RMI的接口不能从java.rmi.Remote继承而来,否则就会出现“Stub class not found”的错误,原因有待深究。
  5. Spring的BeanFactory创建bean实例是有序的,向RMI、JNDI、WebService等注册服务性质的应用,同一应用中的客户端要根据其依赖性调整配置顺序。

 

 

3
3
分享到:
评论
4 楼 344451441 2015-08-17  
非常感谢楼主的无私分享
3 楼 jinnianshilongnian 2013-12-10  
是啊,一直觉得spring兼容性非常好,哈哈。估计设计这个的人经验也不足 哈哈
2 楼 asialee 2013-12-10  
jinnianshilongnian 写道

其实我也是看到了这个文章,其他的都说不兼容,看了这个就详细了,感觉spring应该是无侵入的,怎么rmi这块有这个问题。

相关推荐

    spring remoting的rmi服务2.5版本和3.0版本不兼容解决办法

    由于spring2和spring3的rmi方式调用方式不同引起的,通过查阅相关文档后发现,spring3不在需要生成skeleton和stub了,所以把这个类从spring-context中删除了,解决办法就是想办法将它再加进来

    Spring2.5+DWR3.0整合实现autocomplete

    标题中的“Spring2.5+DWR3.0整合实现autocomplete”是指在Web开发中,使用Spring 2.5框架与Direct Web Remoting (DWR) 3.0库结合,来实现一个自动补全(autocomplete)功能。这个功能常见于搜索引擎、表单输入等场景...

    spring2.0和spring2.5 及以上版本的jar包区别 spring jar 包详解

    本文将重点介绍Spring2.0与Spring2.5及以上版本之间的区别,特别是关于它们的jar包组成及其使用场景。 #### Spring2.0与Spring2.5版本间的jar包区别 在Spring2.0时代,为了方便开发者快速构建应用程序,提供了一个...

    spring2.5 -3.0 hibernate3.3 jar包说明

    接下来,我们将详细介绍Spring 2.5至3.0版本中包含的关键Jar包及其功能: 1. **spring-core.jar** 这个Jar包包含了Spring框架的基础部分,是所有Spring应用的基础,提供了IoC容器的基础实现。它还包含了核心工具...

    spring RMI 服务(自动把service发布为RMI服务)

    总结来说,Spring RMI服务通过Spring的IoC和AOP特性简化了RMI服务的实现,同时提供了自动化发布服务的能力。开发者只需要定义服务接口和实现,Spring会负责服务的注册、暴露和管理。这种方式使得分布式系统的构建...

    spring remoting

    在分布式系统中,远程调用(Remoting)是必不可少的技术之一,Spring Remoting提供了多种远程调用解决方案,帮助开发者轻松实现服务间的通信。本文将深入探讨Spring Remoting的核心概念、实现方式以及实际应用。 ...

    Spring RMI

    在Spring Boot应用中,可以通过配置文件(application.properties或application.yml)设置RMI端口和服务,自动暴露RMI接口。 ### 7. 示例代码 创建远程接口: ```java public interface MyRemoteService extends ...

    SpringRMI小例子

    Spring提供了对RMI的高级抽象,通过其`org.springframework.remoting.rmi`包,使得RMI服务的创建、配置和暴露变得更加简单。在Spring MVC项目中,我们可以将RMI服务作为一个Bean来管理,这样可以充分利用Spring的...

    Spring-RMI.rar_spring rmi

    1.2 Spring的RMI支持:Spring通过`org.springframework.remoting.rmi.RmiServiceExporter`和`RmiProxyFactoryBean`简化了RMI的使用。`RmiServiceExporter`用于发布服务,而`RmiProxyFactoryBean`则用于创建RMI服务的...

    配置整合DWR3.0和Spring2.5使用annotation注解

    在本文中,我们将探讨如何将Direct Web Remoting (DWR) 3.0与Spring 2.5框架整合,并利用注解(Annotation)进行配置。DWR是一个允许JavaScript与Java服务器端进行交互的库,而Spring 2.5引入了对注解的强大支持,...

    spring-web-2.5.jar

    org.springframework.remoting.caucho.BurlapClientInterceptor.class org.springframework.remoting.caucho.BurlapProxyFactoryBean.class org.springframework.remoting.caucho.BurlapServiceExporter.class org....

    rmi与spring整合实例

    - Spring提供了多种与RMI集成的方式,包括使用`org.springframework.remoting.rmi.RmiServiceExporter`和`RmiProxyFactoryBean`。 3. RMI与Spring整合: - `RmiServiceExporter`是Spring提供的一个工具类,用于...

    spring rmi 小例子

    在本示例中,我们将探讨如何使用Spring RMI创建一个小的应用程序,这通常涉及到服务器端(服务提供者)和客户端(服务消费者)的设置。 首先,让我们了解Spring RMI的核心概念: 1. **接口定义**:在RMI中,我们...

    spring rmi 集成

    **Spring RMI 集成详解*...综上所述,Spring RMI集成使得在分布式环境中开发和管理服务变得更为便捷。通过正确配置服务器和客户端,可以实现高效的远程调用,同时利用Spring的管理能力,提高代码的可维护性和可测试性。

    Spring 2.5 jar 所有开发包及完整文档及项目开发实例

    13) spring-mock.jar需spring-core.jar,spring-beans.jar,spring-dao.jar,spring-context.jar,spring-jdbc.jarspring2.0和spring2.5及以上版本的jar包区别Spring 2.5的Jar打包 在Spring 2.5中, Spring Web MVC...

    spring RMI简单例子

    Spring RMI在此基础上提供了更高级别的抽象,通过Spring的依赖注入(DI)和AOP(面向切面编程)能力简化了服务的创建和调用。 要实现Spring RMI,我们需要以下组件: 1. **远程接口(Remote Interface)**:这是...

    spring rmi应用

    Spring RMI整合了RMI机制,提供了一种更加灵活和易于管理的方式,让开发者可以在Spring容器中定义远程服务,并通过Spring的IoC(Inversion of Control)和AOP(Aspect Oriented Programming)特性来增强这些服务。...

    java spring+rmi 的远程调用例子

    Java Spring与RMI(Remote Method Invocation)的整合是企业级应用中常见的远程服务交互方式。Spring框架提供了对RMI的支持,使得开发者能够方便地在分布式环境中进行服务的调用。这个压缩包文件“Spring+RMI”很...

    spring RMI 实用分享

    在本文中,我们将深入探讨Spring框架如何集成RMI,以及如何创建和使用RMI客户端。 首先,让我们了解RMI的基本概念。RMI是一种机制,它允许一个对象在一台计算机上执行的方法可以在另一台计算机上执行,就好像这些...

Global site tag (gtag.js) - Google Analytics