2017-10-02 7 views
1

私が行う必要があるのは、インターセプタを使用するとかなりシンプルですが、アノテーションに基づいたより洗練されたソリューションを本当に望んでいました。私の "解決策"は実際には機能しませんし、私はなぜそれがわからないということです。多分、これは可能ではない。抽象コントローラを拡張するコントローラ内のメソッドのカスタム注釈

私の基本的なスタックは、次のとおりです。 春ブーツ1.4.1:

  • 春ブート・スターター・ウェブ
  • 春ブート・スターター・AOP
  • 春ブート・スターター-JDBC
  • ばねブートスタータキャッシュ

ばね豆4.3.4 および他のユーティリティおよび試験瓶種々。

私は抽象コントローラを拡張するいくつかのコントローラを持っています。この抽象コントローラは接続を準備する必要があり、次に各コントローラはacquire()メソッドに配置された独自の実装を使用しています。時々、いくつかのcronジョブがこのエンドポイントに当たっています。いくつかのコントローラ/ジョブについて監査を行いたいが、必ずしも全てを監査する必要はない。 私は、監査を行うべき場所にカスタムアノテーションを追加することを考えていました。監査必要

public abstract class ImportController { 
    @RequestMapping(value = "/checkout", method = RequestMethod.GET, produces = "application/json") 
    public String importEntities() { 
     //some code here .... 
     MyResult result = acquire(param); 
     //some code again .... 
    } 

    public abstract MyResult acquire(MyParam param) 
} 

実装:監査は必要ありません

@RestController 
@RequestMapping(value = "/cars") 
public class CarsImportController extends ImportController { 

    @Override 
    @MyJobAudit // <--- this should add a pointcut used for Audit logging 
    public MyResult acquire(MyParam param) { 
      //cars specific code 
    } 
} 

実装を

@RestController 
@RequestMapping(value = "/tomatoes") 
public class TomatoesImportController extends ImportController { 

    @Override 
    //no audit annotation 
    public MyResult acquire(MyParam param) { 
      //tomatoes specific code 
    } 
} 

マイJobAudit注釈:

@Retention(RetentionPolicy.RUNTIME) 
    public @interface MyJobAudit { 
} 

と側面クラス:

@Aspect 
@Component 
public class SystemAspectArchitecture { 
    @Pointcut("@annotation(MyJobAudit)") 
    public void auditableJob() { 
    } 
} 

注釈をさまざまなサービスクラスに入れようとしましたが、機能します。しかし、で取得していないメソッド。ここには何か間違いがあります。私は何を把握することができません...

+0

'acquire()'メソッドが本当に 'public'であることを確認できますか? Springのアスペクトはパブリックメソッドでのみ機能するためです。 –

+0

はい、公開です – Buzzo

答えて

2

問題はあなたの勧告コードの呼び出しにあります。あなたは以下のコードが定義されている、以下を参照してください。

@GetMappein(value = "/checkout") 
public String getCheckout() { 
    //some code here .... 
    MyResult result = acquire(param); //Uh oh!!! 
    //some code again .... 
} 

しかし、問題は、お勧めのコードは、春には(あなたが見ることはないコントローラの場合とする)あなたのために作成したプロキシで定義されているということですが、あなたの呼び出し上記のacquire(param)メソッドの実装は、Springプロキシではなく、具体的なクラスに直接行われます。すなわち、this.acquire(param)と同じですが、コードはthisのプロキシでアドバイスされ、this(具体的なオブジェクト)。

問題を解決する方法は、現在のプロキシにアクセスすることです。私は以下のように解決しました。

まず、アプリケーションでexpose-proxyを有効にします。

@SpringBootApplication 
@EnableAspectJAutoProxy(exposeProxy = true) 
public class MyApplication { 

    public static void main(String[] args) { 
     SpringApplication.run(MyApplication, args); 
    } 
} 

次に、あなたが助言されることになっているメソッドの呼び出しをするつもり具体的なクラスで、あなたは次のようにします。

@RestController 
public class ConcreteController { 

    @GetMapping("/checkout") 
    public String getSomething() { 
     Object proxy = AopContext.currentProxy(); 
     return ((ConcreteController) proxy).acquire("Luke Skywalker"); 
    } 

    @Auditable 
    public String acquire(Object param) { 
     return "Hello World, " + param; 
    } 

} 

AopContext.currentProxy()はあなたにアクセスできるようになりますacquire(params)のアドバイスが実際に定義されているthisコントローラのプロキシです。それは期待どおりに動作します。

私は、これを回避する唯一の方法は、真のAOPとSpringプロキシだけではないことを理解します。真のAOPを使用する場合、コンパイル時またはロード時にコードにアドバイスを与える、ある形式のコードを作る必要があります。そのようにして、コードは、ダムの春のプロキシだけでなく、具体的なクラスに直接アドバイスされます。真のAOP this.acquire()を使用すると、コンパイル時またはロード時に計装を介して通知されます。しかし、あなたが唯一のSpringプロキシを使用している場合は、アドバイスされたクラス内でダイレクトメソッド呼び出しのようなことを直接行うことはできません。そのたびにプロキシを通過するようにする必要があります。

+0

エドウィンに感謝します!間違いなく、Springが行う内側と隠されたトリックについてもっと学び、「プロキシー」もその1つです。 – Buzzo

関連する問題