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. 数据库新秀 postgresql vs mongo 性能PK

    前几天看了一篇文章<High Performance JSON PostgreSQL vs. MongoDB> 发布在Percona Live Europe 2017 作者是<Dom ...

  2. 域名系统DNS简介

    域名系统(Domain Name System, DNS)是互联网的核心应用层协议之一, 它用于查询域名对应的IP地址.在使用域名访问任何网络资源时都需要先进行域名解析. www.cnblogs.co ...

  3. layui 文字滚动

    将消息标题滚动 上面是效果 <li class="layui-nav-item"> <div class="layui-carousel" i ...

  4. 轻量级ORM——PetaPoco

    近乎产品的数据访问是基于轻量级ORM——PetaPoco,哪怕是最新的5.2版本也是基于PetaPoco. 产品源码下载地址:http://www.jinhusns.com/Products/Down ...

  5. DOM.getBoundingClientRect()

  6. 解决vue-cli不能初始化webpack模板的问题(vue init卡住了,解决办法)

    报这个错误 有人说是代理问题.我也不懂,但这个方法有用 1.去github上下载要初始化的模板 https://github.com/vuejs-templates/webpack 或者直接用git去 ...

  7. CSS之设置滚动条样式

    因为在现在的大部分项目中很多都用到了滚动条,有时候用到模拟的滚动条,现在说下滚动条的CSS也能解决. 比如网易邮箱的滚动条样子很好看,就是利用的CSS来设置的,而且是webkit浏览器的.如图所示: ...

  8. cdn原理的理解

    今天要做个小笔记,浅谈一下对cdn的一些理解,在工作中我们经常用到cdn代理访问,那他的原理是什么不知道大家有没有考虑过 CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集 ...

  9. 【代码笔记】Web-HTML-框架

    一,效果图. 二,代码. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  10. 关于input的焦点事件

    关于input的焦点事件 $(".scanf_integral").focus(function(){//获取焦点//获取焦点后触发的事件 }) $(".scanf_in ...