1.远程调用rmi协议

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

1.1 spring2.5和spring3.0的区别

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

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

这边给出来解决的办法:

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

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

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

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

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

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

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

RmiProxyFactoryBean 定义

  1. public class RmiProxyFactoryBean extends RmiClientInterceptor implements FactoryBean, BeanClassLoaderAware {
  2. }

父类RmiClientInterceptor 定义

  1. public class RmiClientInterceptor extends RemoteInvocationBasedAccessor implements MethodInterceptor {
  2. //spring容器 bean实例化阶段  是否要 查找远程对象 预查找
  3. private boolean lookupStubOnStartup = true;
  4. //查找过的 远程对象是否进行缓存
  5. private boolean cacheStub = true;
  6. //如果连接失败 是否刷新远程调用stub
  7. private boolean refreshStubOnConnectFailure = false;
  8. //rmi客户端 套接字工厂
  9. private RMIClientSocketFactory registryClientSocketFactory;
  10. //缓存远程调用对象
  11. private Remote cachedStub;
  12. //查找远程对象时用到的监控器
  13. private final Object stubMonitor = new Object();
  14. //.....略
  15. }

一:查找远程服务对象

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

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

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

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

2.查找远程对象

  1. public void afterPropertiesSet() {
  2. //检查serviceUrl 属性是否为空 如果为空直接抛出异常
  3. super.afterPropertiesSet();
  4. //查找远程对象
  5. prepare();
  6. }
  1. public void prepare() throws RemoteLookupFailureException {
  2. //预查找远程对象 默认为true
  3. if (this.lookupStubOnStartup) {
  4. //通过标准Api  查找远程对象
  5. Remote remoteObj = lookupStub();
  6. //是否对stub进行缓存
  7. if (this.cacheStub) {
  8. this.cachedStub = remoteObj;
  9. }
  10. }
  11. }

通过java API查找远程对象

  1. protected Remote lookupStub() throws RemoteLookupFailureException {
  2. try {
  3. Remote stub = null;
  4. if (this.registryClientSocketFactory != null) {
  5. ...略            }
  6. else {
  7. //TODO 通过客户端配置 serviceUrl查找对象
  8. stub = Naming.lookup(getServiceUrl());
  9. }
  10. return stub;
  11. }
  12. catch (MalformedURLException ex) {
  13. throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);
  14. }
  15. catch (NotBoundException ex) {
  16. throw new RemoteLookupFailureException(
  17. "Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex);
  18. }
  19. catch (RemoteException ex) {
  20. throw new RemoteLookupFailureException("Lookup of RMI stub failed", ex);
  21. }
  22. }

二:返回代理对象

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

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

三:调用方法

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

  1. public Object invoke(MethodInvocation invocation) throws Throwable {
  2. //获取远程对象  如果配置了缓存cacheStub=true  从缓存中获取  缓存中没有 现在立刻查找
  3. Remote stub = getStub();
  4. try {
  5. //TODO 客户端调用远程方法时  拦截处理
  6. return doInvoke(invocation, stub);
  7. }
  8. catch (RemoteConnectFailureException ex) {
  9. return handleRemoteConnectFailure(invocation, ex);
  10. }
  11. catch (RemoteException ex) {
  12. //如果是连接失败异常
  13. if (isConnectFailure(ex)) {
  14. //处理连接失败. 是否需要刷新
  15. return handleRemoteConnectFailure(invocation, ex);
  16. }
  17. else {
  18. throw ex;
  19. }
  20. }
  21. }

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

  1. protected Object doInvoke(MethodInvocation invocation, Remote stub) throws Throwable {
  2. //spring RmiInvocationHandler包装的远程对象   非实现Remote接口的
  3. if (stub instanceof RmiInvocationHandler) {
  4. try {
  5. //不是标准的Rmi
  6. return doInvoke(invocation, (RmiInvocationHandler) stub);
  7. }
  8. //....略
  9. }
  10. else {
  11. //标准的java Rmi
  12. try {
  13. //直接通过反射调用
  14. return RmiClientInterceptorUtils.invokeRemoteMethod(invocation, stub);
  15. }
  16. //....略
  17. }
  18. }

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

  1. public static Object invokeRemoteMethod(MethodInvocation invocation, Object stub)
  2. throws InvocationTargetException {
  3. Method method = invocation.getMethod();
  4. try {
  5. if (method.getDeclaringClass().isInstance(stub)) {
  6. // directly implemented
  7. return method.invoke(stub, invocation.getArguments());
  8. }
  9. else {
  10. // not directly implemented
  11. Method stubMethod = stub.getClass().getMethod(method.getName(), method.getParameterTypes());
  12. return stubMethod.invoke(stub, invocation.getArguments());
  13. }
  14. }
  15. catch (InvocationTargetException ex) {
  16. throw ex;
  17. }
  18. catch (NoSuchMethodException ex) {
  19. throw new RemoteProxyFailureException("No matching RMI stub method found for: " + method, ex);
  20. }
  21. catch (Throwable ex) {
  22. throw new RemoteProxyFailureException("Invocation of RMI stub method failed: " + method, ex);
  23. }
  24. }

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

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

RemoteInvocation定义

  1. public class RemoteInvocation implements Serializable {
  2. private String methodName;
  3. private Class[] parameterTypes;
  4. private Object[] arguments;
  5. private Map attributes;
  6. public RemoteInvocation(MethodInvocation methodInvocation) {
  7. this.methodName = methodInvocation.getMethod().getName();
  8. this.parameterTypes = methodInvocation.getMethod().getParameterTypes();
  9. this.arguments = methodInvocation.getArguments();
  10. }
  11. }

总结一下:

  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等注册服务性质的应用,同一应用中的客户端要根据其依赖性调整配置顺序。

写给spring版本的那些事儿的更多相关文章

  1. 聊聊Spring Cloud版本的那些事儿

    说说Spring Cloud版本的那些事儿. 版本命名 之前提到过,Spring Cloud是一个拥有诸多子项目的大型综合项目,原则上其子项目也都维护着自己的发布版本号.那么每一个Spring Clo ...

  2. 关于RequestParam在不同的Spring版本上,接口在controller重载时注解可能失效的踩坑记录

    先抛背景: 我项目中的Spring版本是2.0.3.RELEASE. api-demo负责暴露接口,service-demo负责实现功能.接口参数的@RequestParam和@RequestBody ...

  3. 【工作篇】了解升级 Spring 版本导致的跨域问题

    一.背景 最近需要统一升级 Spring 的版本,避免 common 包和各个项目间的 Spring 版本冲突问题.这次升级主要是从 Spring 4.1.9.RELEASE 升级到 Spring 4 ...

  4. spring ioc原理(看完后大家可以自己写一个spring)

    控制反转/依赖注入 最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专 ...

  5. java24 手写服务器最终版本

    手写服务器最终版本; <?xml version="1.0" encoding="UTF-8"?> <web-app> <serv ...

  6. (转)spring ioc原理(看完后大家可以自己写一个spring)

    最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专注于Manning, ...

  7. Windows系统版本判定那些事儿

    v:* { } o:* { } w:* { } .shape { }p.MsoNormal,li.MsoNormal,div.MsoNormal { margin: 0cm; margin-botto ...

  8. Windows系统版本判定那些事儿[转]

    Windows系统版本判定那些事儿 转自CSDN,原文链接,我比较不要脸, 全部给复制过来了 前言 本文并不是讨论Windows操作系统的版本来历和特点,也不是讨论为什么没有Win9,而是从程序员角度 ...

  9. spring版本不兼容JDK问题

    在实验书上Spring项目的时候出现一个问题,导入包和使用注释的时候eclipse出现报错. 导入包报错:The import org cannot be resolved 注释报错:componen ...

随机推荐

  1. DNS Wildcard(DNS泛域名)

    在DNS中,泛域名(wildcard Resource Record)可以被认为是一种合成RR的机制,借助于它,DNS服务器可以响应本来不存在的域名的请求,它的设计初衷是用来把所有邮件都转发到一个邮件 ...

  2. 光盘作为yum源

    1.挂载光盘 mkdir /media/cdrom       //在/media下建立cdrom目录,默认情况是没有的  mount /dev/cdrom /mnt/cdrom2.进入 /etc/y ...

  3. Deep learning with Python 学习笔记(10)

    生成式深度学习 机器学习模型能够对图像.音乐和故事的统计潜在空间(latent space)进行学习,然后从这个空间中采样(sample),创造出与模型在训练数据中所见到的艺术作品具有相似特征的新作品 ...

  4. Angular2入门:TypeScript的类型 - 数组解构

  5. Maven教程4(私服-nexus)

    仓库管理器也叫私服或代理仓库 仓库管理器有两个服务目的:首先它的角色是一个高度可配置的介于你的组织与公开Maven仓库之间的代理,其次它为你的组织提供了一个可部署你组织内部生成的构件的地方. 1Nex ...

  6. Scala字符串与容器

    String 前文已经说明了字符串的声明方式: var s = "Hello World"; var s: String = "Hello World"; 在 ...

  7. this引用逃逸

    1.什么是This逃逸? 在构造器构造还未彻底完成前(即实例初始化阶段还未完成),将自身this引用向外抛出并被其他线程复制(访问)了该引用,可能会问到该还未被初始化的变量,甚至可能会造成更大严重的问 ...

  8. [android] 手机卫士手势滑动切换屏幕

    定义手势识别器 获取手势识别器GestureDetector对象,通过new GestureDetector(context,listener),参数:上下文,监听器 匿名内部类实现简单手势监听器Si ...

  9. Mybatis关联查询之一对多和多对一XML配置详解

    平时在开发过程中dao.bean和XML文件都是自动生成的,很少写XML的配置关系,今天记录一下mybatis的关联查询中的多对一和一对多的情况. 首先是有两张表(学生表Student和老师Teach ...

  10. 漫画|你还记得原生的JDBC怎么连接数据库吗?

    数据表的设计范式 在实际开发中最为常见的设计范式有三个: 第一范式是最基本的范式.如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式: 第二范式需要确保数据库表中的每一列都 ...