在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!!!!太好了,不过,以上修改还是显得太过草率,但很好去规范化,只是向大家展示一下我遇到的问
题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值