The IoC container is the core of Spring. It's responsible for creating objects (beans), wiring their dependencies, and managing their complete lifecycle. "Inversion of Control" means instead of your code calling new SomeService(), the container creates and injects it for you.
| Interface | Description | Use When |
|---|---|---|
BeanFactory | Basic container. Lazy initialization by default. Minimal features. | Memory-constrained environments (rarely today) |
ApplicationContext | Superset of BeanFactory. Eager init, event publishing, i18n, AOP support. | Always use this in production |
Common ApplicationContext implementations you'll encounter in interviews:
AnnotationConfigApplicationContext— for standalone apps using@ConfigurationclassesClassPathXmlApplicationContext— legacy XML-based configAnnotationConfigServletWebServerApplicationContext— what Spring Boot uses internally for web apps
BeanNameAware.setBeanName() — bean gets its name from the containerBeanFactoryAware.setBeanFactory() — reference to factory injectedApplicationContextAware.setApplicationContext() — context reference injectedBeanPostProcessor.postProcessBeforeInitialization() — pre-init hook@PostConstruct / InitializingBean.afterPropertiesSet() — init methods calledBeanPostProcessor.postProcessAfterInitialization() — post-init hook (AOP proxy created here)@PreDestroy / DisposableBean.destroy() — cleanup called@PostConstruct, it won't be intercepted by AOP — the proxy doesn't exist yet at Step 7.
All four are specializations of @Component. Functionally they all register the class as a Spring bean. The difference is semantic + one functional addition for @Repository.
It's a meta-annotation — a convenience wrapper for three annotations:
com.example) — so it sees everything under com.example.service, com.example.controller, etc.
Starters are curated dependency bundles. Instead of adding 8 individual Maven/Gradle dependencies for a JPA setup, you add one starter that handles all transitive dependencies at compatible versions.
| Starter | What it pulls in |
|---|---|
spring-boot-starter-web | Spring MVC, Tomcat, Jackson (JSON), validation |
spring-boot-starter-data-jpa | Hibernate, Spring Data, JDBC, transaction mgmt |
spring-boot-starter-security | Spring Security, crypto, config support |
spring-boot-starter-test | JUnit 5, Mockito, AssertJ, MockMvc, Testcontainers |
spring-boot-starter-actuator | Health, metrics, info, environment endpoints |
spring-boot-starter-kafka | Apache Kafka client + Spring Kafka |
spring-boot-starter-parent and get blessed dependency management for free.
- Activate via CLI:
java -jar app.jar --spring.profiles.active=prod - Activate via env:
SPRING_PROFILES_ACTIVE=prod - In tests:
@ActiveProfiles("test") - Spring Boot 3.x: Prefer
application-{profile}.ymlfiles overspring.profileskey in YAML (deprecated)
Spring Boot loads config from multiple sources in a specific priority order. Higher in the list overrides lower.
- Command-line arguments (
--server.port=9090) — highest priority SPRING_APPLICATION_JSONenv variable (inline JSON)- OS environment variables (
SERVER_PORT=9090) - Java System Properties (
-Dserver.port=9090) - Profile-specific
application-{profile}.ymloutside JAR application.ymloutside JAR (in./config/or./)- Profile-specific
application-{profile}.ymlinside JAR application.ymlinside JAR (classpath) — lowest priority
| Feature | @Value | @ConfigurationProperties |
|---|---|---|
| Binding | One field at a time | Entire prefix group at once |
| Validation | No built-in | Works with @Validated, @NotNull, etc. |
| Type Safety | String parsing, error-prone | Strongly typed with POJO |
| IDE Support | Limited | Full autocomplete with spring-configuration-processor |
| SpEL | Yes | No (not needed) |
| Refactoring | String keys break silently | IDE refactoring safe |
@SpringBootApplication includes @EnableAutoConfiguration@EnableAutoConfiguration imports AutoConfigurationImportSelectorMETA-INF/spring.factories under EnableAutoConfiguration key. Loads ~130+ auto-config candidates.META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (faster, AOT-friendly)@Conditional annotations that filter which ones actually apply@Bean methods create beans — but only if conditions pass.--debug flag or set logging.level.org.springframework.boot.autoconfigure=DEBUG to see the "AUTO-CONFIGURATION REPORT" showing which auto-configs matched and which were excluded and why.
| Annotation | Condition |
|---|---|
@ConditionalOnClass | Class exists on classpath |
@ConditionalOnMissingClass | Class does NOT exist on classpath |
@ConditionalOnBean | Specific bean exists in context |
@ConditionalOnMissingBean | No bean of that type in context (most used) |
@ConditionalOnProperty | Config property exists with value |
@ConditionalOnWebApplication | Running as a web app |
@ConditionalOnCloudPlatform | Running on Kubernetes, Cloud Foundry, etc. |
@ConditionalOnExpression | SpEL expression evaluates true |
| Scope | Instances | Real World Analogy | Use Case |
|---|---|---|---|
singleton | 1 per IoC container (default) | Company CEO — one person handles all requests | Stateless services, repositories, configuration |
prototype | New instance per request | Coffee cup — every customer gets a fresh one | Stateful beans, command objects |
request | New instance per HTTP request | HTTP ticket — new ticket per request, gone after response | Request-scoped data, correlationId holder |
session | New instance per HTTP session | Shopping cart — one per user session | User-session data in web apps |
application | 1 per ServletContext | Office lobby — shared among all users of the app | App-wide counters, shared state |
websocket | 1 per WebSocket session | Phone call — lasts duration of the call only | WebSocket stateful data |
getBean() each time, or use @Lookup method injection.
A circular dependency occurs when Bean A depends on Bean B, and Bean B depends on Bean A. Spring Boot 2.6+ throws BeanCurrentlyInCreationException by default (circular dependency detection is on).
| Feature | Constructor | Setter | Field |
|---|---|---|---|
| Immutability | ✓ final allowed | ✗ not final | ✗ not final |
| Testing (no Spring) | ✓ plain new() | ✓ setters | ✗ needs reflection |
| Circular detection | ✓ fails fast | ✗ silent | ✗ silent |
| Optional deps | ✗ awkward | ✓ natural fit | Partial |
| Spring recommendation | ✓ Preferred | Sometimes | ✗ Avoid |
@SmsChannel) for self-documenting code.
- Filters run before DispatcherServlet — good for authentication, CORS headers, request logging
- Interceptors run inside DispatcherServlet — access to handler info, pre/post/afterCompletion hooks
- @ExceptionHandler / @ControllerAdvice catch exceptions after controller execution
- MessageConverters handle content negotiation — Jackson for JSON, JAXB for XML
spring.jpa.show-sql=true and logging.level.org.hibernate.SQL=DEBUG. In production, use Hibernate statistics or a tool like Datasource Proxy to detect N+1 automatically.
| Interface Projection | DTO Projection | |
|---|---|---|
| Performance | Good (select needed cols) | Best (direct constructor) |
| SpEL expressions | ✓ Supported | ✗ Not supported |
| Derived query support | ✓ Works automatically | Needs @Query |
| Type safety | Proxy, some limitations | ✓ Strong, record-based |
| Best for | Simple column subsets | Complex computed results |
| Propagation | Behavior | Real Scenario |
|---|---|---|
REQUIRED (default) | Join existing tx. Create new if none exists. | Most service methods — part of the main business transaction |
REQUIRES_NEW | Always create new tx. Suspend existing. | Audit logging — must commit even if main tx rolls back |
SUPPORTS | Join existing if present. No tx if none. | Read-only methods that work with or without a transaction |
NOT_SUPPORTED | Suspend existing tx. Run without tx. | Calling a legacy system that can't participate in transactions |
MANDATORY | Must have existing tx. Throw if none. | Methods that should never be called outside a transaction |
NEVER | Throw if existing tx is present. | Methods that must never run in a transactional context |
NESTED | Savepoint within existing tx. Partial rollback. | Processing items in a batch — rollback one item, continue others |
| Isolation Level | Dirty Read | Non-Repeatable Read | Phantom Read | Performance |
|---|---|---|---|---|
READ_UNCOMMITTED | ✗ Allowed | ✗ Allowed | ✗ Allowed | Fastest |
READ_COMMITTED (PG default) | ✓ Prevented | ✗ Allowed | ✗ Allowed | Fast |
REPEATABLE_READ (MySQL default) | ✓ Prevented | ✓ Prevented | ✗ Allowed | Medium |
SERIALIZABLE | ✓ Prevented | ✓ Prevented | ✓ Prevented | Slowest |
@Version) rather than high isolation levels — it scales much better.
| Annotation | Loads | Speed | Use When |
|---|---|---|---|
@SpringBootTest | Full application context | Slowest (~3-10s) | Integration tests — test full flow end-to-end |
@WebMvcTest | Only web layer (controllers, filters, advice) | Fast (~1s) | Unit testing controllers in isolation |
@DataJpaTest | Only JPA repositories + in-memory DB | Medium | Test repository queries — custom JPQL, derived queries |
@JsonTest | Jackson + JSON serialization only | Fastest | Test JSON serialization/deserialization |
@RestClientTest | RestTemplate + MockServer | Fast | Test REST clients in isolation |
spring.jpa.show-sql=true. Use Datasource Proxy or Hibernate Statistics. Find N+1 queries, missing indexes, full table scans.hikaricp.connections.pending. Pool exhaustion looks like random slowness.Core / Bean Configuration
Web / REST
/users/{id} → @PathVariable Long id?page=1 → @RequestParam(defaultValue="1") int pageData / JPA
nativeQuery=true for raw SQL.Config / Conditional / Security
Critical Rules — Self-invocation Trap applies to ALL of these
this.method() internally bypasses the proxy. The annotation has NO effect:@Transactional @Cacheable @Async @Retryable @RateLimiter @CircuitBreakerAlways invoke these methods from outside the class (via another bean) to ensure the proxy intercepts the call.