Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
Chaturmind
← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 8+ Years (Senior & Lead)Spring & Spring Boot Internals
✓ FreeAdvanced· 8 min read

Spring AOP, Proxies & @Async Internals — Interview Questions

Proxy-based AOP vs AspectJ weaving, JDK dynamic proxies vs CGLIB in Spring, why private methods and self-invocation escape advice (@Transactional/@Cacheable), restricting aspects to specific beans, @Around vs @Before/@AfterReturning, ordering multiple aspects, exceptions thrown from advice, conditional aspects via profiles/properties, Spring AOP's limits compared with full AspectJ, and how @Async works behind the scenes.

Published September 25, 2026


How to use this lesson

Nearly every "Spring annotation didn't work" bug comes down to proxies:

  • @Transactional on a private method;
  • a this. call;
  • a bean that isn't proxied because of post-processor ordering;
  • a final class.

Explain the proxy mechanics once, and the other questions follow.

Q1. How does Spring AOP work internally: proxies, or bytecode weaving?

Short answer: Proxies, at runtime.

  1. During bean initialisation, an auto-proxy creator (AnnotationAwareAspectJAutoProxyCreator, a BeanPostProcessor) checks each bean against all the advisors (pointcut + advice, from @Aspect classes and infrastructure advisors like the transaction or cache interceptors).
  2. If any advisor matches, it returns a proxy instead of the raw bean, and that proxy is what gets injected everywhere.
  3. A method call on the proxy runs an interceptor chain (ReflectiveMethodInvocation.proceed()), then invokes the target.

Spring uses AspectJ's annotation and pointcut syntax, but not AspectJ weaving. Full AspectJ (compile-time or load-time weaving) modifies the bytecode of the target classes themselves. It's available in Spring (@EnableLoadTimeWeaving, or the AspectJ compiler), but rarely used.

Learn it in depth → Spring AOP

Q2. What's the difference between JDK dynamic proxies and CGLIB proxies in Spring?

Short answer:

  • JDK dynamic proxy: implements the bean's interfaces. Clients must inject by interface. Only interface methods are advised.
  • CGLIB proxy: generates a subclass of the bean class at runtime. It can be injected by class, and advises public and protected methods (package-private too, since Spring 6, with limitations). It can't proxy final classes or final methods, and for constructor calls Spring uses Objenesis, to avoid running the constructor twice.
  • The defaults: Spring Framework uses JDK proxies when interfaces exist, but Spring Boot sets spring.aop.proxy-target-class=true, so CGLIB is the default in Boot applications.

Q3. Can Spring AOP advise private methods? Why not?

Short answer: No.

  • JDK proxies only implement interface methods, which are public.
  • CGLIB proxies work by overriding methods in a subclass, and private methods can't be overridden (nor can final or static ones).

So advice on private methods is silently ignored: @Transactional, @Cacheable and @Async on a private method have no effect. Use full AspectJ weaving if you truly need to advise private methods, or refactor the method into a public method on a separate bean.

Q4. What happens if a self-invoked method is annotated with @Transactional or @Cacheable?

Short answer: The annotation is bypassed. this.method() inside the bean calls the target object directly, not the proxy, so no transaction starts and no caching happens. It's a very common production bug: for example, a public @Transactional(REQUIRES_NEW) audit method, called from another method of the same class, runs in the outer transaction, or in none.

Fixes:

  1. Move the method to another bean (the cleanest option).
  2. Self-inject lazily: @Lazy @Autowired private MyService self;, then call self.method().
  3. AopContext.currentProxy(), with @EnableAspectJAutoProxy(exposeProxy = true).
  4. Programmatic TransactionTemplate, or the cache API.
  5. AspectJ mode (@EnableTransactionManagement(mode = ASPECTJ)), which weaves the bytecode, so self-calls are advised.

Q5. How would you apply an aspect only to specific beans?

Short answer: Narrow the pointcut:

  • Within a type or package: within(com.shopin.orders..*).
  • By bean name: bean(orderService) or bean(*Repository) (Spring-specific).
  • By annotation: @annotation(com.shopin.Audited) (methods), or @within(com.shopin.Audited) (types). This is the cleanest and most explicit option, because it's opt-in.
  • By type: target(com.shopin.PaymentGateway) or this(...).
  • Combinations: execution(public * *(..)) && @within(org.springframework.stereotype.Service).
@Aspect @Component
class AuditAspect {
    @Around("@annotation(audited)")
    Object audit(ProceedingJoinPoint pjp, Audited audited) throws Throwable {
        long start = System.nanoTime();
        try { return pjp.proceed(); }
        finally { auditLog.record(audited.action(), pjp.getSignature().toShortString(), System.nanoTime() - start); }
    }
}

Q6. What's the difference between @Around and @Before/@AfterReturning?

Short answer:

  • @Before: runs before the method. It can't stop the call, except by throwing. Used for validation, security checks and logging.

  • @AfterReturning(returning = "result"): runs after normal completion, and can read the return value, but not replace it.

  • @AfterThrowing: runs on an exception.

  • @After: a finally-style advice.

  • @Around: wraps the invocation, with a ProceedingJoinPoint. It can:

    • decide whether to proceed;
    • modify the arguments (proceed(args));
    • change the return value;
    • catch, translate or retry exceptions;
    • measure time.

    It's the most powerful, and the most error-prone: you must call proceed(), and return its result.

Use the least powerful advice that does the job. Spring's own transaction, cache and retry interceptors are effectively around advice.

Q7. Can several aspects apply to the same method? In what order do they run?

Short answer: Yes. All the matching advisors form one interceptor chain. The order is controlled by @Order or implementing Ordered on the aspect class:

  • the lowest order value has the highest precedence;
  • it's the outermost layer: its "before" part runs first, and its "after" part runs last (an onion model).

Without an explicit order, the order is undefined. Within a single aspect, advice types run in a defined order (@Around, @Before, @After, @AfterReturning, @AfterThrowing), but two advice methods of the same type in one aspect have no guaranteed order. Split them into separate aspects, or merge them.

Key points to cover:

  • Infrastructure ordering matters. For example, you'd want a retry advice outside the transaction advice, so each retry gets a new transaction. Control it with @Order (and @EnableTransactionManagement(order = ...)).

Q8. What happens when an AOP advice throws an exception?

Short answer: The exception propagates to the caller, like one thrown by the target method:

  • In @Before: the target method isn't invoked.
  • In @AfterReturning or @After: the method already ran, and its side effects stay, but the caller still gets an exception. If a transaction advice is outside it, the transaction rolls back. If it's inside it, the commit may already have happened.
  • In @Around: anything thrown, or not caught around proceed(), propagates. An around advice can also swallow or translate exceptions from the target.
  • Checked exceptions: if an advice throws a checked exception the method doesn't declare, the proxy wraps it in UndeclaredThrowableException.

Rule: cross-cutting advice (logging, metrics) should never break the business call. Catch and log inside it.

Q9. How do you make an aspect conditional on Spring profiles or properties?

Short answer: Aspects are beans, so use bean conditions on the aspect class:

  • @Profile("!prod") for a debug or tracing aspect;
  • @ConditionalOnProperty(name = "app.audit.enabled", havingValue = "true") (in Boot);
  • @ConditionalOnClass for optional integrations.

If the bean isn't created, the advisor doesn't exist, and there's no proxying overhead. For runtime toggling, check a feature flag inside the advice (a small cost on every call), and prefer the conditional bean when the decision is static.

@Aspect @Component
@ConditionalOnProperty(prefix = "app.tracing", name = "method-timing", havingValue = "true")
class MethodTimingAspect { /* ... */ }

Q10. What are the limitations of Spring AOP compared with full AspectJ?

Short answer:

  • Only method-execution join points on Spring beans. There's no advice on field access, constructors, static initialisers, or on objects created with new outside the container.
  • No self-invocation interception, and no private, final or static methods.
  • Runtime proxy overhead, plus identity and getClass() surprises.
  • Only public (and, with CGLIB, protected and package-visible) methods.

Full AspectJ (compile-time or load-time weaving) removes all of these: any join point, any object, self-calls, and no proxy indirection. The costs are build and agent complexity, and harder debugging. Use it for cases like @Configurable domain objects, or advising third-party code.

Q11. How does @Async work behind the scenes?

Short answer:

  1. @EnableAsync registers the AsyncAnnotationBeanPostProcessor, which wraps beans that have @Async methods in a proxy, with an AnnotationAsyncExecutionInterceptor.
  2. When a caller invokes an @Async method through the proxy, the interceptor submits a task to an executor, and returns immediately. The executor is:
    • the one named in @Async("imageExecutor");
    • otherwise the AsyncConfigurer executor;
    • otherwise a single TaskExecutor bean;
    • otherwise Boot's auto-configured applicationTaskExecutor (a ThreadPoolTaskExecutor, or a virtual-thread executor when spring.threads.virtual.enabled=true).
  3. Return types:
    • void: exceptions go to the AsyncUncaughtExceptionHandler;
    • Future/CompletableFuture<T>: the result or exception is delivered through the future.
  4. Context: the ThreadLocal-based context (security, MDC, transactions) doesn't carry over unless you add a TaskDecorator. A transaction doesn't span into the async method.

The pitfalls:

  • Self-invocation means the call runs synchronously.
  • Private or final methods are ignored.
  • Unbounded queues in custom executors.
  • Lost exceptions from void methods.
  • Blocking the caller by calling .get() right away defeats the purpose.

Follow-up questions this topic invites — and their answers

Q: How can you tell whether a bean is a proxy? A: AopUtils.isAopProxy(bean), isCglibProxy, isJdkDynamicProxy. AopUtils.getTargetClass(bean) gives the real class. ClassUtils.getUserClass strips the CGLIB suffix ($$SpringCGLIB$$).

Q: Why do JPA entities and Spring beans need non-final classes, or the Kotlin all-open plugin? A: CGLIB (Spring) and Hibernate/ByteBuddy (lazy proxies) create subclasses at runtime. Final classes and methods prevent the overriding they need.

Q: What does proxyTargetClass = true change? A: It forces CGLIB class-based proxies, even when interfaces exist, so the bean can be injected by concrete class. It's Boot's default, to avoid "bean is not of required type" errors.

Q: Is AOP a good fit for business logic? A: No. Keep aspects for true cross-cutting concerns (transactions, security, metrics, auditing, retries). Business rules hidden in aspects are hard to discover, test and debug.

Previous

IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions

Next

Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions

AI Tutor

Lesson: Spring AOP, Proxies & @Async Internals — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.