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. Mini2440 通过 SPI 操作 OLED (裸板下使用 SPI 控制器)

    在裸板下使用 SPI 的话,有两种方法可选: 使用 IO 口模拟 SPI 进行操作 使用 SPI 控制器进行操作 这里我们选用控制器的方式,简单方便. 初始化 SPI static void SPIC ...

  2. Vim 匹配相同的单词并高亮

    将光标移动到要匹配的单词处: "g + d" :高亮显示所有相同的单词 shift + "*" :向下查找相同单词并高亮显示 shift + "#&q ...

  3. Jquery闪烁提示特效

    样式:.red{ border:1px solid #d00; background:#ffe9e8; color:#d00;} function shake(ele,cls,times){ var ...

  4. www.jqhtml.com 前端框架特效

    www.jqhtml.com * 请选择课程 初级班 (PS.HTML.CSS.静态网站项目实战) 中级班 JavaScript基础.JavaScript DOM.jQuery.JS进阶.HTML5和 ...

  5. Windows安装diango框架<一>

    下一篇:使用Django创建网站项目<二> python工具安装 python下载:https://www.python.org/downloads/windows/(我的版本3.7.0) ...

  6. MarkDown常用格式

    常用格式 ** :加粗 <br> : 换行 > :可以用来引用文章,很漂亮. 可以展开的文件夹格式 <details> <summary>框架</sum ...

  7. webpack打包时排除其中一个css、js文件,或单独打包一个css、js文件

    在项目中经常会需要将一些接口的配合文件或者某些样式文件,分离出来单独打包,便于后期改动,这里我以css文件为例,介绍实现两种方法: 项目目录: 如上图所示,现在我需要将项目中的scBtn.css文件单 ...

  8. 前端面试整理——javascript算法和测试题

    (1)算法: 1.斐波那契数列:1.1.2.3.5.8.13.21.输入n,输出数列中第n位数的值. 方案一: function fn(n){ var num1 = 1, num2= 1, num3 ...

  9. [Error] 未发现相关 less 编译器配置,请检查wepy.config.js文件

    此错误是由于缺少包引起的 npm install less -d 直接装包即可

  10. id、name、setter方法注入、构造方法注入、工厂方法注入、注解注入、方法注入、方法替换、Web作用域、普通bean引用Web作用域的bean

    spring IoC的id和name id的命名需要满足XML对id的命名规范,必须以字母开始,后面可以是字母.数字.连字符.下画线.句号.冒号等等号,但逗号和空格是非法的.如果用户确实希望用一些特殊 ...