09 Aspect-oriented Programming
Aspect-oriented programming (AOP) is a programming paradigm that allows you to separate cross-cutting concerns from the main application logic. This separation is achieved by defining aspects, which are reusable modules that can be applied to different parts of the application. Aspects can be used to implement features such as logging, security, and transaction management, without cluttering the main application code with boilerplate code.
AOP is used to physically separate logically independent concerns. The ultimate goal is to improve modularity and maintainability by reducing code duplication and improving oraganization.
Terminology
- Aspect: A module that encapsulates a cross-cutting concern.
- Join Point: A point in the execution of the program, such as method invocation, exception handling, or variable assignment.
- Advice: The action taken by an aspect at a particular join point.
- Pointcut: A set of join points where the advice(s) should be applied.
- Weaving: The process of applying aspects to the main application code.
Spring AOP
Spring AOP is a key component of the Spring Framework. Spring itself uses AOP to provide transaction management, security, logging, and other cross-cutting concerns. It allows you to define aspects using AspectJ-style syntax.
Flow
When the calling code invokes a method (e.g., foo()), the Spring AOP proxy intercepts the call and applies the advice defined in the aspect.
Afterwards, the proxy forwards the call to the actual object, which executes the method.
The object returns control to the proxy which applies any additional advice and returns the result to the calling code.
As soon as the calls reach the target object/instance, calls to the same instance cannot be intercepted by the proxy. These calls are not run by the Spring Container and are therefore not intercepted.
sequenceDiagram
participant C as Caller
participant P as Proxy
participant O as Object
C->>P: Invoke: foo()
P->>P: Before...
P->>O: Invoke: foo()
O->>O: foo()
O->>P: Return: foo()
P->>P: After...
P->>C: Return: foo()
Pointcut Expressions
Spring AOP limits join points to only method execution.
Pointcut expressions are used to define the join points where the advice should be applied. They can be used to match method invocations, field access, exception handling, and other join points. In Spring AOP, pointcut expressions are defined using AspectJ-style syntax. It does not support all AspectJ expressions (but maybe will in the future). For now, Spring AOP supports the following pointcut designators (PCD):
execution: Method execution join points. This is the primary pointcut designator in Spring AOP.within: Limits matching to join points within certain types (e.g. method execution within a matching class).this: Limits matching to join points where the bean reference (proxy) is an instance of the given type.target: Limits matching to join points where the target object (object being proxied) is an instance of the given type.args: Limits matching to join points where the arguments are instances of the given types.@target: Limits matching to join points where the class of the executing method has an annotation of the given type.@args: Limits matching to join points where the runtime type of the actual arguments passed have annotations of the given type(s).@within: Limits matching to join points within types that have the given annotation.@annotation: Limits matching to join points where the method has the given annotation.bean: Limits matching to join points to a particular named Spring bean or to a set of named Spring beans when using wildcards.
Pointcut expressions can be combined using logical operators: && (and), || (or), and ! (not).
Examples
execution(* com.example.service.*.*(..)): Matches the execution of any method in thecom.example.servicepackage.execution(* com.example.service.*.get*(..)): Matches the execution of any method in thecom.example.servicepackage that starts withget.within(com.example.service.*): Matches any join point within thecom.example.servicepackage.@annotation(com.example.annotation.MyAnnotation): Matches any method that has the@MyAnnotationannotation.args(Float,..): Matches any method that takes aFloatas the first parameter.@within(com.example.annotation.MyAnnotation) && args(com.example.model.MyClass): Matches any method within a type that has the@MyAnnotationannotation and only takes aMyClassas parameter.