web-dev-qa-db-ja.com

別のメソッド内のメソッド呼び出しでSpring AOPが機能しない

ABC.Javaには2つのメソッドが定義されています

public void method1(){
   .........
   method2();
  ...........
}


public void method2(){
  ...............
  ...............  
}

method2の呼び出しでAOPが欲しいので、1つのクラスを作成しました、AOPLogger.Java、メソッドで提供されるアスペクト機能を持つcheckAccess
設定ファイルで、次のようなことをしました

<bean id="advice" class="p.AOPLogger" />
<aop:config>
  <aop:pointcut id="abc" expression="execution(*p.ABC.method2(..))" />
  <aop:aspect id="service" ref="advice">
    <aop:before pointcut-ref="abc" method="checkAccess" />          
  </aop:aspect>
</aop:config>

しかし、method2が呼び出されると、AOP機能は呼び出されません。つまり、checkAccessメソッドはAOPLoggerクラスから呼び出されません。

不足していることはありますか?

47
Anand

アスペクトは、Beanを囲むproxyに適用されます。 Beanへの参照を取得するたびに、実際に設定で参照されるクラスではなく、関連するインターフェースを実装し、実際のクラスに委任し、AOPなどの機能を追加する合成クラスであることに注意してください。

上記の例では、クラスでを直接呼び出しています。一方、そのクラスインスタンスがSpring Beanとして別のインスタンスに注入される場合、プロキシとして注入されます。 、したがって、メソッド呼び出しがプロキシ上で呼び出されます(アスペクトがトリガーされます)

上記を達成したい場合は、method1/method2別のBeanに入れるか、非スプリング指向のAOPフレームワークを使用します。

Spring doc(セクション "AOPプロキシについて") これと、いくつかの回避策(上記の最初の提案を含む)

62
Brian Agnew

それは自己注入の使用によって行うことができます。インジェクトされたインスタンスを通じて内部メソッドを呼び出すことができます:

@Component
public class Foo {
    @Resource
    private Foo foo;

    public void method1(){
        ..
        foo.method2();
        ..
    }
    public void method2(){
        ..
    }
}

Spring 4.3以降では、@ Autowiredを使用して行うこともできます。

4.3の時点で、@ Autowiredはインジェクションの自己参照、つまり現在インジェクションされているBeanへの参照も考慮します。

33

私は同じ種類の問題を抱えていたので、SpringのApplicationContextAwareBeanNameAwareを実装し、以下のように対応するメソッドを実装することで克服しました。

_class ABC implements ApplicationContextAware,BeanNameAware{

      @Override
      public void setApplicationContext(ApplicationContext ac) throws BeansException {
          applicationContext=ac;
      }

      @Override
      public void setBeanName(String beanName) {
          this.beanName=beanName;
      }
      private ApplicationContext applicationContext;
      private String beanName;
}
_

次に、同じクラスのメソッドを呼び出しているときに_this._を_((ABC) applicationContext.getBean(beanName))._に置き換えました。これにより、同じクラスのメソッドの呼び出しがプロキシのみを介して確実に行われます。

したがって、method1()

_ public void method1(){
    .........
    ((ABC) applicationContext.getBean(beanName)).method2();
    ...........
  }
_

お役に立てれば。

5
pavan

Spring AOPフレームワークは「プロキシ」ベースであり、ここで非常によく説明されています: http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/aop.html# aop-understanding-aop-proxies

Springがアスペクト(この例では「ABC」など)で構成されたBeanを構築するとき、実際のBeanのように動作する「プロキシ」オブジェクトを実際に作成します。プロキシは単に呼び出しを「実際の」オブジェクトに委任しますが、この間接を作成することにより、プロキシは「アドバイス」を実装する機会を得ます。たとえば、アドバイスはメソッド呼び出しごとにメッセージを記録できます。このスキームでは、実際のオブジェクトのメソッド( "method1")が同じオブジェクトの他のメソッド(たとえばmethod2)を呼び出す場合、それらの呼び出しは画像内でプロキシなしで行われるため、アドバイスを実装する機会はありません。

あなたの例では、method1()が呼び出されると、プロキシは本来行うべきことを行う機会を得ますが、method1()がmethod2()を呼び出す場合、画像にはアスペクトはありません。ただし、me​​thod2が他のBeanから呼び出された場合、プロキシはアドバイスを実行できます。

お役に立てれば。

ありがとう、ラグー

3
Raghuram

_@Autowired_を使用すると動作します。内部メソッドをthis.method()として呼び出す代わりに、次のことができます。

_@Autowired
Foo foo;
_

そして呼び出し:

_foo.method2();
_
2
Rashmingadhvi

これに誰も言及していないことに驚いていますが、Springが提供するControlFlowPointcutを使用できると思います。

ControlFlowPointcutはスタックトレースを調べ、スタックトレースで特定のメソッドを見つけた場合にのみポイントカットと一致します。基本的にポイントカットは、特定のコンテキストでメソッドが呼び出された場合にのみ一致します。

この場合、次のようなポイントカットを作成できます

ControlFlowPointcut cf = new ControlFlowPointcut(MyClass.class, "method1");

現在、ProxyFactoryを使用してMyClassインスタンスにプロキシを作成し、method1()を呼び出します。

上記の場合、method1()から呼び出されるため、method2()のみが推奨されます。

0
Arjun Patil

別の方法は、method2()を他のClass Fileにモード設定し、そのクラスに@Componentアノテーションを付けます。次に、必要なときに@Autowiredを使用してそれを挿入します。このようにして、AOPはそれをインターセプトできます。

例:

You were doing this...


Class demo{
   method1(){
    -------
    -------
    method2();
    -------
    -------
   }

   method2(){
    ------
    -----
   }
}

Now if possible do this :

@Component
class NewClass{
    method2(){
    ------
    -----
   }
}


Class demo{

 @AutoWired
 NewClass newClass;

   method1(){
    -------
    -------
    newClass.method2();
    -------
    -------
   }

}
0

あなたが達成したいことは不可能です。説明は Spring Reference Documentation にあります。

0
Uwe Plonus

この方法で自己注入を行うと、Springアプリケーションの外部でこのクラスを使用できます。

@Component
public class ABC {

    @Resource
    private ABC self = this;

    public void method1() {
        self.method2();
    }

    public void method2() {

    }

}
0
Fai

@EnableAspectJAutoProxy(exposeProxy = true)で呼び出しに注釈を付け、((Class)AopContext.currentProxy())。method();でインスタンスメソッドを呼び出します

カップリングに増加するため、これは厳密には推奨されません

0
shiva kumar

Spring docs 、5.6.1章AOPプロキシの理解に示されているように、別の方法があります。

public class SimplePojo implements Pojo {

    public void foo() {
        // this works, but... gah!
        ((Pojo) AopContext.currentProxy()).bar();
    }

    public void bar() {
        // some logic...
    }
}

著者はこの方法を推奨していませんが。なぜなら:

これにより、コードがSpring AOPに完全に結合され、クラス自体が、AOPに直面して飛行するAOPコンテキストで使用されているという事実を認識します。また、プロキシを作成するときに追加の構成が必要です。

0
Qianyue