Woche 8, Tag 1 — Spring AOP Mental Model: Proxies, Aspects, Advice, Pointcuts und Self-Invocation
Ziel
Heute willst du Spring AOP verstehen.
Die Kernfragen:
- Was ist AOP?
- Welches Problem löst AOP?
- Was ist ein querschnittliches Anliegen?
- Was ist ein Aspekt?
- Was ist Advice?
- Was ist ein Pointcut?
- Was ist ein Join Point?
- Warum ist Spring AOP proxy-basiert?
- Was ist der Unterschied zwischen JDK-Proxy und CGLIB-Proxy?
- Warum funktioniert Self-Invocation nicht?
- Wie hängt AOP mit
@Transactional, Security und Caching zusammen? - 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
| Begriff | Bedeutung |
|---|---|
| Aspekt | Klasse mit querschnittlicher Logik |
| Advice | Code, der an einem passenden Punkt läuft |
| Pointcut | Ausdruck, der festlegt, wo Advice gilt |
| Join Point | Punkt in der Programmausführung, meist Methodenausführung in Spring AOP |
| Zielobjekt | das echte Objekt, das geproxied wird |
| Proxy | Wrapper-Objekt, das Spring erstellt |
| Weaving | Anwenden 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:
@Beforelä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:
@Afterlä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:
@AfterReturninglä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:
@AfterThrowinglä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:
@Aroundsteuert die Methodenausführung und mussproceed()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:
ProceedingJoinPointlä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 vergessenesproceed()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
| Thema | JDK Dynamic Proxy | CGLIB-Proxy |
|---|---|---|
| Proxied | Interfaces | Klassen |
| Im JDK enthalten | ja | nein, aber über Spring eingebunden |
| Interface nötig | ja | nein |
| Methodenlimits | Interface-Methoden | kann keine finalen Methoden beraten |
| Häufig in Spring | ja | ja |
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:
@Transactionalhä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:
@Cacheablefunktioniert, 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:
@Asyncbraucht 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:
@Aroundeignet 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
| Thema | Spring AOP | AspectJ |
|---|---|---|
| Stil | proxy-basiert | weaving-basiert |
| Umfang | Spring-Beans | breiteres Java-Programm |
| Komplexität | einfacher | mächtiger |
| Spring-Bean nötig | ja | nein |
| Feldzugriff-Join-Points | nein | ja |
| Konstruktor-Join-Points | nein | ja |
| Häufiger Einsatz | Transaktionen, Security, Caching, Logging | fortgeschrittene 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
@Transactionalmanchmal 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.
@Beforeläuft vor der Methode.@Afterläuft nach Erfolg oder Exception.@AfterReturningläuft nach erfolgreichem Return.@AfterThrowingläuft nach einer Exception.@Aroundumschließt die Methodenausführung.@Aroundmussproceed()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.