Spring AOP aspect not triggered in thread

为君一笑 提交于 2021-02-08 04:23:09

问题


Every method that called before thread is running, works good, but after starting a thread It doesn't enters aspect.

I'm using JDK dynamic proxy objects that implements interface, all public methods called from other thread and not from object itself.

I'm using spring 3.0.6.

Please help me to understand what I'm missing.

Aspect:

@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 Configuration:

<?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>

Creation of bean:

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

Running threads:

// 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();
}

Output:

CabLoggingAspect: init()
CabLoggingAspect: logAnyCall(): setMeter
CabLoggingAspect: logAnyCall(): setStationEventListener
CabLoggingAspect: logAnyCall(): setMeter
CabLoggingAspect: logAnyCall(): setStationEventListener
CabLoggingAspect: logAnyCall(): run
CabLoggingAspect: logAnyCall(): run

run() function called and nothing else printed by aspect. I'm modifying existing project, all thread are running and I can see output from threads, not related to implementation of aspect

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

Update/Solution

Problem

As described by Biju Kunjummen in his answer. When thread is running it notifies back to the caller and pass self instance. All following calls were executed on this instance directly bypassing proxy.

public class CabImpl implements ICab { @Override public void run() {
mStationListener.onCabReady(this); } }

Solution

Because I have multiple places where I pass the instance of the original object with event, the quick solution was to store reference to the proxy object inside instance and send it. But more correct solution is to use AspectJ, this will illuminate unnecessary code modification and dependencies.

Modifications inside cab object

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

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

Creation of cab

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

回答1:


I see that your Cab implements Runnable interface and you are creating a thread with Cab and starting up the thread.

I am assuming the run method in Cab is calling the other methods exposed by Cab, if that is the case then it would bypass the dynamic proxy completely and you would see this behavior.

For eg. if a client(not Cab) calls setMeter, then the call flow would be:

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

Now consider the case of Cab itself calling setMeter through another of its methods(run() in this case):

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

The this from cab.run() would resolve not to the dynamic proxy but to the actual cab itself and hence bypass aspects completely.

There are a few workarounds:

  1. Instead of Dynamic proxies use full AspectJ - then the weaving will be directly in your Cab code instead of a proxy around it.
  2. Instead of calling this.setMeter() from inside the run() method of cab, keep a reference to the proxy somewhere, and call proxy.setMeter().


来源:https://stackoverflow.com/questions/11495320/spring-aop-aspect-not-triggered-in-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!