
# AOP

AOP (aspect-oriented programming) lets you run your own code before, after or around the methods of other beans, without editing those beans.

## Why you need it

Some concerns touch many classes at once: logging, timing, auditing, permission checks, retries. Writing that code inside every method repeats it and mixes it with the real work. With AOP you write it once, in an **aspect**, and say with a **pointcut** which methods it applies to.

You do not need AOP for code that belongs to one class only. Put that code in the class. AOP is also not free: an advised bean is replaced by a generated proxy, and calls a bean makes to itself skip the advice (see [Proxies](proxies.md)).

## The words

| Term | What it is | In code |
|---|---|---|
| Aspect | A bean that holds the extra code | a class with `@Aspect()` and `@Component()` |
| Advice | One piece of extra code, and when it runs | a method with `@Before`, `@After`, `@AfterReturning`, `@AfterThrowing` or `@Around` |
| Pointcut | Which methods the advice applies to | the string in the annotation, such as `'@annotation(Timed)'` |
| Join point | One intercepted call | the `JoinPoint` or `ProceedingJoinPoint` parameter |
| Proxy | The generated wrapper that calls the advice | written by the generator, injected instead of the bean |

## Setup

AOP is part of `spring_core_dart`. Import `package:spring_core_dart/aop.dart`, or `package:spring_core_dart/spring_core_dart.dart`, which exports it. Dart has no runtime reflection, so the proxies are written at build time by `spring_core_dart_generator`. Your package must depend on it and run `build_runner`.

```yaml
dependencies:
  spring_core_dart: ^0.7.1

dev_dependencies:
  build_runner: ^2.17.0
  spring_core_dart_generator: ^0.7.1
```

```bash
dart run build_runner build
```

## A short example

Time every method marked `@Timed()`.

```dart
// 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'];
}
```

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

No extra switch is needed. The context turns AOP on as soon as it finds an `@Aspect` or an `Advisor` bean.

## Pages in this section

- [@Aspect](aspect.md) - declare a bean that holds advice; ordering and per-instance aspects.
- [@Pointcut and pointcut expressions](pointcut.md) - name a pointcut and the expression syntax that selects methods.
- [Advice](advice.md) - `@Before`, `@After`, `@AfterReturning`, `@AfterThrowing` and `@Around`.
- [@DeclareParents](declare-parents.md) - make the proxies of matching beans implement an extra interface.
- [JoinPoint](join-point.md) - what advice can read about the intercepted call, and `MethodInterceptor`.
- [Proxies](proxies.md) - how proxies are made, self-calls, advisors in code, and the limits.

## In Spring (Java)

```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");
    }
  }
}
```

Differences that matter:

- Spring creates proxies at runtime (JDK dynamic proxies or CGLIB). Here the generator writes the proxy classes at build time, so `build_runner` must run after every change.
- There is no `@EnableAspectJAutoProxy`. An aspect or advisor bean is enough.
- Type names in pointcuts are simple names (`Timed`, not `com.example.Timed`). Dart has no packages.
- Dart getters and setters, including the implicit ones of public fields, can be advised too.

## See also

- [Beans](../beans/index.md)
- [@Component](../beans/component.md)
- [@Bean](../beans/bean.md)
- [ApplicationContext](../beans/application-context.md)
