AOP

AOP(アスペクト指向プログラミング)は、他の Bean のメソッドの前、後、または周囲で独自のコードを実行する仕組みです。対象の Bean を編集する必要はありません。

なぜ必要か

ログ出力、処理時間の計測、監査、権限チェック、リトライなど、多くのクラスに同時に関わる関心事があります。これらをすべてのメソッドに書くと、同じコードが繰り返され、本来の処理と混ざります。AOP では、そのコードを アスペクト に一度だけ書き、どのメソッドに適用するかを ポイントカット で指定します。

1 つのクラスにしか関係しないコードには AOP は不要です。そのクラスに書いてください。また AOP にはコストもあります。アドバイスが適用された Bean は生成されたプロキシに置き換えられ、Bean が自分自身を呼び出す場合はアドバイスが実行されません(プロキシ を参照)。

用語

用語 意味 コード上の形
アスペクト 追加のコードを持つ Bean @Aspect() と @Component() を付けたクラス
アドバイス 追加のコード 1 つと、それが実行されるタイミング @Before、@After、@AfterReturning、@AfterThrowing、@Around を付けたメソッド
ポイントカット アドバイスを適用するメソッド アノテーションに渡す文字列。例: '@annotation(Timed)'
ジョインポイント 横取りされた 1 回の呼び出し JoinPoint または ProceedingJoinPoint パラメータ
プロキシ アドバイスを呼び出す生成済みのラッパー ジェネレーターが書き出し、Bean の代わりに注入される

セットアップ

AOP は spring_core_dart に含まれています。package:spring_core_dart/aop.dart、またはそれをエクスポートしている package:spring_core_dart/spring_core_dart.dart をインポートします。Dart には実行時リフレクションがないため、プロキシはビルド時に spring_core_dart_generator が書き出します。パッケージはこれに依存し、build_runner を実行する必要があります。

dependencies:
  spring_core_dart: ^0.7.1

dev_dependencies:
  build_runner: ^2.17.0
  spring_core_dart_generator: ^0.7.1
dart run build_runner build

短い例

@Timed() を付けたすべてのメソッドの処理時間を計測します。

// lib/src/beans.dart
import 'package:spring_core_dart/spring_core_dart.dart';
import 'package:my_app/spring_gen/src/beans.spring.dart'; // written by the generator

// The module: every class in this directory and below is scanned.
@ComponentScan()
final BeanModule appBeans = $appBeans;

// An annotation is a plain class with a const constructor.
class Timed {
  const Timed();
}

// The aspect: a bean whose @Around method runs instead of each
// method marked @Timed().
@Aspect()
@Component()
@Log()
class TimingAspect {
  @Around('@annotation(Timed)')
  Object? time(ProceedingJoinPoint call) {
    final watch = Stopwatch()..start();
    try {
      return call.proceed(); // runs the real method
    } finally {
      log.info('${call.signature.name}: ${watch.elapsedMilliseconds} ms');
    }
  }
}

// An ordinary bean. It does not know about the aspect.
@Service()
class Search {
  @Timed()
  List<String> find(String text) => ['dart', 'spring'];
}
// bin/main.dart
import 'package:my_app/src/beans.dart';
import 'package:spring_core_dart/spring_core_dart.dart';

void main() {
  final context = ApplicationContext.run([appBeans]);
  // getBean returns the proxy, so the advice runs and logs "find: 0 ms".
  context.getBean<Search>().find('d');
  context.close();
}

追加の設定は不要です。コンテキストは @Aspect または Advisor Bean を見つけた時点で AOP を有効にします。

このセクションのページ

Spring (Java) では

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
class AppConfig {}

@Aspect
@Component
class TimingAspect {
  @Around("@annotation(com.example.Timed)")
  Object time(ProceedingJoinPoint call) throws Throwable {
    long start = System.nanoTime();
    try {
      return call.proceed();
    } finally {
      log.info(call.getSignature().getName() + ": "
          + (System.nanoTime() - start) / 1_000_000 + " ms");
    }
  }
}

重要な違い:

関連項目