or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aop.mdconfiguration.mddependency-injection.mdfunctions.mdhttp-client.mdhttp-server.mdindex.mdmanagement.mdmessaging.mdreactive.mdretry.mdscheduling.mdwebsocket.md

aop.mddocs/

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

```