Spring RMI 源码浅析-RmiProxyFactoryBean 调用服务

来源:互联网 发布:常用的数据挖掘软件 编辑:程序博客网 时间:2024/05/22 10:38
spring Rmi 客户端是通过 RmiProxyFactoryBean 和它的父类来完成 查找远程对象  生成代理对象 方法调用

 RmiProxyFactoryBean 定义

[java] view plaincopyprint?
  1. public class RmiProxyFactoryBean extends RmiClientInterceptor implements FactoryBean, BeanClassLoaderAware {  
  2. }  

父类RmiClientInterceptor 定义

[java] view plaincopyprint?
  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] view plaincopyprint?
  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] view plaincopyprint?
  1. public void afterPropertiesSet() {  
  2.     //检查serviceUrl 属性是否为空 如果为空直接抛出异常  
  3.     super.afterPropertiesSet();  
  4.     //查找远程对象   
  5.     prepare();  
  6. }  
[java] view plaincopyprint?
  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] view plaincopyprint?
  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] view plaincopyprint?
  1. /** 
  2.  * 返回远程代理对象 
  3.  * 创建代理对象 是在afterPropertiesSet 方法完成 
  4.  */  
  5. public Object getObject() {  
  6.     return this.serviceProxy;  
  7. }  

三:调用方法

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

[java] view plaincopyprint?
  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] view plaincopyprint?
  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] view plaincopyprint?
  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] view plaincopyprint?
  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] view plaincopyprint?
  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 方法调用都会经过它调用目标方法。

原创粉丝点击