0
# Aspect-Oriented Programming
1
2
Micronaut's AOP system provides compile-time aspect weaving with method and constructor interception, around advice, and introduction support without runtime proxy generation.
3
4
## Capabilities
5
6
### Method Interception
7
8
Intercept method calls with custom logic using around advice.
9
10
```java { .api }
11
/**
12
* Custom interceptor annotation
13
*/
14
@Around
15
@InterceptorBinding
16
@Retention(RetentionPolicy.RUNTIME)
17
@Target({ElementType.TYPE, ElementType.METHOD})
18
public @interface Timed {
19
String value() default "";
20
}
21
22
/**
23
* Method interceptor implementation
24
*/
25
@Singleton
26
public class TimedInterceptor implements MethodInterceptor<Object, Object> {
27
28
@Override
29
public Object intercept(InvocationContext<Object, Object> context) {
30
long start = System.currentTimeMillis();
31
try {
32
return context.proceed();
33
} finally {
34
long duration = System.currentTimeMillis() - start;
35
String methodName = context.getExecutableMethod().getName();
36
log.info("Method {} took {}ms", methodName, duration);
37
}
38
}
39
}
40
41
/**
42
* Using the interceptor
43
*/
44
@Singleton
45
public class UserService {
46
47
@Timed
48
public User findById(Long id) {
49
return userRepository.findById(id);
50
}
51
52
@Timed("user-creation")
53
public User createUser(User user) {
54
return userRepository.save(user);
55
}
56
}
57
```
58
59
### Cache Interception
60
61
Built-in caching aspects for method-level caching.
62
63
```java { .api }
64
/**
65
* Cacheable methods
66
*/
67
@Singleton
68
public class ProductService {
69
70
@Cacheable("products")
71
public Product findById(Long id) {
72
return productRepository.findById(id);
73
}
74
75
@Cacheable(value = "products", parameters = {"category", "active"})
76
public List<Product> findByCategory(String category, boolean active) {
77
return productRepository.findByCategoryAndActive(category, active);
78
}
79
80
@CacheInvalidate("products")
81
public Product updateProduct(Product product) {
82
return productRepository.save(product);
83
}
84
85
@CacheInvalidate(value = "products", all = true)
86
public void clearProductCache() {
87
// Cache will be cleared automatically
88
}
89
}
90
```
91
92
### Introduction Advice
93
94
Create interface implementations dynamically using introduction advice.
95
96
```java { .api }
97
/**
98
* Introduction annotation
99
*/
100
@Introduction
101
@Bean
102
@Retention(RetentionPolicy.RUNTIME)
103
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
104
public @interface Repository {
105
String value() default "";
106
}
107
108
/**
109
* Introduction interceptor
110
*/
111
@Singleton
112
public class RepositoryIntroduction implements MethodInterceptor<Object, Object> {
113
114
@Override
115
public Object intercept(InvocationContext<Object, Object> context) {
116
String methodName = context.getExecutableMethod().getName();
117
118
if (methodName.startsWith("find")) {
119
return handleFindMethod(context);
120
} else if (methodName.startsWith("save")) {
121
return handleSaveMethod(context);
122
} else if (methodName.startsWith("delete")) {
123
return handleDeleteMethod(context);
124
}
125
126
return context.proceed();
127
}
128
129
private Object handleFindMethod(InvocationContext<Object, Object> context) {
130
// Auto-implement find methods
131
return null;
132
}
133
}
134
135
/**
136
* Interface that will be implemented automatically
137
*/
138
@Repository
139
public interface CustomerRepository {
140
Customer findById(Long id);
141
List<Customer> findByName(String name);
142
Customer save(Customer customer);
143
void deleteById(Long id);
144
}
145
```
146
147
### Retry and Circuit Breaker
148
149
Built-in resilience patterns using AOP.
150
151
```java { .api }
152
/**
153
* Retry interceptor
154
*/
155
@Singleton
156
public class ExternalService {
157
158
@Retryable(attempts = "3", delay = "1s", multiplier = "2.0")
159
public String callExternalApi() {
160
// This method will be retried up to 3 times
161
// with exponential backoff (1s, 2s, 4s)
162
return restClient.call();
163
}
164
165
@CircuitBreaker(attempts = "5", openStatusTimeout = "1m", resetTimeout = "30s")
166
public Data fetchData() {
167
// Circuit breaker will open after 5 failures
168
// Stay open for 1 minute, then try to reset
169
return dataProvider.fetch();
170
}
171
}
172
173
/**
174
* Fallback methods
175
*/
176
@Singleton
177
public class UserService {
178
179
@Retryable
180
public User getUserFromPrimary(Long id) {
181
return primaryDataSource.getUser(id);
182
}
183
184
@Fallback
185
public User getUserFromPrimary(Long id, Exception ex) {
186
log.warn("Failed to get user from primary source", ex);
187
return fallbackDataSource.getUser(id);
188
}
189
}
190
```
191
192
### Validation Interception
193
194
Automatic validation using Bean Validation annotations.
195
196
```java { .api }
197
/**
198
* Validation interceptor (automatically applied)
199
*/
200
@Singleton
201
public class OrderService {
202
203
public Order createOrder(@Valid @NotNull OrderRequest request) {
204
// Parameters will be validated automatically
205
return new Order(request.getCustomerId(), request.getItems());
206
}
207
208
@Validated
209
public void updateOrderStatus(@NotNull Long orderId,
210
@NotBlank String status) {
211
// Method-level validation
212
orderRepository.updateStatus(orderId, status);
213
}
214
}
215
```
216
217
### Security Interception
218
219
Method-level security using annotations.
220
221
```java { .api }
222
/**
223
* Security annotations
224
*/
225
@Singleton
226
@Secured("isAuthenticated()")
227
public class AdminService {
228
229
@Secured({"ROLE_ADMIN"})
230
public void deleteUser(Long userId) {
231
userRepository.delete(userId);
232
}
233
234
@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
235
public Report generateReport(ReportType type) {
236
return reportGenerator.generate(type);
237
}
238
239
@PreAuthorize("@securityService.canAccessUser(authentication, #userId)")
240
public User getUser(Long userId) {
241
return userRepository.findById(userId);
242
}
243
}
244
```
245
246
## Types
247
248
```java { .api }
249
// Core AOP interfaces
250
public interface MethodInterceptor<T, R> extends Interceptor<T, R> {
251
R intercept(InvocationContext<T, R> context);
252
}
253
254
public interface ConstructorInterceptor<T> extends Interceptor<T, T> {
255
T intercept(InvocationContext<T, T> context);
256
}
257
258
public interface InvocationContext<T, R> {
259
T getTarget();
260
ExecutableMethod<T, R> getExecutableMethod();
261
Map<String, Object> getAttributes();
262
Object[] getParameterValues();
263
R proceed() throws RuntimeException;
264
R proceed(Interceptor<T, R> from) throws RuntimeException;
265
}
266
267
public interface MethodInvocationContext<T, R> extends InvocationContext<T, R> {
268
MutableArgumentValue<?>[] getArguments();
269
<A> Optional<A> getAttribute(CharSequence name, Class<A> type);
270
void setAttribute(CharSequence name, Object value);
271
}
272
273
// Interceptor binding
274
public interface InterceptorRegistry {
275
<T> Interceptor<T, ?>[] resolveInterceptors(ExecutableMethod<T, ?> method,
276
InterceptorKind kind);
277
<T> List<BeanRegistration<Interceptor<T, ?>>> findInterceptors(InterceptorKind kind,
278
BeanDefinition<?> beanDefinition);
279
}
280
281
// Proxy interfaces
282
public interface InterceptedProxy<T> {
283
T interceptedTarget();
284
ExecutableMethod<?, ?> findInterceptedMethod();
285
}
286
287
public interface Intercepted {
288
// Marker interface for intercepted beans
289
}
290
291
// Cache annotations (built-in AOP)
292
@Target({ElementType.METHOD, ElementType.TYPE})
293
@Retention(RetentionPolicy.RUNTIME)
294
@Around
295
@InterceptorBinding
296
public @interface Cacheable {
297
String[] value() default {};
298
String[] parameters() default {};
299
boolean atomic() default true;
300
}
301
302
@Target({ElementType.METHOD, ElementType.TYPE})
303
@Retention(RetentionPolicy.RUNTIME)
304
@Around
305
@InterceptorBinding
306
public @interface CacheInvalidate {
307
String[] value() default {};
308
String[] parameters() default {};
309
boolean all() default false;
310
boolean async() default false;
311
}
312
313
// Retry annotations
314
@Target({ElementType.METHOD, ElementType.TYPE})
315
@Retention(RetentionPolicy.RUNTIME)
316
@Around
317
@InterceptorBinding
318
public @interface Retryable {
319
String attempts() default "3";
320
String delay() default "1s";
321
String multiplier() default "1.0";
322
String maxDelay() default "";
323
Class<? extends Throwable>[] includes() default {};
324
Class<? extends Throwable>[] excludes() default {};
325
}
326
327
@Target({ElementType.METHOD, ElementType.TYPE})
328
@Retention(RetentionPolicy.RUNTIME)
329
@Around
330
@InterceptorBinding
331
public @interface CircuitBreaker {
332
String attempts() default "20";
333
String openStatusTimeout() default "1m";
334
String resetTimeout() default "20s";
335
Class<? extends Throwable>[] includes() default {};
336
Class<? extends Throwable>[] excludes() default {};
337
}
338
```