写给spring版本的那些事儿
1.远程调用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
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版本的程序,将其解压,解压命令如下:
- 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();
- }
- }
总结一下:
- 标准的Rmi 即实现了jdk Remote接口的 直接使用反射机制调用
- 非标准的Rmi spring暴露服务时包装成自己的对象[RmiInvocationHandler] 当客户段调用的时候 被拦截器拦截 封装方法名 参数等信息 最后调用RmiInvocationHandler的invoke方法 invoke方法类似中转站(泛化调用) 只要非标准Rmi 方法调用都会经过它调用目标方法。
- Spring对RMI的支持果然很不错,在Cglib等工具的支持下,使用RMI终于可以同Naming、rmic和stub告别了。
- 用以发布RMI的接口不能从java.rmi.Remote继承而来,否则就会出现“Stub class not found”的错误,原因有待深究。
- Spring的BeanFactory创建bean实例是有序的,向RMI、JNDI、WebService等注册服务性质的应用,同一应用中的客户端要根据其依赖性调整配置顺序。
写给spring版本的那些事儿的更多相关文章
- 聊聊Spring Cloud版本的那些事儿
说说Spring Cloud版本的那些事儿. 版本命名 之前提到过,Spring Cloud是一个拥有诸多子项目的大型综合项目,原则上其子项目也都维护着自己的发布版本号.那么每一个Spring Clo ...
- 关于RequestParam在不同的Spring版本上,接口在controller重载时注解可能失效的踩坑记录
先抛背景: 我项目中的Spring版本是2.0.3.RELEASE. api-demo负责暴露接口,service-demo负责实现功能.接口参数的@RequestParam和@RequestBody ...
- 【工作篇】了解升级 Spring 版本导致的跨域问题
一.背景 最近需要统一升级 Spring 的版本,避免 common 包和各个项目间的 Spring 版本冲突问题.这次升级主要是从 Spring 4.1.9.RELEASE 升级到 Spring 4 ...
- spring ioc原理(看完后大家可以自己写一个spring)
控制反转/依赖注入 最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专 ...
- java24 手写服务器最终版本
手写服务器最终版本; <?xml version="1.0" encoding="UTF-8"?> <web-app> <serv ...
- (转)spring ioc原理(看完后大家可以自己写一个spring)
最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专注于Manning, ...
- Windows系统版本判定那些事儿
v:* { } o:* { } w:* { } .shape { }p.MsoNormal,li.MsoNormal,div.MsoNormal { margin: 0cm; margin-botto ...
- Windows系统版本判定那些事儿[转]
Windows系统版本判定那些事儿 转自CSDN,原文链接,我比较不要脸, 全部给复制过来了 前言 本文并不是讨论Windows操作系统的版本来历和特点,也不是讨论为什么没有Win9,而是从程序员角度 ...
- spring版本不兼容JDK问题
在实验书上Spring项目的时候出现一个问题,导入包和使用注释的时候eclipse出现报错. 导入包报错:The import org cannot be resolved 注释报错:componen ...
随机推荐
- Mini2440 通过 SPI 操作 OLED (裸板下使用 SPI 控制器)
在裸板下使用 SPI 的话,有两种方法可选: 使用 IO 口模拟 SPI 进行操作 使用 SPI 控制器进行操作 这里我们选用控制器的方式,简单方便. 初始化 SPI static void SPIC ...
- Vim 匹配相同的单词并高亮
将光标移动到要匹配的单词处: "g + d" :高亮显示所有相同的单词 shift + "*" :向下查找相同单词并高亮显示 shift + "#&q ...
- Jquery闪烁提示特效
样式:.red{ border:1px solid #d00; background:#ffe9e8; color:#d00;} function shake(ele,cls,times){ var ...
- www.jqhtml.com 前端框架特效
www.jqhtml.com * 请选择课程 初级班 (PS.HTML.CSS.静态网站项目实战) 中级班 JavaScript基础.JavaScript DOM.jQuery.JS进阶.HTML5和 ...
- Windows安装diango框架<一>
下一篇:使用Django创建网站项目<二> python工具安装 python下载:https://www.python.org/downloads/windows/(我的版本3.7.0) ...
- MarkDown常用格式
常用格式 ** :加粗 <br> : 换行 > :可以用来引用文章,很漂亮. 可以展开的文件夹格式 <details> <summary>框架</sum ...
- webpack打包时排除其中一个css、js文件,或单独打包一个css、js文件
在项目中经常会需要将一些接口的配合文件或者某些样式文件,分离出来单独打包,便于后期改动,这里我以css文件为例,介绍实现两种方法: 项目目录: 如上图所示,现在我需要将项目中的scBtn.css文件单 ...
- 前端面试整理——javascript算法和测试题
(1)算法: 1.斐波那契数列:1.1.2.3.5.8.13.21.输入n,输出数列中第n位数的值. 方案一: function fn(n){ var num1 = 1, num2= 1, num3 ...
- [Error] 未发现相关 less 编译器配置,请检查wepy.config.js文件
此错误是由于缺少包引起的 npm install less -d 直接装包即可
- id、name、setter方法注入、构造方法注入、工厂方法注入、注解注入、方法注入、方法替换、Web作用域、普通bean引用Web作用域的bean
spring IoC的id和name id的命名需要满足XML对id的命名规范,必须以字母开始,后面可以是字母.数字.连字符.下画线.句号.冒号等等号,但逗号和空格是非法的.如果用户确实希望用一些特殊 ...