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· 14 min read

IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions

How Spring resolves circular dependencies (three-level cache, and why constructor injection fails), constructor/method selection for injection, prototype beans inside singletons (lookup, ObjectProvider, scoped proxies), @Lazy, field vs constructor vs setter injection internals, optional dependencies, ObjectFactory/Provider, the full bean lifecycle with BeanPostProcessors and BeanFactoryPostProcessors, @PostConstruct vs InitializingBean, request/session scopes, post-processor ordering, SmartInitializingSingleton, SmartLifecycle, FactoryBean, ApplicationContextAware as an anti-pattern, lazy vs eager initialisation, @Component vs @Bean, and context hierarchies.

Published September 25, 2026


How to use this lesson

At 8+ years you're expected to explain how the container works, not just which annotations to use:

  1. bean definitions;
  2. factory post-processing;
  3. instantiation;
  4. population;
  5. initialisation with post-processors (where proxies are created);
  6. destruction.

Most "weird Spring behaviour" questions are answered by knowing where in this pipeline something happens.

Q1. How does Spring resolve circular dependencies? Does it work with constructor injection?

Short answer: For singleton beans with field or setter injection, Spring can break some cycles using early references. It keeps a three-level singleton cache:

  1. singletonObjects: fully initialised beans;
  2. earlySingletonObjects: early references;
  3. singletonFactories: ObjectFactory callbacks that can produce an early reference, possibly a proxy, through SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference.

When A is being created, and needs B, which needs A, B receives A's early reference, before A's properties are populated.

Constructor injection can't be resolved this way. A can't be instantiated without B, and B can't be instantiated without A, so you get a BeanCurrentlyInCreationException.

Important:

  • Since Spring Boot 2.6, circular references are prohibited by default (spring.main.allow-circular-references=false). Startup fails, and shows the cycle.
  • The right fix is redesign:
    • extract the shared logic into a third bean;
    • use events;
    • or inject one side lazily (@Lazy or ObjectProvider) as a last resort.

Learn it in depth → IoC Container Fundamentals

Q2. How does Spring decide which constructor or method to use for dependency injection?

Short answer:

  • One constructor: it's used automatically (since Spring 4.3; no @Autowired needed).

  • Several constructors: the one annotated with @Autowired (or @Inject) is used. With @Autowired(required = false) on several of them, Spring picks the "greediest" satisfiable one (the most parameters it can resolve). If there's no annotation and no default constructor, it fails.

  • Records and Kotlin data classes: the canonical or primary constructor.

  • Parameter resolution:

    1. by type;
    2. then narrowed by @Qualifier;
    3. then @Primary or @Priority;
    4. then by parameter name matching a bean name (which needs -parameters compilation, the default with Boot's plugins).

    Optional<T>, ObjectProvider<T>, List<T>/Map<String,T> (all beans of that type) and @Value are also supported.

  • @Bean factory methods: their parameters are resolved the same way. For overloaded @Bean methods, the greediest satisfiable method wins.

  • Setter or method injection: any method annotated with @Autowired is called after construction.

Q3. Can you inject a prototype bean into a singleton? What are the caveats?

Short answer: You can, but the prototype is injected only once, when the singleton is created. The singleton then reuses that same "prototype" instance forever, which defeats the scope. To get a new instance per use:

  • ObjectProvider<T> (preferred) or ObjectFactory<T>/JSR-330 Provider<T>, calling getObject() each time;
  • @Lookup method injection (Spring overrides an abstract or stub method with CGLIB);
  • a scoped proxy: @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS). This gives a new target on every method call, which is usually surprising for prototypes, and is mainly used for request and session scopes.
@Service
class ReportService {
    private final ObjectProvider<ReportBuilder> builders;          // ReportBuilder is @Scope("prototype")
    ReportService(ObjectProvider<ReportBuilder> builders) { this.builders = builders; }
    Report build(Criteria c) { return builders.getObject().withCriteria(c).build(); }   // a fresh builder each time
}

Key points to cover:

  • Spring doesn't manage prototypes after creation. Destruction callbacks aren't called, so release their resources yourself.

Q4. What does @Lazy do in dependency injection, and how does it affect bean initialisation?

Short answer:

  • On a bean (@Lazy @Component): the singleton is created on first request, instead of at context startup. spring.main.lazy-initialization=true makes all beans lazy.
  • On an injection point (@Lazy on a constructor parameter or field): Spring injects a lazy-resolution proxy. The real bean is looked up and created on the first method call. This is also used to break circular dependencies.

The trade-offs:

  • Faster startup, and lower memory use for rarely used beans (useful in development and tests).
  • Configuration errors surface at runtime, not at startup: a missing property or bad wiring fails on the first request.
  • First-request latency.
  • Proxies can confuse getClass() or ==.

In production, eager initialisation is safer. Use lazy selectively.

Q5. What happens internally when @Autowired is used on a field, a constructor, or a setter?

Short answer: It's all processed by AutowiredAnnotationBeanPostProcessor:

  • Constructor: resolved during instantiation. Dependencies are available before the object exists, so fields can be final, the object is always complete, and cycles fail fast.
  • Field: injected during the population phase (postProcessProperties), after the constructor, through reflection (Field.setAccessible(true)). The object is briefly incomplete, fields can't be final, and tests need reflection or a Spring context to set them.
  • Setter or method: also in the population phase, by invoking the method. That makes it suitable for optional or reconfigurable dependencies.

The recommendation: constructor injection for mandatory dependencies (immutable, explicit, testable), and setters only for optional ones. Avoid field injection outside tests.

Q6. How do you inject a dependency only if it's available (optional)?

Short answer: Several ways:

  • ObjectProvider<T>: ifAvailable(consumer), getIfAvailable(defaultSupplier), getIfUnique(). It's lazy and flexible (preferred).
  • Optional<T> as a constructor or @Bean parameter.
  • @Autowired(required = false) on a setter or field: it stays null if absent.
  • @Nullable on a parameter.
  • List<T>: an empty list if there are no beans.
  • Conditional beans (@ConditionalOnBean/@ConditionalOnMissingBean), to provide defaults.
@Service
class NotificationService {
    private final ObjectProvider<SmsSender> sms;
    NotificationService(ObjectProvider<SmsSender> sms) { this.sms = sms; }
    void notify(User u, String msg) {
        sms.ifAvailable(s -> s.send(u.phone(), msg));    // only if an SmsSender bean exists
    }
}

Q7. What are ObjectFactory and Provider for in dependency injection?

Short answer: They inject a factory handle instead of the bean itself, so the bean is looked up lazily, on each call:

  • ObjectFactory<T> (Spring): getObject().
  • jakarta.inject.Provider<T> (JSR-330): get(), a standard, portable API.
  • ObjectProvider<T> (Spring 4.3+) extends ObjectFactory, with optional, unique and stream access (getIfAvailable, orderedStream()).

Uses:

  • getting prototype or request-scoped beans from singletons;
  • optional dependencies;
  • breaking cycles;
  • avoiding eager creation of expensive beans.

Q8. Explain the full Spring bean lifecycle, including post-processors.

Short answer:

  1. Bean definitions are loaded (component scanning, @Bean methods, auto-configuration imports).
  2. BeanFactoryPostProcessors / BeanDefinitionRegistryPostProcessors modify the definitions (for example ConfigurationClassPostProcessor processes @Configuration, and PropertySourcesPlaceholderConfigurer resolves ${...}).
  3. BeanPostProcessors are registered.
  4. Per bean:
    1. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation (which can short-circuit with a proxy);
    2. instantiation (the constructor or factory method);
    3. MergedBeanDefinitionPostProcessor;
    4. an early reference is exposed for cycles;
    5. population: property values, @Autowired, @Value, @Resource;
    6. Aware callbacks: BeanNameAware, BeanFactoryAware, ApplicationContextAware (and the other *Aware interfaces through a post-processor);
    7. postProcessBeforeInitialization, including @PostConstruct (handled by CommonAnnotationBeanPostProcessor);
    8. InitializingBean.afterPropertiesSet();
    9. a custom initMethod;
    10. postProcessAfterInitialization: AOP proxies are created here (AbstractAutoProxyCreator), for @Transactional, @Async, @Cacheable and aspects.
  5. After all the singletons are created: SmartInitializingSingleton.afterSingletonsInstantiated(), then the SmartLifecycle.start() phases, then ContextRefreshedEvent and ApplicationReadyEvent (in Boot).
  6. Shutdown: SmartLifecycle.stop() (in reverse phase order), then @PreDestroy, then DisposableBean.destroy(), then a custom destroyMethod. Only for singletons, not prototypes.

Learn it in depth → Bean Lifecycle In Detail

Q9. What's the difference between @PostConstruct and InitializingBean?

Short answer: Both run after dependency injection, before the bean is used:

  • @PostConstruct (Jakarta annotation) runs first. It's not coupled to Spring's API (it's portable, and a plain annotation on any method), and it's processed by CommonAnnotationBeanPostProcessor.
  • InitializingBean.afterPropertiesSet() runs second. It couples your class to Spring, but doesn't depend on annotation processing. Framework code uses it.

Then the @Bean(initMethod = "...") runs. Prefer @PostConstruct, or better, do the initialisation in the constructor, if dependencies are constructor-injected. Keep init methods fast, and don't do heavy I/O in them. The same pattern applies to destruction: @PreDestroy, then DisposableBean, then destroyMethod.

Q10. When would you use @Scope("request") or @Scope("session")?

Short answer:

  • Request scope: one instance per HTTP request. For per-request state that many components need: a request context (correlation ID, tenant, locale, user), per-request caches, or accumulating audit information.
  • Session scope: one instance per HTTP session. For user-session state in stateful web applications: a wizard's progress, a shopping cart in a server-rendered application, user preferences.

Injecting them into singletons requires a scoped proxy: @RequestScope/@SessionScope default to proxyMode = TARGET_CLASS, so every call resolves to the current request's instance.

The caveats:

  • they're not available outside a web request thread (async tasks, schedulers), which gives IllegalStateException unless context is propagated;
  • session beans must be serialisable for session replication;
  • stateless REST APIs usually avoid session scope, preferring tokens and external stores.

Q11. What are bean post-processors, and how does Spring Boot use them internally?

Short answer: A BeanPostProcessor is a container extension that can modify or wrap every bean instance around initialisation (postProcessBeforeInitialization, postProcessAfterInitialization). Its subinterfaces hook in earlier (instantiation, property population, early references). Much of Spring works through them:

  • AutowiredAnnotationBeanPostProcessor: @Autowired, @Value, @Inject.
  • CommonAnnotationBeanPostProcessor: @PostConstruct, @PreDestroy, @Resource.
  • AnnotationAwareAspectJAutoProxyCreator (an auto-proxy creator): wraps beans in AOP proxies (@Transactional, @Cacheable, @Async through AsyncAnnotationBeanPostProcessor, aspects).
  • ConfigurationPropertiesBindingPostProcessor: binds @ConfigurationProperties.
  • ApplicationContextAwareProcessor, ScheduledAnnotationBeanPostProcessor (@Scheduled), PersistenceExceptionTranslationPostProcessor (@Repository), Micrometer and observation instrumentation, validation post-processors.

You can write your own, for cross-cutting wrapping, or to validate conventions at startup.

Q12. In what order do several post-processors execute, and how do you control it?

Short answer: Post-processors are sorted, and applied in this order:

  1. those implementing PriorityOrdered, first;
  2. then Ordered;
  3. then the rest, in registration order.

Within each group, they're sorted by getOrder() (lower runs first). @Order is honoured for components, but for post-processors, implement Ordered/PriorityOrdered, because they're instantiated very early. The same applies to BeanFactoryPostProcessors. The consequence: a post-processor that runs after the auto-proxy creator sees the proxy, not the target.

Common trap: declaring a BeanPostProcessor as a non-static @Bean method in a @Configuration class that has other dependencies. That forces early instantiation of that class and its dependencies, which then aren't eligible for post-processing ("Bean X is not eligible for getting processed by all BeanPostProcessors"). Declare post-processor @Bean methods static.

Q13. What is SmartInitializingSingleton for?

Short answer: A callback, afterSingletonsInstantiated(), invoked once, after all non-lazy singletons in the context have been fully created and initialised. It's ideal for logic that needs all the beans present:

  • scanning the context for annotated methods (Spring itself uses it for @EventListener registration and @JmsListener/@KafkaListener endpoint setup);
  • validating cross-bean configuration;
  • building registries of strategies.

Unlike @PostConstruct (per bean, where other beans might not be ready yet), it runs after the whole singleton graph is ready, but before SmartLifecycle components are started.

Q14. What is SmartLifecycle?

Short answer: An interface for components with start and stop semantics tied to the context lifecycle:

  • start()/stop(Runnable callback) for asynchronous shutdown;
  • isAutoStartup();
  • getPhase() for ordering: lower phases start first and stop last.

Spring uses it for message listener containers (Kafka, JMS), schedulers, embedded web servers (Boot's WebServerStartStopLifecycle), and graceful shutdown. Use it when your bean runs background activity (a poller, a consumer) that must start only when the context is ready, and stop gracefully, in the right order (for example, stop consuming messages before the database pool closes).

Q15. What are FactoryBeans, and how do they differ from regular beans?

Short answer: A FactoryBean<T> is a bean whose job is to produce another object. When you inject or getBean("name"), you get the product (getObject()), not the factory. getBean("&name") returns the factory itself. It's used for complex creation logic that's awkward in plain configuration: LocalContainerEntityManagerFactoryBean, SqlSessionFactoryBean (MyBatis), proxy factories, and Spring Data repository factories. Methods: getObject(), getObjectType() (important for type matching before creation), and isSingleton().

With Java config, a @Bean method is usually simpler. FactoryBean is mainly for framework and library integration.

Q16. What is ApplicationContextAware for, and when is it an anti-pattern?

Short answer: Implementing ApplicationContextAware gives a bean a reference to the ApplicationContext, through setApplicationContext. Legitimate uses: framework or infrastructure code that must look up beans dynamically by name or type at runtime (plugin registries), publish events (though ApplicationEventPublisher is better), or access resources and environment.

It's an anti-pattern when business code uses it as a service locator (context.getBean(OrderService.class)), or keeps a static holder (SpringContext.getBean(...) from anywhere):

  • it hides dependencies;
  • it breaks testability;
  • it couples code to Spring;
  • it causes ordering bugs (the context may not be ready yet).

Prefer constructor injection, ObjectProvider, or injected Map<String, Strategy> registries.

Q17. How does Spring handle lazy vs eager initialisation of beans?

Short answer:

  • Eager (the default for singletons): all non-lazy singletons are created at startup, during refresh() (preInstantiateSingletons). Misconfigurations fail fast, the first requests are fast, and warm-up happens before traffic arrives.
  • Lazy: created on first use, through @Lazy on the bean, @Lazy on a @Configuration class (all its beans), or globally with spring.main.lazy-initialization=true. Startup is faster, with lower initial memory, but errors are deferred, and the first request is slower.
  • Prototype, request and session scopes are always created on demand.

You can combine them: global lazy initialisation plus @Lazy(false) for critical beans.

Q18. What's the difference between @Component and @Bean?

Short answer:

  • @Component (and @Service, @Repository, @Controller) goes on your own classes. They're discovered by component scanning, and Spring instantiates them through their constructors.
  • @Bean goes on a method in a @Configuration class that returns an object Spring should manage. Use it for third-party classes you can't annotate (ObjectMapper, RestClient, DataSource), for conditional or programmatic construction (different implementations per profile), or when you need several beans of the same type, configured differently.

Both produce ordinary singleton beans. @Bean gives explicit control over creation (initMethod, destroyMethod, scope and conditions per method).

Q19. What is a Spring context hierarchy?

Short answer: ApplicationContexts can have a parent. A child context sees beans in its parent, but not vice versa, and a child can override parent beans locally. Uses:

  • Classic Spring MVC: a root WebApplicationContext (services, repositories, from ContextLoaderListener) plus a child context per DispatcherServlet (controllers, and MVC infrastructure).
  • Spring Boot: SpringApplicationBuilder.parent(...).child(...), for modular applications.
  • Spring Cloud: a bootstrap context (the legacy parent), and per-client child contexts for OpenFeign and LoadBalancer configuration (each client gets its own context, with isolated configuration).
  • Tests: @ContextHierarchy.

The caveats:

  • AOP post-processors and @Transactional configuration don't cross contexts automatically.
  • Events propagate from child to parent.
  • Duplicate definitions can cause confusion about which bean is used.

Q20. What is a BeanFactoryPostProcessor?

Short answer: A hook that runs after bean definitions are loaded, but before any beans are instantiated. It can read and modify the bean definitions (metadata: class, scope, property values, lazy flag), or register new ones (BeanDefinitionRegistryPostProcessor). Examples:

  • ConfigurationClassPostProcessor: parses @Configuration, @ComponentScan, @Import and @Bean into definitions (it's the heart of annotation configuration);
  • PropertySourcesPlaceholderConfigurer: resolves ${...} placeholders in definitions;
  • CustomScopeConfigurer, and Spring Data's repository registration.

Contrast it with a BeanPostProcessor, which works on bean instances, after instantiation. A BeanFactoryPostProcessor must not instantiate beans (calling getBean in it causes premature creation), and should be declared as a static @Bean.

Follow-up questions this topic invites — and their answers

Q: Why are @Configuration classes proxied with CGLIB? A: So that inter-bean method calls (dataSource() called inside another @Bean method) return the container-managed singleton, instead of creating new instances ("full" mode). With @Configuration(proxyBeanMethods = false) ("lite" mode), there's no proxy and faster startup, but direct calls create new objects. So inject beans as method parameters instead.

Q: What's the difference between @Resource, @Inject and @Autowired? A: @Autowired (Spring) resolves by type, then qualifier or name, and supports required=false. @Inject (JSR-330) behaves similarly, but has no required attribute. @Resource (Jakarta) resolves by name first (the field or setter name), then by type.

Q: How do you run code after the application has fully started? A: Use an ApplicationRunner/CommandLineRunner bean, or listen for ApplicationReadyEvent. Avoid heavy work in @PostConstruct: it delays startup, and other beans or the web server may not be ready yet.

Q: What happens if two beans implement the same interface and you inject it without a qualifier? A: NoUniqueBeanDefinitionException, unless one is @Primary, a qualifier or parameter name matches a bean name, or you inject a List/Map of all implementations.

Previous

SOLID, Clean Code & Anti-Patterns — Interview Questions

Next

Spring AOP, Proxies & @Async Internals — Interview Questions

AI Tutor

Lesson: IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.