2

スレッドが実行される前に呼び出されたすべてのメソッドは正常に機能しますが、スレッドを開始した後はアスペクトに入りません。

インターフェイスを実装するJDK動的プロキシオブジェクトを使用しています。すべてのパブリックメソッドは、オブジェクト自体からではなく、他のスレッドから呼び出されます。

スプリング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());
    }
}

春の構成:

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

更新/解決策

問題

BijuKunjummenが彼の答えで説明したように。スレッドが実行されているときは、呼び出し元に通知してセルフインスタンスを渡します。以下のすべての呼び出しは、プロキシを直接バイパスしてこのインスタンスで実行されました。

パブリッククラスCabImplはICabを実装します{@Overridepublicvoid run(){
mStationListener.onCabReady(this); }}

解決

元のオブジェクトのインスタンスをイベントとともに渡す場所が複数あるため、簡単な解決策は、インスタンス内にプロキシオブジェクトへの参照を格納して送信することでした。しかし、より正しい解決策はAspectJを使用することです。これにより、不要なコードの変更と依存関係が明らかになります。

キャブオブジェクト内の変更

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

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

キャブの作成

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

1 に答える 1

2

CabがRunnableインターフェースを実装していて、Cabでスレッドを作成し、スレッドを起動しているようです。

Cabのrunメソッドが、Cabによって公開されている他のメソッドを呼び出していると想定しています。その場合、動的プロキシが完全にバイパスされ、この動作が見られます。

たとえば。クライアント(Cabではない)がsetMeterを呼び出す場合、呼び出しフローは次のようになります。

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

ここで、Cab自体が別のメソッド(この場合はrun())を介してsetMeterを呼び出す場合を考えてみましょう。

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

thisfrom cab.run()は、動的プロキシではなく実際のcab自体に解決されるため、アスペクトを完全にバイパスします。

いくつかの回避策があります。

  1. 動的プロキシの代わりに完全なAspectJを使用します。その場合、ウィービングは、その周りのプロキシではなく、Cabコードに直接含まれます。
  2. cabのrun()メソッド内からthis.setMeter()を呼び出す代わりに、プロキシへの参照をどこかに保持し、proxy.setMeter()を呼び出します。
于 2012-07-15T22:26:16.913 に答える