zoukankan      html  css  js  c++  java
  • 写给spring版本的那些事儿

    1.远程调用rmi协议

    Java代码  收藏代码
      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版本的程序,将其解压,解压命令如下:

    Java代码  收藏代码
    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

       

    Java代码  收藏代码
    1. jar -cvf spring-2.5.6-rmi-compatibility.jar org/springframework/remoting/rmi  

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

        

    Xml代码  收藏代码
    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 定义

    Java代码  收藏代码
    1. public class RmiProxyFactoryBean extends RmiClientInterceptor implements FactoryBean, BeanClassLoaderAware {    
    2. }    

     父类RmiClientInterceptor 定义

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

     一:查找远程服务对象

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

    Java代码  收藏代码
    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.查找远程对象

    Java代码  收藏代码
    1. public void afterPropertiesSet() {  
    2.     //检查serviceUrl 属性是否为空 如果为空直接抛出异常  
    3.     super.afterPropertiesSet();  
    4.     //查找远程对象   
    5.     prepare();  
    6. }  

     

    Java代码  收藏代码
    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查找远程对象

    Java代码  收藏代码
    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方法 返回的对象

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

     

    三:调用方法

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

    Java代码  收藏代码
    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方法处理

    Java代码  收藏代码
    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方法

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

     

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

    Java代码  收藏代码
    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定义 

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

     

    总结一下:

    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等注册服务性质的应用,同一应用中的客户端要根据其依赖性调整配置顺序。
  • 相关阅读:
    Job for vsftpd.service failed because the control process exited with error code
    Linux 调优方案, 修改最大连接数-ulimit
    vsftpd配置文件详解
    Linux下TCP最大连接数受限问题
    vsftp限制FTP用户只能访问自己的目录
    linux YUM常用 命令
    Linux 系统sudo命令
    部分有关 广告联盟作弊 与反作弊资料收集
    Boosted Tree
    如何将数据转换libsvm格式文件
  • 原文地址:https://www.cnblogs.com/avivahe/p/6139185.html
Copyright © 2011-2022 走看看