1. 程式人生 > >Spring RMI 原始碼淺析-RmiProxyFactoryBean 呼叫服務

Spring RMI 原始碼淺析-RmiProxyFactoryBean 呼叫服務

spring Rmi 客戶端是通過 RmiProxyFactoryBean 和它的父類來完成 查詢遠端物件  生成代理物件 方法呼叫

 RmiProxyFactoryBean 定義

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

父類RmiClientInterceptor 定義

[java] view plaincopyprint?
  1. publicclass RmiClientInterceptor extends RemoteInvocationBasedAccessor implements
     MethodInterceptor {  
  2.     //spring容器 bean例項化階段  是否要 查詢遠端物件 預查詢 
  3.     privateboolean lookupStubOnStartup = true;  
  4.     //查詢過的 遠端物件是否進行快取  
  5.     privateboolean cacheStub = true;  
  6.     //如果連線失敗 是否重新整理遠端呼叫stub  
  7.     privateboolean refreshStubOnConnectFailure = false;  
  8.     //rmi客戶端 套接字工廠  
  9.     private RMIClientSocketFactory registryClientSocketFactory;  
  10.     //快取遠端呼叫物件
  11.     private Remote cachedStub;  
  12.     //查詢遠端物件時用到的監控器  
  13.     privatefinal Object stubMonitor = new Object();  
  14.     //.....略
  15. }  

一:查詢遠端服務物件

  RmiProxyFactoryBean 是InitializingBean介面的實現   Spring容器在bean的例項化(getBean)階段  回撥afterPropertiesSet 來查詢遠端物件  然後 生成遠端代理物件

[java] view plaincopyprint?
  1. publicvoid
     afterPropertiesSet() {  
  2.     //父類RmiClientInterceptor檢查serviceUrl是否配置
  3.     //父類RmiClientInterceptor 查詢遠端物件 
  4.     super.afterPropertiesSet();  
  5.     //遠端呼叫介面檢查
  6.     if (getServiceInterface() == null) {  
  7.         thrownew 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. publicvoid afterPropertiesSet() {  
  2.     //檢查serviceUrl 屬性是否為空 如果為空直接丟擲異常
  3.     super.afterPropertiesSet();  
  4.     //查詢遠端物件 
  5.     prepare();  
  6. }  
[java] view plaincopyprint?
  1. publicvoid 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.         thrownew RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);  
  14.     }  
  15.     catch (NotBoundException ex) {  
  16.         thrownew RemoteLookupFailureException(  
  17.                 "Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex);  
  18.     }  
  19.     catch (RemoteException ex) {  
  20.         thrownew 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.     returnthis.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. publicstatic 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.             thrownew RemoteProxyFailureException("No matching RMI stub method found for: " + method, ex);  
  20.         }  
  21.         catch (Throwable ex) {  
  22.             thrownew RemoteProxyFailureException("Invocation of RMI stub method failed: " + method, ex);  
  23.         }  
  24.     }  

 非標準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. publicclass 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 方法呼叫都會經過它呼叫目標方法。