在Eclipse RCP中使用Spring AOP/ProxyFactory的問題

也許你還沒有在RCP中用過SPRING,也許你用過,但沒有遇到什麼問題,那是因爲你只用了簡單的IOC,而沒有用到AOP或是FactoryBean,因此,您並沒有碰到什麼問題,但一些很簡單的需求導致我們需在/可以在RCP中使用AOP。
如:
1、需要用到Spring remoting,利用客戶端的invoker訪問遠程的業務,
2、對客戶端的業務聲明事務,你也需要用到類似的東西。
爲了儘量將問題最小化,我們只在context中聲明瞭最少的BEAN,如下:
<bean id="synClient"
 class="com.mudboy.rcp.syncpoc.client.grpclient.GroupClient" singleton="false">
 <property name="synService"><ref bean="syncServerProxy"/></property>
</bean>
<bean  id="syncServerProxy" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
 <property name="serviceUrl"><value>rmi://192.168.40.120:2345/synservice</value></property>                   
<property name="serviceInterface"><value>com.mudboy.rcp.syncpoc.client.grpserver.SynServer</value></property>
</bean>

它聲明瞭一個調用遠程RMI服務的代理。

很顯然,根據SPRING DEBUG信息,它是在生成syncServerProxy出現了錯誤,異常類型是初始化錯誤,沒有更詳細的信息。其中,我們是在plug-in是使用BEAN相關代碼,並將spring加入到依賴包。


spring 版本是1.2.7
經過一番跟蹤,發現在spring aop機制與rcp classloader存在一些不協調的地方,在下面的代碼中出現了問題:
org.springframework.aop.framework.DefaultAopProxyFactory類中,該方法是產生代理的執行路徑之一:
 
public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
  if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() ||
      advisedSupport.getProxiedInterfaces().length == 0) {
   if (!cglibAvailable) {
    throw new AopConfigException(
      "Cannot proxy target class because CGLIB2 is not available. " +
      "Add CGLIB to the class path or specify proxy interfaces.");
   }
   return CglibProxyFactory.createCglibProxy(advisedSupport);
  }
  else {
   return new JdkDynamicAopProxy(advisedSupport);
  }
 }
根據常用設置,代碼走到了return new JdkDynamicAopProxy(advisedSupport);,並在其它調用其(JdkDynamicAopProxy)
public Object getProxy(ClassLoader classLoader)方法。該方法如下:
public Object getProxy(ClassLoader classLoader) {
  if (logger.isDebugEnabled()) {
   Class targetClass = this.advised.getTargetSource().getTargetClass();
   logger.debug("Creating JDK dynamic proxy" +
     (targetClass != null ? " for [" + targetClass.getName() + "]" : ""));
  }
  Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
  return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
 }
前面,利用ClassUtils.getDefaultClassLoader()得到了默認的classloader並調用了getProxy,結果在
Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);時發生了錯誤(是這JRE裏的代碼了)

後來打出了classloader 樹,發生,plug-in應用的classloader只有兩層,並沒有同sun的底層JRE的classloader
關聯起來。因此,你用proxy(JRE)中的動態代理生成的類試圖轉換成根它沒有關係的另一個classloader的一個類
當然會出錯了。

發現這個問題後,定了解決問題的主要思路,就是不用JAVA本身的動態代理,而使用cglib,即讓應用走if的上一個分支,
要讓它走這個分支很簡單,只需簡單的將isOptimize設爲true就行,做法:
org/springframework/aop/framework/ProxyFactory
public static Object getProxy(Class proxyInterface, Interceptor interceptor) {
  ProxyFactory proxyFactory = new ProxyFactory();
  proxyFactory.addInterface(proxyInterface);
  proxyFactory.addAdvice(interceptor);
  return proxyFactory.getProxy();
}
中加一句:
proxyFactory.setOptimize(true);即可。


本已爲就沒有問題了,查在生成BEAN的時候還是出
了錯,錯誤信息"Either an interface or a target is required for proxy creation",最終定位在了文件:
org.springframework.aop.framework.Cglib2AopProxy,在其構造函數中,有如下語句:
if (config.getTargetSource().getTargetClass() == null) {
 throw new AopConfigException("Either an interface or a target is required for proxy creation");
}

而在整個調用流中,顯然是沒有設置TargetSource,也就是說,用cglib還是有些不太一樣,但看到這代碼的上面有如下
作者的註釋:
//DK - is this check really necessary?
//should this 'config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE' be enough?
於是乾脆打它上面的話注掉,記它不作這個檢查,再運行,還是出錯,這次錯誤發生在同一文件的如下方法:
public Object getProxy(ClassLoader classLoader)

Class rootClass = this.advised.getTargetSource().getTargetClass();
用到了TargetSource,其實,其targetClass無非是我的那個接口呀,
com.mudboy.rcp.syncpoc.client.grpserver.SynServer
來個BT一點的,直接將上面這句改爲:
Class rootClass = this.advised.getProxiedInterfaces()[0]

後運行應用,OK!!!!太好了,不過,以上修改還是顯得太過草率,但很好去規範化,只是向大家展示一下我遇到的問
題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章