弹簧AOP方面没有在线程中触发

时间:2012-07-15 20:22:28

标签: multithreading spring aop

在线程运行之前调用的每个方法都运行良好,但是在启动线程之后它不会进入方面。

我正在使用实现接口的JDK动态代理对象, 从其他线程调用的所有公共方法,而不是从对象本身调用。

我正在使用spring 3.0.6。

请帮助我理解我所缺少的东西。

方面:

@Aspect 
public class CabLoggingAspect {

    public void init() {
        System.out.println("CabLoggingAspect: init()");
    }

    @Pointcut("execution(* com.station.taxi.ICab.*(..))")
    public void anyCall() { }

    @Before("anyCall()")
    public void logAnyCall(JoinPoint joinPoint) {
        System.out.println("CabLoggingAspect: logAnyCall(): "+joinPoint.getSignature().getName());
    }
}

Spring配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

    <bean id="cab" class="com.station.taxi.Cab" scope="prototype" />    
    <aop:aspectj-autoproxy>
        <aop:include name="cabLoggingAspect"/>
    </aop:aspectj-autoproxy>
    <!-- AspectJ -->
    <bean id="cabLoggingAspect" class="com.station.taxi.aop.CabLoggingAspect" init-method="init"/>
</beans>

创建bean:

public ICab createCab(int num, String whileWaiting) {
    Object o = mApplicationContext.getBean("cab", num, whileWaiting);
    return (ICab)o;
}

运行线程:

// public interface ICab extends Runnable { ...
// ... 
// initCabs.add(mContext.createCab(Integer.valueOf(cabNum), whileWaiting));
// ...
for(ICab cab: initCabs) {
    cab.setMeter(createTaxiMeter());
    cab.setStationEventListener(this);
    mInitThreads.add(cab);
}
// ...
for(Runnable t: mInitThreads) {
    new Thread(t).start();
}

输出:

CabLoggingAspect: init()
CabLoggingAspect: logAnyCall(): setMeter
CabLoggingAspect: logAnyCall(): setStationEventListener
CabLoggingAspect: logAnyCall(): setMeter
CabLoggingAspect: logAnyCall(): setStationEventListener
CabLoggingAspect: logAnyCall(): run
CabLoggingAspect: logAnyCall(): run
调用了

run()函数,方面没有打印任何其他内容。我正在修改现有项目,所有线程都在运行,我可以看到线程的输出,与方面的实现无关

15/07/2012 23:19:05 Usjy - Passanger is ready and running...
15/07/2012 23:19:05 Usjy - Took cab starting transit
15/07/2012 23:19:06 MCMk - Passanger is ready and running...
15/07/2012 23:19:06 MCMk - Took cab starting transit
15/07/2012 23:19:08 Usjy - Arrived at TelAviv paid 5.8
15/07/2012 23:19:10 MCMk - Arrived at TelAviv paid 6.3

更新/溶液

问题

如Biju Kunjummen所述。当线程运行时,它会通知回调用者并传递自我实例。所有后续调用都在此实例上直接绕过代理执行。

公共类CabImpl实现ICab {        @覆盖        public void run(){
          mStationListener.onCabReady(本);        }    }

解决方案

因为我有多个地方用事件传递原始对象的实例,所以快速解决方案是存储对实例内代理对象的引用并发送它。但更正确的解决方案是使用AspectJ,这将说明不必要的代码修改和依赖。

cab对象内的修改

private ICab mAopProxy;
//...
public void setAopProxy(ICab proxy) {
    mAopProxy = proxy;
}

@Override
public void run() {
    mStationListener.onCabReady(mAopProxy);
    //...
}

创建cab

Advised advised = (Advised)mApplicationContext.getBean("cab", num, whileWaiting);
Cab cab = (Cab) advised.getTargetSource().getTarget();
cab.setAopProxy((ICab)advised);

1 个答案:

答案 0 :(得分:1)

我看到你的Cab实现了Runnable接口,你正在用Cab创建一个线程并启动线程。

我假设Cab中的run方法正在调用Cab公开的其他方法,如果是这种情况那么它会完全绕过动态代理,你会看到这种行为。

例如。如果客户端(而非Cab)调用setMeter,则调用流程为:

client->CabDynamicProxy.setMeter()->cab.setMeter();

现在考虑一下Cab本身通过另一种方法调用setMeter的情况(在这种情况下是run()):

Thread.start()->CabDynamicProxy.run()->cab.run()->cab.setMeter();

来自cab.run()的this将不会解析为动态代理,而是解析为实际的cab本身,从而完全绕过方面。

有一些解决方法:

  1. 而不是动态代理使用完整的AspectJ - 然后编织将 直接在您的驾驶室代码中,而不是围绕它的代理。
  2. 不是从cab的run()方法中调用this.setMeter(),而是在某处保留对代理的引用,并调用proxy.setMeter()。