or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aop.mdcore-container.mddata-access.mdindex.mdintegration.mdmessaging.mdreactive-web.mdtesting.mdweb-framework.md

aop.mddocs/

0

# Aspect-Oriented Programming (AOP)

1

2

Spring AOP provides aspect-oriented programming implementation allowing you to define method interceptors and pointcuts to cleanly decouple cross-cutting concerns like logging, security, transactions, and caching from your business logic.

3

4

## Maven Dependencies

5

6

```xml

7

<!-- Spring AOP -->

8

<dependency>

9

<groupId>org.springframework</groupId>

10

<artifactId>spring-aop</artifactId>

11

<version>5.3.39</version>

12

</dependency>

13

14

<!-- AspectJ for advanced AOP features -->

15

<dependency>

16

<groupId>org.aspectj</groupId>

17

<artifactId>aspectjweaver</artifactId>

18

<version>1.9.7</version>

19

</dependency>

20

21

<!-- Enable AspectJ support -->

22

<dependency>

23

<groupId>org.springframework</groupId>

24

<artifactId>spring-aspects</artifactId>

25

<version>5.3.39</version>

26

</dependency>

27

```

28

29

## Core Imports

30

31

```java { .api }

32

// Core AOP interfaces

33

import org.aopalliance.intercept.MethodInterceptor;

34

import org.aopalliance.intercept.MethodInvocation;

35

import org.springframework.aop.MethodBeforeAdvice;

36

import org.springframework.aop.AfterReturningAdvice;

37

import org.springframework.aop.ThrowsAdvice;

38

39

// Pointcut and Advisor interfaces

40

import org.springframework.aop.Pointcut;

41

import org.springframework.aop.Advisor;

42

import org.springframework.aop.PointcutAdvisor;

43

import org.springframework.aop.ClassFilter;

44

import org.springframework.aop.MethodMatcher;

45

46

// Proxy creation

47

import org.springframework.aop.framework.ProxyFactory;

48

import org.springframework.aop.framework.AopProxy;

49

import org.springframework.aop.aspectj.AspectJProxyFactory;

50

51

// AspectJ annotations

52

import org.aspectj.lang.annotation.Aspect;

53

import org.aspectj.lang.annotation.Before;

54

import org.aspectj.lang.annotation.After;

55

import org.aspectj.lang.annotation.AfterReturning;

56

import org.aspectj.lang.annotation.AfterThrowing;

57

import org.aspectj.lang.annotation.Around;

58

import org.aspectj.lang.annotation.Pointcut;

59

import org.aspectj.lang.JoinPoint;

60

import org.aspectj.lang.ProceedingJoinPoint;

61

62

// Configuration

63

import org.springframework.context.annotation.EnableAspectJAutoProxy;

64

import org.springframework.aop.config.AopConfigUtils;

65

```

66

67

## Core AOP Concepts

68

69

### Advice Types

70

71

```java { .api }

72

// Tag interface for all advice types

73

public interface Advice {

74

}

75

76

// Method interceptor that sits around method invocations

77

@FunctionalInterface

78

public interface MethodInterceptor extends Interceptor {

79

Object invoke(MethodInvocation invocation) throws Throwable;

80

}

81

82

// Advice invoked before method execution

83

public interface MethodBeforeAdvice extends BeforeAdvice {

84

void before(Method method, Object[] args, Object target) throws Throwable;

85

}

86

87

// Advice invoked after successful method execution

88

public interface AfterReturningAdvice extends AfterAdvice {

89

void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;

90

}

91

92

// Advice invoked after method throws exception

93

public interface ThrowsAdvice extends AfterAdvice {

94

// Marker interface - implementations should have methods like:

95

// public void afterThrowing(Exception ex)

96

// public void afterThrowing(Method method, Object[] args, Object target, Exception ex)

97

}

98

```

99

100

### Pointcut and Advisor

101

102

```java { .api }

103

// Core interface expressing where advice should be applied

104

public interface Pointcut {

105

ClassFilter getClassFilter();

106

MethodMatcher getMethodMatcher();

107

108

Pointcut TRUE = TruePointcut.INSTANCE;

109

}

110

111

// Interface for matching classes

112

@FunctionalInterface

113

public interface ClassFilter {

114

boolean matches(Class<?> clazz);

115

ClassFilter TRUE = TrueClassFilter.INSTANCE;

116

}

117

118

// Interface for matching methods

119

public interface MethodMatcher {

120

boolean matches(Method method, Class<?> targetClass);

121

boolean isRuntime();

122

boolean matches(Method method, Class<?> targetClass, Object... args);

123

124

MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

125

}

126

127

// Base interface holding advice and filter determining applicability

128

public interface Advisor {

129

Advice getAdvice();

130

boolean isPerInstance();

131

}

132

133

// Superinterface for all Advisors driven by Pointcut

134

public interface PointcutAdvisor extends Advisor {

135

Pointcut getPointcut();

136

}

137

```

138

139

### Proxy Creation

140

141

```java { .api }

142

// Delegate interface for AOP proxy creation

143

public interface AopProxy {

144

Object getProxy();

145

Object getProxy(ClassLoader classLoader);

146

}

147

148

// Factory for AOP proxies for programmatic use

149

public class ProxyFactory extends AdvisedSupport {

150

public ProxyFactory();

151

public ProxyFactory(Object target);

152

public ProxyFactory(Class<?>... proxyInterfaces);

153

public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor);

154

155

public Object getProxy();

156

public Object getProxy(ClassLoader classLoader);

157

158

// Static convenience methods

159

public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor);

160

public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource);

161

}

162

163

// AspectJ-based extension of ProxyFactory

164

public class AspectJProxyFactory extends ProxyCreatorSupport {

165

public AspectJProxyFactory();

166

public AspectJProxyFactory(Object target);

167

168

public void addAspect(Object aspectInstance);

169

public void addAspect(Class<?> aspectClass);

170

public <T> T getProxy();

171

public <T> T getProxy(ClassLoader classLoader);

172

}

173

```

174

175

## AspectJ Integration

176

177

### Enabling AspectJ Support

178

179

```java { .api }

180

// Enables support for handling components marked with AspectJ's @Aspect annotation

181

@Target(ElementType.TYPE)

182

@Retention(RetentionPolicy.RUNTIME)

183

@Documented

184

@Import(AspectJAutoProxyRegistrar.class)

185

public @interface EnableAspectJAutoProxy {

186

boolean proxyTargetClass() default false;

187

boolean exposeProxy() default false;

188

}

189

190

// Configuration class enabling AspectJ auto proxy

191

@Configuration

192

@EnableAspectJAutoProxy

193

public class AopConfig {

194

// AspectJ aspects will be automatically detected and applied

195

}

196

```

197

198

### AspectJ Annotations

199

200

```java { .api }

201

// Marks a class as an aspect

202

@Retention(RetentionPolicy.RUNTIME)

203

@Target(ElementType.TYPE)

204

public @interface Aspect {

205

String value() default "";

206

}

207

208

// Declares a pointcut

209

@Retention(RetentionPolicy.RUNTIME)

210

@Target(ElementType.METHOD)

211

public @interface Pointcut {

212

String value() default "";

213

String argNames() default "";

214

}

215

216

// Before advice

217

@Retention(RetentionPolicy.RUNTIME)

218

@Target(ElementType.METHOD)

219

public @interface Before {

220

String value();

221

String argNames() default "";

222

}

223

224

// After returning advice

225

@Retention(RetentionPolicy.RUNTIME)

226

@Target(ElementType.METHOD)

227

public @interface AfterReturning {

228

String value() default "";

229

String pointcut() default "";

230

String returning() default "";

231

String argNames() default "";

232

}

233

234

// After throwing advice

235

@Retention(RetentionPolicy.RUNTIME)

236

@Target(ElementType.METHOD)

237

public @interface AfterThrowing {

238

String value() default "";

239

String pointcut() default "";

240

String throwing() default "";

241

String argNames() default "";

242

}

243

244

// After (finally) advice

245

@Retention(RetentionPolicy.RUNTIME)

246

@Target(ElementType.METHOD)

247

public @interface After {

248

String value();

249

String argNames() default "";

250

}

251

252

// Around advice

253

@Retention(RetentionPolicy.RUNTIME)

254

@Target(ElementType.METHOD)

255

public @interface Around {

256

String value();

257

String argNames() default "";

258

}

259

```

260

261

### Join Points

262

263

```java { .api }

264

// Runtime context for advice execution

265

public interface JoinPoint {

266

String toString();

267

String toShortString();

268

String toLongString();

269

Object getThis();

270

Object getTarget();

271

Object[] getArgs();

272

Signature getSignature();

273

SourceLocation getSourceLocation();

274

String getKind();

275

StaticPart getStaticPart();

276

}

277

278

// For around advice that can control method execution

279

public interface ProceedingJoinPoint extends JoinPoint {

280

Object proceed() throws Throwable;

281

Object proceed(Object[] args) throws Throwable;

282

}

283

284

// Signature information

285

public interface Signature {

286

String toString();

287

String toShortString();

288

String toLongString();

289

String getName();

290

int getModifiers();

291

Class<?> getDeclaringType();

292

String getDeclaringTypeName();

293

}

294

295

public interface MethodSignature extends CodeSignature {

296

Class<?> getReturnType();

297

Method getMethod();

298

}

299

```

300

301

## Practical Usage Examples

302

303

### Basic AspectJ Aspects

304

305

```java { .api }

306

// Logging aspect

307

@Aspect

308

@Component

309

public class LoggingAspect {

310

311

private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

312

313

// Pointcut for all service methods

314

@Pointcut("execution(* com.example.service.*.*(..))")

315

public void serviceLayer() {}

316

317

// Pointcut for all repository methods

318

@Pointcut("execution(* com.example.repository.*.*(..))")

319

public void repositoryLayer() {}

320

321

// Combined pointcut

322

@Pointcut("serviceLayer() || repositoryLayer()")

323

public void businessLayer() {}

324

325

// Before advice

326

@Before("businessLayer()")

327

public void logBefore(JoinPoint joinPoint) {

328

logger.info("Calling method: {}.{}() with args: {}",

329

joinPoint.getTarget().getClass().getSimpleName(),

330

joinPoint.getSignature().getName(),

331

Arrays.toString(joinPoint.getArgs()));

332

}

333

334

// After returning advice

335

@AfterReturning(pointcut = "businessLayer()", returning = "result")

336

public void logAfterReturning(JoinPoint joinPoint, Object result) {

337

logger.info("Method {}.{}() returned: {}",

338

joinPoint.getTarget().getClass().getSimpleName(),

339

joinPoint.getSignature().getName(),

340

result);

341

}

342

343

// After throwing advice

344

@AfterThrowing(pointcut = "businessLayer()", throwing = "exception")

345

public void logAfterThrowing(JoinPoint joinPoint, Exception exception) {

346

logger.error("Method {}.{}() threw exception: {}",

347

joinPoint.getTarget().getClass().getSimpleName(),

348

joinPoint.getSignature().getName(),

349

exception.getMessage(), exception);

350

}

351

}

352

```

353

354

### Performance Monitoring Aspect

355

356

```java { .api }

357

@Aspect

358

@Component

359

public class PerformanceAspect {

360

361

private static final Logger logger = LoggerFactory.getLogger(PerformanceAspect.class);

362

363

@Around("@annotation(Timed)")

364

public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {

365

long startTime = System.currentTimeMillis();

366

367

try {

368

Object result = joinPoint.proceed();

369

return result;

370

} finally {

371

long endTime = System.currentTimeMillis();

372

long executionTime = endTime - startTime;

373

374

logger.info("Method {}.{}() executed in {} ms",

375

joinPoint.getTarget().getClass().getSimpleName(),

376

joinPoint.getSignature().getName(),

377

executionTime);

378

}

379

}

380

381

@Around("execution(* com.example.service.*.*(..))")

382

public Object monitorServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {

383

String methodName = joinPoint.getSignature().getName();

384

StopWatch stopWatch = new StopWatch();

385

386

stopWatch.start();

387

try {

388

Object result = joinPoint.proceed();

389

return result;

390

} finally {

391

stopWatch.stop();

392

logger.debug("Service method {} took {} ms", methodName, stopWatch.getTotalTimeMillis());

393

}

394

}

395

}

396

397

// Custom annotation for marking methods to be timed

398

@Target(ElementType.METHOD)

399

@Retention(RetentionPolicy.RUNTIME)

400

public @interface Timed {

401

String value() default "";

402

}

403

```

404

405

### Security Aspect

406

407

```java { .api }

408

@Aspect

409

@Component

410

public class SecurityAspect {

411

412

@Autowired

413

private SecurityService securityService;

414

415

@Before("@annotation(requiresRole)")

416

public void checkRole(JoinPoint joinPoint, RequiresRole requiresRole) {

417

String[] requiredRoles = requiresRole.value();

418

419

if (!securityService.hasAnyRole(requiredRoles)) {

420

throw new SecurityException("Access denied. Required roles: " +

421

Arrays.toString(requiredRoles));

422

}

423

}

424

425

@Around("@annotation(requiresPermission)")

426

public Object checkPermission(ProceedingJoinPoint joinPoint, RequiresPermission requiresPermission) throws Throwable {

427

String permission = requiresPermission.value();

428

429

if (!securityService.hasPermission(permission)) {

430

throw new SecurityException("Access denied. Required permission: " + permission);

431

}

432

433

return joinPoint.proceed();

434

}

435

}

436

437

// Security annotations

438

@Target(ElementType.METHOD)

439

@Retention(RetentionPolicy.RUNTIME)

440

public @interface RequiresRole {

441

String[] value();

442

}

443

444

@Target(ElementType.METHOD)

445

@Retention(RetentionPolicy.RUNTIME)

446

public @interface RequiresPermission {

447

String value();

448

}

449

450

// Usage in service classes

451

@Service

452

public class UserService {

453

454

@RequiresRole({"ADMIN", "USER_MANAGER"})

455

public User createUser(User user) {

456

// Create user logic

457

return user;

458

}

459

460

@RequiresPermission("user:delete")

461

public void deleteUser(Long userId) {

462

// Delete user logic

463

}

464

}

465

```

466

467

### Caching Aspect

468

469

```java { .api }

470

@Aspect

471

@Component

472

public class CachingAspect {

473

474

private final CacheManager cacheManager;

475

476

public CachingAspect(CacheManager cacheManager) {

477

this.cacheManager = cacheManager;

478

}

479

480

@Around("@annotation(cacheable)")

481

public Object cache(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {

482

String cacheName = cacheable.value();

483

String key = generateKey(joinPoint);

484

485

// Check cache first

486

Object cached = cacheManager.get(cacheName, key);

487

if (cached != null) {

488

return cached;

489

}

490

491

// Execute method and cache result

492

Object result = joinPoint.proceed();

493

cacheManager.put(cacheName, key, result);

494

495

return result;

496

}

497

498

@After("@annotation(cacheEvict)")

499

public void evictCache(JoinPoint joinPoint, CacheEvict cacheEvict) {

500

String cacheName = cacheEvict.value();

501

if (cacheEvict.allEntries()) {

502

cacheManager.clear(cacheName);

503

} else {

504

String key = generateKey(joinPoint);

505

cacheManager.evict(cacheName, key);

506

}

507

}

508

509

private String generateKey(JoinPoint joinPoint) {

510

StringBuilder keyBuilder = new StringBuilder();

511

keyBuilder.append(joinPoint.getSignature().getName());

512

513

for (Object arg : joinPoint.getArgs()) {

514

keyBuilder.append(":").append(arg != null ? arg.toString() : "null");

515

}

516

517

return keyBuilder.toString();

518

}

519

}

520

521

@Target(ElementType.METHOD)

522

@Retention(RetentionPolicy.RUNTIME)

523

public @interface Cacheable {

524

String value();

525

}

526

527

@Target(ElementType.METHOD)

528

@Retention(RetentionPolicy.RUNTIME)

529

public @interface CacheEvict {

530

String value();

531

boolean allEntries() default false;

532

}

533

```

534

535

### Programmatic AOP with ProxyFactory

536

537

```java { .api }

538

// Using ProxyFactory directly for programmatic AOP

539

public class AopExample {

540

541

public void demonstrateProxyFactory() {

542

// Create target object

543

UserService target = new UserServiceImpl();

544

545

// Create proxy factory

546

ProxyFactory proxyFactory = new ProxyFactory(target);

547

548

// Add advice

549

proxyFactory.addAdvice(new MethodInterceptor() {

550

@Override

551

public Object invoke(MethodInvocation invocation) throws Throwable {

552

System.out.println("Before method: " + invocation.getMethod().getName());

553

554

try {

555

Object result = invocation.proceed();

556

System.out.println("After method: " + invocation.getMethod().getName());

557

return result;

558

} catch (Exception e) {

559

System.out.println("Exception in method: " + invocation.getMethod().getName());

560

throw e;

561

}

562

}

563

});

564

565

// Get proxy

566

UserService proxy = (UserService) proxyFactory.getProxy();

567

568

// Use proxy - advice will be applied

569

User user = proxy.findById(1L);

570

}

571

}

572

573

// Method-specific interception

574

public class ValidationInterceptor implements MethodInterceptor {

575

576

@Override

577

public Object invoke(MethodInvocation invocation) throws Throwable {

578

Method method = invocation.getMethod();

579

Object[] args = invocation.getArguments();

580

581

// Validate arguments before method execution

582

for (Object arg : args) {

583

if (arg == null) {

584

throw new IllegalArgumentException("Null argument not allowed for method: " + method.getName());

585

}

586

}

587

588

// Proceed with method execution

589

return invocation.proceed();

590

}

591

}

592

593

// Using specific pointcuts

594

public class ServicePointcutAdvisor extends DefaultPointcutAdvisor {

595

596

public ServicePointcutAdvisor() {

597

// Create pointcut that matches all methods in service classes

598

Pointcut pointcut = new StaticMethodMatcherPointcut() {

599

@Override

600

public boolean matches(Method method, Class<?> targetClass) {

601

return targetClass.getSimpleName().endsWith("Service");

602

}

603

};

604

605

setPointcut(pointcut);

606

setAdvice(new LoggingInterceptor());

607

}

608

}

609

```

610

611

### Advanced Pointcut Expressions

612

613

```java { .api }

614

@Aspect

615

@Component

616

public class AdvancedPointcutAspect {

617

618

// Method execution pointcuts

619

@Pointcut("execution(public * com.example..*(..))")

620

public void publicMethods() {}

621

622

@Pointcut("execution(* com.example.service.*.save*(..))")

623

public void saveMethods() {}

624

625

@Pointcut("execution(* com.example.repository.*Repository.find*(..))")

626

public void findMethods() {}

627

628

// Annotation-based pointcuts

629

@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")

630

public void transactionalMethods() {}

631

632

@Pointcut("@within(org.springframework.stereotype.Service)")

633

public void serviceClasses() {}

634

635

// Type-based pointcuts

636

@Pointcut("within(com.example.service..*)")

637

public void withinServicePackage() {}

638

639

@Pointcut("target(com.example.service.UserService)")

640

public void userServiceTarget() {}

641

642

// Argument-based pointcuts

643

@Pointcut("args(java.lang.String, ..)")

644

public void methodsWithStringFirstArg() {}

645

646

@Pointcut("args(userId) && execution(* com.example.service.*.*(..))")

647

public void serviceMethodsWithUserId(Long userId) {}

648

649

// Bean pointcuts

650

@Pointcut("bean(*Service)")

651

public void serviceBeans() {}

652

653

@Pointcut("bean(userService)")

654

public void userServiceBean() {}

655

656

// Complex combinations

657

@Around("(serviceClasses() || repositoryClasses()) && publicMethods() && !execution(* toString())")

658

public Object aroundBusinessMethods(ProceedingJoinPoint joinPoint) throws Throwable {

659

// Complex advice logic

660

return joinPoint.proceed();

661

}

662

663

// Pointcut with argument binding

664

@Before("execution(* com.example.service.*.update*(..)) && args(entity)")

665

public void beforeUpdate(Object entity) {

666

System.out.println("Updating entity: " + entity);

667

}

668

669

// Multiple argument binding

670

@Before("execution(* com.example.service.UserService.updateUser(..)) && args(userId, userData)")

671

public void beforeUserUpdate(Long userId, UserData userData) {

672

System.out.println("Updating user " + userId + " with data: " + userData);

673

}

674

}

675

```

676

677

### Integration with Spring Features

678

679

```java { .api }

680

// AOP with Spring Boot auto-configuration

681

@SpringBootApplication

682

@EnableAspectJAutoProxy

683

public class Application {

684

public static void main(String[] args) {

685

SpringApplication.run(Application.class, args);

686

}

687

}

688

689

// Conditional aspects based on properties

690

@Aspect

691

@Component

692

@ConditionalOnProperty(name = "app.audit.enabled", havingValue = "true")

693

public class AuditAspect {

694

695

@AfterReturning(pointcut = "@annotation(Auditable)", returning = "result")

696

public void auditMethod(JoinPoint joinPoint, Object result) {

697

// Audit logic

698

}

699

}

700

701

// Profile-specific aspects

702

@Aspect

703

@Component

704

@Profile("development")

705

public class DebugAspect {

706

707

@Around("execution(* com.example..*(..))")

708

public Object debugMethod(ProceedingJoinPoint joinPoint) throws Throwable {

709

System.out.println("DEBUG: Entering " + joinPoint.getSignature().getName());

710

Object result = joinPoint.proceed();

711

System.out.println("DEBUG: Exiting " + joinPoint.getSignature().getName());

712

return result;

713

}

714

}

715

716

// Integration with other Spring features

717

@Aspect

718

@Component

719

public class IntegrationAspect {

720

721

@Autowired

722

private ApplicationEventPublisher eventPublisher;

723

724

@Autowired

725

private MeterRegistry meterRegistry;

726

727

@AfterReturning("execution(* com.example.service.OrderService.createOrder(..))")

728

public void afterOrderCreated(JoinPoint joinPoint) {

729

// Publish application event

730

eventPublisher.publishEvent(new OrderCreatedEvent(this, "Order created"));

731

732

// Update metrics

733

meterRegistry.counter("orders.created").increment();

734

}

735

}

736

```

737

738

## Configuration and Best Practices

739

740

### XML Configuration (Alternative)

741

742

```xml

743

<!-- Enable AspectJ auto proxy in XML -->

744

<aop:aspectj-autoproxy proxy-target-class="true"/>

745

746

<!-- Register aspect as bean -->

747

<bean id="loggingAspect" class="com.example.LoggingAspect"/>

748

749

<!-- Programmatic AOP configuration -->

750

<aop:config>

751

<aop:aspect ref="loggingAspect">

752

<aop:pointcut id="businessMethods"

753

expression="execution(* com.example.service.*.*(..))"/>

754

<aop:before method="logBefore" pointcut-ref="businessMethods"/>

755

<aop:after-returning method="logAfterReturning"

756

pointcut-ref="businessMethods"

757

returning="result"/>

758

</aop:aspect>

759

</aop:config>

760

```

761

762

### Performance Considerations

763

764

```java { .api }

765

// Optimize pointcut expressions for better performance

766

@Aspect

767

@Component

768

public class OptimizedAspect {

769

770

// More specific pointcuts perform better

771

@Pointcut("execution(* com.example.service.UserService.*(..))") // Good

772

public void userServiceMethods() {}

773

774

@Pointcut("execution(* *.*(..))") // Avoid - too broad

775

public void allMethods() {}

776

777

// Use within() for better performance when targeting packages

778

@Pointcut("within(com.example.service..*)")

779

public void servicePackage() {}

780

781

// Combine pointcuts efficiently

782

@Pointcut("servicePackage() && execution(public * save*(..))")

783

public void publicSaveMethods() {}

784

}

785

```

786

787

Spring AOP provides a powerful and flexible way to implement cross-cutting concerns in your applications. It integrates seamlessly with the Spring IoC container and provides both annotation-driven and programmatic approaches to aspect-oriented programming.