Zum Hauptinhalt springen

Woche 8, Tag 1 — Spring AOP Mental Model: Proxies, Aspects, Advice, Pointcuts und Self-Invocation

Ziel

Heute willst du Spring AOP verstehen.

Die Kernfragen:

  1. Was ist AOP?
  2. Welches Problem löst AOP?
  3. Was ist ein querschnittliches Anliegen?
  4. Was ist ein Aspekt?
  5. Was ist Advice?
  6. Was ist ein Pointcut?
  7. Was ist ein Join Point?
  8. Warum ist Spring AOP proxy-basiert?
  9. Was ist der Unterschied zwischen JDK-Proxy und CGLIB-Proxy?
  10. Warum funktioniert Self-Invocation nicht?
  11. Wie hängt AOP mit @Transactional, Security und Caching zusammen?
  12. Was sind häufige Prüfungsfallen?

1. Warum AOP lernen?

AOP bedeutet:


Aspect-Oriented Programming

Spring nutzt AOP intern für viele wichtige Features.

Beispiele:


@Transactional
@PreAuthorize
@Cacheable
@Async
eigene Logging-Aspekte
eigenes Performance-Monitoring
eigenes Auditing

Wenn du AOP verstehst, verstehst du, warum diese Features funktionieren — und warum sie manchmal nicht funktionieren.

Merksatz:

AOP erklärt viele „Spring-Magie“-Features.


2. Das Problem, das AOP löst

Stell dir vor, du hast viele Service-Methoden:


public TaskDto createTask(CreateTaskRequest request) {
log.info("Creating task");
long start = System.currentTimeMillis();

try {
// business logic
return result;
} finally {
long duration = System.currentTimeMillis() - start;
log.info("createTask took {} ms", duration);
}
}

Dann noch eine weitere Methode:


public ClientDto createClient(CreateClientRequest request) {
log.info("Creating client");
long start = System.currentTimeMillis();

try {
// business logic
return result;
} finally {
long duration = System.currentTimeMillis() - start;
log.info("createClient took {} ms", duration);
}
}

Problem:


dieselbe Logging-Logik überall wiederholt
dieselbe Timing-Logik überall wiederholt
dieselbe Security-Logik überall wiederholt
dieselbe Transaktions-Logik überall wiederholt

Das macht den Code unübersichtlich.

AOP hilft, wiederholte technische Logik aus den Business-Methoden herauszuziehen.

Merksatz:

AOP trennt wiederholte technische Anliegen von der Business-Logik.


3. Querschnittliches Anliegen

Ein querschnittliches Anliegen ist Logik, die in vielen Teilen der Anwendung vorkommt.

Beispiele:


Logging
Transaktionen
Security
Caching
Metriken
Auditing
Retry-Logik
Performance-Timing
Exception-Tracking

Diese Anliegen „schneiden quer“ durch viele Klassen.

Business-Logik:


Task erstellen
Rechnung bezahlen
Benutzer registrieren
Dokument freigeben
Report senden

Querschnittliche Logik:


Methodenaufruf loggen
Transaktion starten
Berechtigung prüfen
Ergebnis cachen
Ausführungszeit messen

Merksatz:

Querschnittliche Anliegen sind wiederholte technische Anliegen über viele Klassen hinweg.


4. Die einfache Idee von AOP

Ohne AOP:


Business-Methode enthält Business-Logik + Logging + Transaktionen + Security.

Mit AOP:


Business-Methode enthält Business-Logik.
Aspekt enthält Logging / Transaktionen / Security.
Spring wendet den Aspekt um passende Methoden herum an.

Einfaches Bild:


Client

Spring-AOP-Proxy

Before-Advice

Zielmethode

After-Advice

Ergebnis zurückgeben

Merksatz:

AOP lässt zusätzliches Verhalten vor, nach oder um Business-Methoden herum laufen.


5. Wichtiges AOP-Vokabular

BegriffBedeutung
AspektKlasse mit querschnittlicher Logik
AdviceCode, der an einem passenden Punkt läuft
PointcutAusdruck, der festlegt, wo Advice gilt
Join PointPunkt in der Programmausführung, meist Methodenausführung in Spring AOP
Zielobjektdas echte Objekt, das geproxied wird
ProxyWrapper-Objekt, das Spring erstellt
WeavingAnwenden von Aspekten auf den Zielcode

Merksatz:

Der Aspekt enthält Advice; der Pointcut entscheidet, wo er läuft.


6. Aspekt

Ein Aspekt ist eine Klasse, die querschnittliche Logik enthält.

Beispiel:


@Aspect
@Component
public class LoggingAspect {
}

@Aspect bedeutet:


Diese Klasse definiert AOP-Regeln.

@Component bedeutet:


Spring soll diesen Aspekt als Bean verwalten.

Merksatz:

Ein Aspekt ist eine Spring-Bean mit AOP-Logik.


7. Advice

Advice ist der Code, der läuft.

Gängige Advice-Typen:


@Before
@After
@AfterReturning
@AfterThrowing
@Around

Beispiel:


@Before("execution(* com.example.task.TaskService.*(..))")
public void logBeforeMethod() {
System.out.println("Method is about to run");
}

Dieses Advice läuft vor passenden Methoden.

Merksatz:

Advice ist die Aktion, die ein Aspekt ausführt.


8. Pointcut

Ein Pointcut entscheidet, wo Advice gilt.

Beispiel:


execution(* com.example.task.TaskService.*(..))

Bedeutung:


alle Methoden in TaskService matchen

Advice + Pointcut:


@Before("execution(* com.example.task.TaskService.*(..))")
public void logBeforeMethod() {
System.out.println("Before TaskService method");
}

Merksatz:

Der Pointcut wählt Methoden aus; dort läuft das Advice.


9. Join Point

Ein Join Point ist ein Punkt in der Programmausführung, an dem Advice laufen kann.

In Spring AOP ist der wichtigste Join Point:


Methodenausführung auf Spring-Beans

Beispiel:


taskService.createTask(request);

Diese Methodenausführung kann von Spring AOP abgefangen werden.

Merksatz:

In Spring AOP sind Join Points vor allem Methodenausführungen auf Spring-Beans.


10. Erster einfacher Aspekt


@Aspect
@Component
public class LoggingAspect {

@Before("execution(* com.example.task.TaskService.*(..))")
public void beforeTaskServiceMethod() {
System.out.println("A TaskService method is about to run");
}
}

Wenn dieser Service existiert:


@Service
public class TaskService {

public TaskDto createTask(CreateTaskRequest request) {
return new TaskDto(1L, request.title());
}
}

Beim Aufruf:


taskService.createTask(request);

kann Spring ausführen:


A TaskService method is about to run

bevor die echte Methode läuft.

Merksatz:

Ein einfacher Aspekt kann Logik vor Service-Methoden ausführen.


11. @Before

@Before-Advice läuft vor der Methode.

Beispiel:


@Before("execution(* com.example..*Service.*(..))")
public void logBefore() {
System.out.println("Before service method");
}

Gut für:


Methodeneinstieg loggen
einfache Bedingungen prüfen
Versuche auditieren

Aber @Before kann den Rückgabewert nicht ändern.

Merksatz:

@Before läuft vor der Methode und kann den Rückgabewert nicht steuern.


12. @After

@After-Advice läuft, nachdem die Methode beendet ist.

Es läuft sowohl bei Erfolg als auch bei einer Exception.

Beispiel:


@After("execution(* com.example..*Service.*(..))")
public void logAfter() {
System.out.println("After service method");
}

Ähnlich wie:


finally {
}

Merksatz:

@After läuft nach Methodenende — bei Erfolg oder Exception.


13. @AfterReturning

@AfterReturning läuft nur, wenn die Methode erfolgreich zurückkehrt.

Beispiel:


@AfterReturning(
pointcut = "execution(* com.example..*Service.*(..))",
returning = "result"
)
public void logAfterReturning(Object result) {
System.out.println("Method returned: " + result);
}

Gut für:


erfolgreiches Ergebnis loggen
erfolgreiche Aktionen auditieren
Metriken nach Erfolg

Merksatz:

@AfterReturning läuft nur bei erfolgreichem Return.


14. @AfterThrowing

@AfterThrowing läuft nur, wenn die Methode eine Exception wirft.

Beispiel:


@AfterThrowing(
pointcut = "execution(* com.example..*Service.*(..))",
throwing = "exception"
)
public void logException(Throwable exception) {
System.out.println("Method failed: " + exception.getMessage());
}

Gut für:


Fehler-Logging
Exception-Metriken
Fehler-Auditing

Merksatz:

@AfterThrowing läuft nur, wenn die Methode eine Exception wirft.


15. @Around

@Around ist der mächtigste Advice-Typ.

Er kann:


vor der Methode laufen
nach der Methode laufen
entscheiden, ob die Methode läuft
Dauer messen
Argumente ändern
Rückgabewert ändern
Exceptions fangen
Exceptions erneut werfen

Beispiel:


@Around("execution(* com.example..*Service.*(..))")
public Object measureTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();

try {
return joinPoint.proceed();
} finally {
long duration = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " took " + duration + " ms");
}
}

Wichtig:


joinPoint.proceed()

ruft die echte Zielmethode auf.

Merksatz:

@Around steuert die Methodenausführung und muss proceed() aufrufen, um fortzufahren.


16. ProceedingJoinPoint

ProceedingJoinPoint wird mit @Around verwendet.

Er gibt Zugriff auf:


Methodensignatur
Methodenargumente
Zielobjekt
proceed()

Beispiel:


@Around("execution(* com.example..*Service.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Method: " + joinPoint.getSignature().getName());
System.out.println("Args: " + Arrays.toString(joinPoint.getArgs()));

return joinPoint.proceed();
}

Merksatz:

ProceedingJoinPoint lässt @Around-Advice zur echten Methode weiterlaufen.


17. Wichtige @Around-Falle

Schlecht:


@Around("execution(* com.example..*Service.*(..))")
public Object badAround(ProceedingJoinPoint joinPoint) {
System.out.println("Before");
return null;
}

Problem:


joinPoint.proceed() wird nicht aufgerufen
die echte Methode läuft nie
null wird zurückgegeben

Korrekt:


@Around("execution(* com.example..*Service.*(..))")
public Object goodAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before");

Object result = joinPoint.proceed();

System.out.println("After");

return result;
}

Merksatz:

Bei @Around überspringt ein vergessenes proceed() die echte Methode.


18. Pointcut-Ausdruck: execution

Der häufigste Spring-AOP-Pointcut-Designator ist:


execution

Beispiel:


execution(* com.example.task.TaskService.createTask(..))

Bedeutung:


createTask-Methode in TaskService matchen

Allgemeine Form:


execution(modifiers-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(parameters-pattern) throws-pattern?)

Keine Panik.

Für Prüfung und Alltag reichen die gängigen Muster.

Merksatz:

execution(...) matcht Methodenausführungen.


19. Gängige Pointcut-Muster

Eine Methode matchen:


execution(* com.example.task.TaskService.createTask(..))

Alle Methoden einer Klasse matchen:


execution(* com.example.task.TaskService.*(..))

Alle Methoden in einem Service-Paket matchen:


execution(* com.example.service.*.*(..))

Alle Methoden in einem Service-Paket und Unterpaketen matchen:


execution(* com.example.service..*.*(..))

Alle Methoden matchen, die auf Service enden:


execution(* com.example..*Service.*(..))

Merksatz:

* bedeutet beliebig; .. bedeutet je nach Position beliebiges Unterpaket oder beliebige Parameter.


20. Pointcut mit Annotation

Du kannst Methoden matchen, die mit einer eigenen Annotation versehen sind.

Eigene Annotation:


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}

Aspekt:


@Aspect
@Component
public class ExecutionTimeAspect {

@Around("@annotation(LogExecutionTime)")
public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();

try {
return joinPoint.proceed();
} finally {
long duration = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " took " + duration + " ms");
}
}
}

Verwendung:


@LogExecutionTime
public TaskDto createTask(CreateTaskRequest request) {
return taskService.createTask(request);
}

Merksatz:

Annotation-basierte Pointcuts sind lesbar und explizit.


21. Benannte Pointcuts

Statt Pointcut-Ausdrücke zu wiederholen, definierst du benannte Pointcuts.


@Aspect
@Component
public class LoggingAspect {

@Pointcut("execution(* com.example..*Service.*(..))")
public void serviceMethods() {
}

@Before("serviceMethods()")
public void beforeServiceMethod() {
System.out.println("Before service method");
}

@AfterThrowing(
pointcut = "serviceMethods()",
throwing = "exception"
)
public void afterException(Throwable exception) {
System.out.println("Exception: " + exception.getMessage());
}
}

Merksatz:

Benannte Pointcuts machen Aspekte leichter wiederverwendbar und lesbarer.


22. Spring AOP ist proxy-basiert

Das ist der wichtigste Teil.

Spring AOP arbeitet meist, indem ein Proxy-Objekt erstellt wird.

Statt den echten Service direkt zu injizieren:


TaskService-Ziel

injiziert Spring:


TaskService-Proxy

Der Proxy umschließt das echte Ziel.

Ablauf:


Aufrufer

Proxy

Advice

Zielmethode

Advice

Aufrufer

Merksatz:

Spring AOP funktioniert, indem Methoden über einen Proxy aufgerufen werden.


23. Zielobjekt vs. Proxy

Zielobjekt:


das echte Service-Objekt

Proxy:


von Spring erstellter Wrapper um den echten Service

Beispiel:


@Service
public class TaskService {

@Transactional
public void createTask() {
// business logic
}
}

Spring kann erstellen:


TaskService-Proxy

Der Proxy fügt Transaktionsverhalten um die echte Methode herum hinzu.

Merksatz:

Der Proxy fügt Verhalten hinzu; das Ziel enthält die Business-Logik.


24. Warum Proxies wichtig sind

Damit AOP funktioniert, muss der Methodenaufruf über den Proxy laufen.

Funktioniert:


Controller -> TaskService-Proxy -> Advice -> echte TaskService-Methode

Funktioniert nicht:


TaskService-Ziel -> this.otherMethod()

weil this das echte Objekt meint, nicht den Proxy.

Merksatz:

AOP funktioniert nur, wenn der Aufruf über den Spring-Proxy läuft.


25. Self-Invocation-Problem

Self-Invocation bedeutet:


eine Methode in derselben Klasse ruft eine andere Methode derselben Klasse auf

Beispiel:


@Service
public class TaskService {

public void outer() {
inner();
}

@Transactional
public void inner() {
// transaction expected?
}
}

Wenn outer() inner() aufruft:


this.inner()

läuft der Aufruf nicht über den Spring-Proxy.

Deshalb gilt @Transactional auf inner() möglicherweise nicht.

Merksatz:

Self-Invocation umgeht den Proxy.


26. Self-Invocation im Bild

Externer Aufruf:


Controller

TaskService-Proxy

@Transactional-Advice

TaskService.inner()

Interner Aufruf:


TaskService.outer()

this.inner()

kein Proxy

kein Advice

Merksatz:

Externe Aufrufe können abgefangen werden; interne Selbstaufrufe können Advice umgehen.


27. Self-Invocation beheben

Besseres Design:


@Service
public class TaskService {

private final TaskTransactionService transactionService;

public TaskService(TaskTransactionService transactionService) {
this.transactionService = transactionService;
}

public void outer() {
transactionService.inner();
}
}

Zweite Bean:


@Service
public class TaskTransactionService {

@Transactional
public void inner() {
// transactional logic
}
}

Jetzt läuft der Aufruf so:


TaskService -> TaskTransactionService-Proxy -> Advice -> Zielmethode

Merksatz:

Verschiebe die beratene Methode in eine andere Spring-Bean, um Self-Invocation zu vermeiden.


28. JDK Dynamic Proxy

Spring kann JDK Dynamic Proxies verwenden.

Der JDK-Proxy arbeitet über Interfaces.

Beispiel:


public interface TaskService {
TaskDto createTask(CreateTaskRequest request);
}

Implementierung:


@Service
public class TaskServiceImpl implements TaskService {

@Override
public TaskDto createTask(CreateTaskRequest request) {
return new TaskDto(1L, request.title());
}
}

Spring kann einen Proxy erstellen, der dasselbe Interface implementiert.

Merksatz:

Der JDK Dynamic Proxy proxied Interfaces.


29. CGLIB-Proxy

Der CGLIB-Proxy erstellt eine Subklasse der Zielklasse.

Beispiel:


@Service
public class TaskService {

public TaskDto createTask(CreateTaskRequest request) {
return new TaskDto(1L, request.title());
}
}

Wenn es kein Interface gibt, kann Spring einen klassenbasierten Proxy verwenden.

Merksatz:

Der CGLIB-Proxy proxied Klassen durch Subclassing.


30. JDK-Proxy vs. CGLIB-Proxy

ThemaJDK Dynamic ProxyCGLIB-Proxy
ProxiedInterfacesKlassen
Im JDK enthaltenjanein, aber über Spring eingebunden
Interface nötigjanein
MethodenlimitsInterface-Methodenkann keine finalen Methoden beraten
Häufig in Springjaja

Merksatz:

JDK-Proxy = Interface-Proxy. CGLIB = Klassen-Proxy.


31. Final-Methoden-Falle

CGLIB nutzt Subclassing.

Deshalb können finale Methoden nicht überschrieben werden.

Beispiel:


@Service
public class TaskService {

@Transactional
public final void createTask() {
// final method
}
}

Ein klassenbasierter Proxy kann diese finale Methode nicht überschreiben.

Deshalb gilt Advice möglicherweise nicht.

Merksatz:

Finale Methoden sind ein Problem für klassenbasierte Proxy-Advice.


32. Private-Methoden-Falle

Spring AOP arbeitet bei Methodenaufrufen über Proxies.

Private Methoden können von außen nicht über den Proxy aufgerufen werden.

Schlecht:


@Service
public class TaskService {

public void outer() {
inner();
}

@Transactional
private void inner() {
}
}

Die private Methode wird nicht beraten.

Merksatz:

Spring AOP berät private Methoden nicht über Proxies.


33. Spring-Bean-Falle

Spring AOP gilt für von Spring verwaltete Beans.

Funktioniert:


@Service
public class TaskService {
}

Funktioniert nicht:


TaskService service = new TaskService();

weil dieses Objekt manuell erstellt wurde, nicht von Spring.

Merksatz:

Spring AOP funktioniert auf Spring-Beans, nicht auf manuell erstellten Objekten.


34. AOP und @Transactional

@Transactional wird häufig mit Spring AOP implementiert.

Wenn die Methode über den Proxy aufgerufen wird:


Proxy startet Transaktion
Zielmethode läuft
Proxy committet oder rollt zurück

Beispiel:


@Service
public class TaskService {

@Transactional
public void createTask() {
// database work
}
}

Ablauf:


Controller

TaskService-Proxy

Transaktion starten

echtes createTask()

Commit oder Rollback

Merksatz:

@Transactional hängt im Standard-Spring-Modus von Proxy-Interception ab.


35. AOP und Method Security

Method Security nutzt ebenfalls Proxy-ähnliche Interception.

Beispiel:


@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
}

Bevor die Methode läuft:


Security-Advice prüft Berechtigung
wenn erlaubt -> Methode läuft
wenn verweigert -> AccessDeniedException

Self-Invocation kann auch bei Method Security eine Falle sein.

Merksatz:

Method Security ist ebenfalls proxy-basierte Interception.


36. AOP und Caching

Caching nutzt ebenfalls Proxy-Interception.

Beispiel:


@Cacheable("tasks")
public TaskDto findById(Long id) {
return loadTaskFromDatabase(id);
}

Ablauf:


Proxy prüft Cache
bei Cache-Hit -> gecachten Wert zurückgeben
bei Cache-Miss -> Zielmethode aufrufen und Ergebnis speichern

Self-Invocation kann auch Caching umgehen.

Merksatz:

@Cacheable funktioniert, wenn Aufrufe über den Proxy laufen.


37. AOP und @Async

@Async hängt ebenfalls von Spring-Interception ab.

Beispiel:


@Async
public void sendEmail() {
}

Bei Aufruf über den Proxy:


Methode kann asynchron laufen

Bei Selbstaufruf:


Methode läuft möglicherweise normal, nicht async

Merksatz:

@Async braucht ebenfalls proxy-basierte Methoden-Interception.


38. Reihenfolge der Aspekte

Wenn mehrere Aspekte auf dieselbe Methode wirken, kann die Reihenfolge wichtig sein.

Beispiel:


Transaktions-Aspekt
Security-Aspekt
Logging-Aspekt
Metriken-Aspekt

Spring kann Aspekte ordnen mit:


@Order(1)

Beispiel:


@Aspect
@Component
@Order(1)
public class SecurityLoggingAspect {
}

Ein niedrigerer Order-Wert hat höhere Priorität.

Merksatz:

Die Aspekt-Reihenfolge ist wichtig, wenn mehrere Aspekte wirken.


39. Beispiel: Logging-Aspekt


@Aspect
@Component
public class ServiceLoggingAspect {

@Around("execution(* com.example..*Service.*(..))")
public Object logServiceMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();

System.out.println("Starting " + methodName);

try {
Object result = joinPoint.proceed();
System.out.println("Finished " + methodName);
return result;
} catch (Throwable exception) {
System.out.println("Failed " + methodName + ": " + exception.getMessage());
throw exception;
}
}
}

Das loggt:


Methodenstart
Methodenerfolg
Methodenfehler

Merksatz:

@Around eignet sich gut für Logging, Timing und das Umschließen der Methodenausführung.


40. Beispiel: Execution-Time-Aspekt


@Aspect
@Component
public class ExecutionTimeAspect {

@Around("@annotation(LogExecutionTime)")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.nanoTime();

try {
return joinPoint.proceed();
} finally {
long durationNs = System.nanoTime() - start;
System.out.println(
joinPoint.getSignature().toShortString()
+ " took "
+ durationNs / 1_000_000
+ " ms"
);
}
}
}

Eigene Annotation:


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}

Verwendung:


@LogExecutionTime
public List<TaskDto> findSlowTasks() {
return taskRepository.findSlowTasks();
}

Merksatz:

Eigene Annotationen machen AOP explizit und lesbar.


41. Was Spring AOP nicht leicht kann

Spring AOP ist im Vergleich zu vollem AspectJ eingeschränkt.

Spring AOP berät vor allem:


Methodenausführungen auf Spring-Beans

Normalerweise berät es nicht:


Feldzugriffe
Konstruktoraufrufe
private Methodenaufrufe
Objekte, die nicht von Spring verwaltet werden
jedes mit new erstellte Objekt
Domain-Objekte außerhalb des Spring-Containers

Dafür kann volles AspectJ-Weaving nötig sein.

Merksatz:

Spring AOP ist einfacher als AspectJ und fokussiert auf Spring-Bean-Methoden.


42. Spring AOP vs. AspectJ

ThemaSpring AOPAspectJ
Stilproxy-basiertweaving-basiert
UmfangSpring-Beansbreiteres Java-Programm
Komplexitäteinfachermächtiger
Spring-Bean nötigjanein
Feldzugriff-Join-Pointsneinja
Konstruktor-Join-Pointsneinja
Häufiger EinsatzTransaktionen, Security, Caching, Loggingfortgeschrittene querschnittliche Anliegen

Merksatz:

Spring AOP ist einfacher; AspectJ ist mächtiger.


43. Wann solltest du AOP nutzen?

Gute Anwendungsfälle:


Logging
Metriken
Auditing
transaktionsähnliche Wrapper
Security-Checks
Ausführungszeit messen
querschnittliche Validierung
caching-ähnliche Anliegen

Vorsicht bei:


Business-Logik in Aspekten versteckt
zu breite Pointcuts
Aspekte ändern Rückgabewerte unerwartet
Aspekte schlucken Exceptions
schwer debuggbares Verhalten

Merksatz:

Nutze AOP für technische querschnittliche Anliegen, nicht für zentrale Business-Logik.


44. Schlechtes AOP-Design

Schlecht:


@Around("execution(* com.example..*.*(..))")
public Object changeBusinessResult(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();

if (result instanceof TaskDto task) {
return new TaskDto(task.id(), "Changed secretly");
}

return result;
}

Problem:


Business-Ergebnis ändert sich unsichtbar
Code wird schwer verständlich
Debugging wird schmerzhaft

Besser:


Business-Regeln in Service-/Domain-Code behalten
Aspekte für technische Anliegen nutzen

Merksatz:

Verstecke wichtiges Business-Verhalten nicht in Aspekten.


45. Aspekte testen

Aspekt-Tests können knifflig sein.

Optionen:


Aspekt-Logik separat unit-testen
Integrationstest, dass Methodenaufruf den Aspekt auslöst
sichtbare Nebeneffekte testen
Spring-Internals nicht testen

Beispiel:


@SpringBootTest
class ExecutionTimeAspectTest {

@Autowired
private TaskService taskService;

@Test
void aspectRunsAroundAnnotatedMethod() {
taskService.findSlowTasks();

// assert observable side effect if possible
}
}

Für die Prüfung wichtig:


AOP-Verhalten braucht einen Spring-Proxy, deshalb beweisen einfache Unit-Tests keine Proxy-Interception.

Merksatz:

Um echte AOP-Interception zu testen, nutze einen Spring-Testkontext.


46. Häufige Prüfungsfallen

Falle 1

AOP ist für querschnittliche Anliegen da.


Falle 2

Ein Aspekt enthält Advice und Pointcuts.


Falle 3

Advice ist der Code, der läuft.


Falle 4

Der Pointcut wählt aus, wo Advice läuft.


Falle 5

Spring AOP ist proxy-basiert.


Falle 6

Spring AOP unterstützt vor allem Methodenausführungs-Join-Points auf Spring-Beans.


Falle 7

AOP gilt nicht für manuell erstellte Objekte.


Falle 8

Self-Invocation umgeht den Proxy.


Falle 9

Private Methoden werden über Spring-AOP-Proxies nicht beraten.


Falle 10

Finale Methoden können ein Problem für klassenbasierte Proxies sein.


Falle 11

JDK Dynamic Proxies proxien Interfaces.


Falle 12

CGLIB-Proxies proxien Klassen durch Subclassing.


Falle 13

@Around muss proceed() aufrufen, wenn die echte Methode laufen soll.


Falle 14

@Transactional, Method Security, Caching und Async-Verhalten hängen oft von Proxies ab.


Falle 15

Nutze AOP für technische Anliegen, nicht für versteckte Business-Logik.


47. Echte Prüfungsfrage: AOP

Frage:

Was ist AOP?

Antwort:

AOP bedeutet Aspect-Oriented Programming. Es ist ein Programmierstil, um querschnittliche Anliegen wie Logging, Transaktionen, Security, Caching oder Metriken von der Business-Logik zu trennen.


48. Echte Prüfungsfrage: Querschnittliches Anliegen

Frage:

Was ist ein querschnittliches Anliegen?

Antwort:

Ein querschnittliches Anliegen ist technische Logik, die in vielen Teilen einer Anwendung vorkommt, zum Beispiel Logging, Transaktionen, Security, Caching, Auditing oder Metriken.


49. Echte Prüfungsfrage: Aspekt

Frage:

Was ist ein Aspekt?

Antwort:

Ein Aspekt ist eine Klasse mit querschnittlicher Logik. In Spring wird er oft mit @Aspect deklariert und als Spring-Bean registriert.


50. Echte Prüfungsfrage: Advice

Frage:

Was ist Advice?

Antwort:

Advice ist der Code, der an einem passenden Join Point läuft. Beispiele sind @Before, @After, @AfterReturning, @AfterThrowing und @Around.


51. Echte Prüfungsfrage: Pointcut

Frage:

Was ist ein Pointcut?

Antwort:

Ein Pointcut ist ein Ausdruck, der festlegt, wo Advice laufen soll — meist matcht er Methodenausführungen in Spring AOP.


52. Echte Prüfungsfrage: Join Point

Frage:

Was ist ein Join Point in Spring AOP?

Antwort:

In Spring AOP ist ein Join Point typischerweise eine Methodenausführung auf einer von Spring verwalteten Bean.


53. Echte Prüfungsfrage: Proxy-basiertes AOP

Frage:

Was bedeutet es, dass Spring AOP proxy-basiert ist?

Antwort:

Spring erstellt ein Proxy-Objekt um die Ziel-Bean. Aufrufe müssen über den Proxy laufen, damit Advice ausgeführt wird.


54. Echte Prüfungsfrage: Self-Invocation

Frage:

Warum löst Self-Invocation kein Spring-AOP-Advice aus?

Antwort:

Weil eine Methode im Zielobjekt eine andere Methode über this aufruft und dabei den Spring-Proxy umgeht. Da der Aufruf nicht über den Proxy läuft, wird kein Advice angewendet.


55. Echte Prüfungsfrage: JDK vs. CGLIB-Proxy

Frage:

Was ist der Unterschied zwischen JDK Dynamic Proxies und CGLIB-Proxies?

Antwort:

JDK Dynamic Proxies proxien Interfaces. CGLIB-Proxies proxien Klassen, indem sie Subklassen erstellen.


56. Echte Prüfungsfrage: @Around

Frage:

Was ist besonders an @Around-Advice?

Antwort:

@Around-Advice kann die Methodenausführung steuern. Es kann vor und nach der Methode laufen, entscheiden, ob die Methode aufgerufen wird, den Rückgabewert ändern und Exceptions behandeln. Es muss proceed() aufrufen, wenn die Zielmethode laufen soll.


57. Antwort fürs Interview

Frage:

Erkläre Spring AOP in einfachen Worten.

Gute Antwort:

Spring AOP lässt dich querschnittliche Logik wie Logging, Transaktionen, Security, Caching oder Metriken um Spring-Bean-Methoden herum anwenden, ohne diese Logik in jede Business-Methode zu packen. Spring macht das meist durch Proxies. Der Aufrufer ruft den Proxy auf, der Proxy führt Advice aus und ruft dann die echte Zielmethode auf.


58. Antwort fürs Interview

Frage:

Was ist der Unterschied zwischen Aspekt, Advice und Pointcut?

Gute Antwort:

Ein Aspekt ist die Klasse mit querschnittlicher Logik. Advice ist der eigentliche Code, der läuft — zum Beispiel vor, nach oder um eine Methode herum. Ein Pointcut ist der Ausdruck, der festlegt, welche Methodenausführungen beraten werden sollen.


59. Antwort fürs Interview

Frage:

Warum funktioniert @Transactional manchmal nicht, wenn eine Methode eine andere Methode in derselben Klasse aufruft?

Gute Antwort:

Weil Springs Standard-Transaktionsverwaltung proxy-basiert ist. Das Transaktions-Advice wird angewendet, wenn ein Methodenaufruf über den Spring-Proxy läuft. Ruft eine Methode in derselben Klasse eine andere über this auf, umgeht der Aufruf den Proxy — das Transaktions-Advice läuft dann möglicherweise nicht. Das nennt man Self-Invocation.


60. Antwort fürs Interview

Frage:

Was ist der Unterschied zwischen Spring AOP und AspectJ?

Gute Antwort:

Spring AOP ist proxy-basiert und wirkt vor allem auf Methodenausführungen von Spring-verwalteten Beans. Es ist einfacher und passt gut zu gängigen Spring-Anliegen wie Transaktionen, Security, Caching und Logging. AspectJ ist mächtiger und nutzt Weaving — es kann mehr Join-Point-Typen beraten, darunter Feldzugriffe, Konstruktoren und Objekte außerhalb von Spring.


61. Kleine Code-Übung

Erstelle einen Aspekt, der vor jeder Service-Methode loggt.

Mögliche Antwort:


@Aspect
@Component
public class LoggingAspect {

@Before("execution(* com.example..*Service.*(..))")
public void logBeforeServiceMethod() {
System.out.println("A service method is about to run");
}
}

Frage:

Was matcht der Pointcut?

Antwort:


Alle Methoden in Klassen, deren Name auf Service endet, unter com.example und seinen Unterpaketen.

62. Kleine Bug-Übung 1

Problem:


@Around("execution(* com.example..*Service.*(..))")
public Object around(ProceedingJoinPoint joinPoint) {
System.out.println("Before");
return null;
}

Frage:

Was ist falsch?

Antwort:

joinPoint.proceed() wird nicht aufgerufen, deshalb läuft die echte Methode nie. Das Advice gibt immer null zurück.

Korrekt:


@Around("execution(* com.example..*Service.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before");
Object result = joinPoint.proceed();
System.out.println("After");
return result;
}

63. Kleine Bug-Übung 2

Problem:


@Service
public class TaskService {

public void outer() {
inner();
}

@Transactional
public void inner() {
// database work
}
}

Frage:

Warum funktioniert @Transactional auf inner() möglicherweise nicht?

Antwort:

outer() ruft inner() über this.inner() auf, nicht über den Spring-Proxy. Das ist Self-Invocation — das Transaktions-Advice gilt dann möglicherweise nicht.


64. Kleine Bug-Übung 3

Problem:


TaskService taskService = new TaskService();
taskService.createTask();

Frage:

Warum greift Spring AOP nicht?

Antwort:

Das Objekt wurde manuell mit new erstellt — es ist keine von Spring verwaltete Bean und kein Spring-Proxy ist beteiligt.


65. Kleine Bug-Übung 4

Problem:


@Service
public final class TaskService {
}

Frage:

Warum kann das ein Problem für CGLIB-Proxying sein?

Antwort:

CGLIB erstellt Subklassen-basierte Proxies. Eine finale Klasse kann nicht subclassed werden — klassenbasiertes Proxying funktioniert dann normalerweise nicht.


Übungsfragen

Frage 1

Was bedeutet AOP?

Antwort:

AOP bedeutet Aspect-Oriented Programming.


Frage 2

Welches Problem löst AOP?

Antwort:

AOP löst das Problem, dass wiederholte querschnittliche technische Logik wie Logging, Transaktionen, Security, Metriken, Auditing oder Caching in Business-Methoden vermischt wird.


Frage 3

Was ist ein querschnittliches Anliegen?

Antwort:

Ein querschnittliches Anliegen ist Logik, die in vielen Teilen einer Anwendung vorkommt, zum Beispiel Logging, Transaktionen, Security oder Caching.


Frage 4

Was ist ein Aspekt?

Antwort:

Ein Aspekt ist eine Klasse mit querschnittlicher Logik, meist mit @Aspect deklariert und als Spring-Bean registriert.


Frage 5

Was ist Advice?

Antwort:

Advice ist der Code, der an einem passenden Join Point läuft, zum Beispiel vor, nach oder um eine Methodenausführung herum.


Frage 6

Was ist ein Pointcut?

Antwort:

Ein Pointcut ist ein Ausdruck, der festlegt, wo Advice laufen soll.


Frage 7

Was ist ein Join Point in Spring AOP?

Antwort:

In Spring AOP ist ein Join Point meist eine Methodenausführung auf einer von Spring verwalteten Bean.


Frage 8

Was macht @Before?

Antwort:

@Before-Advice läuft vor der ausgeführten, gematchten Methode.


Frage 9

Was macht @AfterReturning?

Antwort:

@AfterReturning-Advice läuft nur, wenn die gematchte Methode erfolgreich zurückkehrt.


Frage 10

Was macht @AfterThrowing?

Antwort:

@AfterThrowing-Advice läuft nur, wenn die gematchte Methode eine Exception wirft.


Frage 11

Was macht @Around?

Antwort:

@Around-Advice umschließt die Methodenausführung und kann steuern, ob die Zielmethode läuft, Rückgabewerte ändern, Zeit messen oder Exceptions behandeln.


Frage 12

Warum muss @Around meist proceed() aufrufen?

Antwort:

Weil proceed() die echte Zielmethode aufruft. Ohne diesen Aufruf läuft die Zielmethode nicht.


Frage 13

Was matcht execution(* com.example..*Service.*(..))?

Antwort:

Es matcht alle Methoden in Klassen, die auf Service enden, unter com.example und seinen Unterpaketen.


Frage 14

Was bedeutet es, dass Spring AOP proxy-basiert ist?

Antwort:

Spring erstellt einen Proxy um die Ziel-Bean, und Advice läuft, wenn Aufrufe über diesen Proxy gehen.


Frage 15

Was ist Self-Invocation?

Antwort:

Self-Invocation ist, wenn eine Methode in einer Klasse eine andere Methode in derselben Klasse aufruft.


Frage 16

Warum umgeht Self-Invocation AOP?

Antwort:

Weil der Aufruf this nutzt, nicht den Spring-Proxy. Da der Aufruf nicht über den Proxy läuft, wird kein Advice angewendet.


Frage 17

Was ist der Unterschied zwischen JDK-Proxy und CGLIB-Proxy?

Antwort:

JDK Dynamic Proxies proxien Interfaces. CGLIB-Proxies proxien Klassen durch Subclassing.


Frage 18

Warum sind private Methoden ein Problem für Spring AOP?

Antwort:

Private Methoden werden nicht extern über den Proxy aufgerufen — Spring AOP kann sie im normalen proxy-basierten Modell nicht beraten.


Frage 19

Wie hängt AOP mit @Transactional zusammen?

Antwort:

@Transactional wird häufig über Spring-AOP-Proxies implementiert. Der Proxy startet, committet oder rollt die Transaktion um die Zielmethode herum zurück.


Frage 20

Wann solltest du Spring AOP nutzen?

Antwort:

Nutze Spring AOP für technische querschnittliche Anliegen wie Logging, Metriken, Auditing, Transaktionen, Security oder Caching — nicht, um zentrale Business-Logik zu verstecken.

Merksätze zum Schluss

  • AOP bedeutet Aspect-Oriented Programming.
  • AOP trennt querschnittliche Anliegen von der Business-Logik.
  • Querschnittliche Anliegen umfassen Logging, Transaktionen, Security, Caching, Metriken und Auditing.
  • Ein Aspekt enthält querschnittliche Logik.
  • Advice ist der Code, der läuft.
  • Ein Pointcut wählt aus, wo Advice läuft.
  • In Spring AOP sind Join Points vor allem Methodenausführungen auf Spring-Beans.
  • @Before läuft vor der Methode.
  • @After läuft nach Erfolg oder Exception.
  • @AfterReturning läuft nach erfolgreichem Return.
  • @AfterThrowing läuft nach einer Exception.
  • @Around umschließt die Methodenausführung.
  • @Around muss proceed() aufrufen, wenn die echte Methode laufen soll.
  • Spring AOP ist proxy-basiert.
  • AOP funktioniert, wenn Aufrufe über den Spring-Proxy laufen.
  • Self-Invocation umgeht den Proxy.
  • JDK Dynamic Proxy proxied Interfaces.
  • CGLIB-Proxy proxied Klassen.
  • Private Methoden werden über Spring-AOP-Proxies nicht beraten.
  • Finale Methoden können ein Problem für klassenbasierte Proxies sein.
  • @Transactional, Method Security, Caching und Async hängen oft von Proxies ab.
  • Nutze AOP für technische Anliegen, nicht für versteckte Business-Logik.