Spring Boot + Spring Core + WebFlux Guide
Target audience: 3–10 years experienced Java/Spring developers preparing for interviews and designing
production services.
This guide focuses on Spring Core (IoC/DI, scopes, lifecycle, AOP), Spring Boot auto-configuration and
operations, and the Spring MVC vs WebFlux programming models with practical examples.
1. Spring Core
Spring Core provides the foundational Inversion of Control (IoC) container and Dependency Injection (DI)
mechanism on which the rest of the Spring ecosystem is built.
1.1 IoC and Dependency Injection
Inversion of Control means objects do not create their dependencies directly; instead, the container creates
and wires them. Dependency Injection is the pattern by which these dependencies are provided to objects,
typically via constructors, setters, or field injection.
• Constructor injection: preferred for mandatory dependencies, encourages immutability and testability.
• Setter injection: suitable for optional dependencies or circular references (though the latter should be
avoided).
• Field injection: concise but less testable and harder to reason about; generally discouraged in favor of
constructor injection.
@Service
public class OrderService {
private final PaymentClient paymentClient;
public OrderService(PaymentClient paymentClient) {
[Link] = paymentClient;
}
public void placeOrder(Order order) {
[Link](order);
}
}
1.2 Bean scopes
Spring beans have scopes that define their lifecycle within the container. In a typical Spring Boot backend
application, the most common scopes are singleton and prototype, with request, session, and application
scopes used in web contexts.
• singleton: a single bean instance per Spring container; the default scope.
• prototype: a new bean instance is created every time it is requested from the container.
• request: one instance per HTTP request (web-aware contexts).
• session: one instance per HTTP session.
• application: one instance per ServletContext.
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class PrototypeTask {
// a new instance per lookup
}
1.3 Bean lifecycle
The Spring container manages bean lifecycle from instantiation through dependency injection, initialization,
and destruction. You can hook into lifecycle phases using annotations or interfaces.
• Instantiation and dependency injection.
• BeanNameAware, BeanFactoryAware, ApplicationContextAware callbacks (optional).
• @PostConstruct or [Link]() for initialization logic.
• @PreDestroy or [Link]() for cleanup when the context shuts down.
@Component
public class CacheManager {
@PostConstruct
public void init() {
// load cache
}
@PreDestroy
public void shutdown() {
// flush cache
}
}
1.4 Profiles
Spring profiles allow you to group beans and configuration for different environments (dev, test, prod) and
activate them selectively via configuration or command-line flags.
@Profile("dev")
@Configuration
public class DevConfig {
@Bean
public DataSource h2DataSource() {
// in-memory DB for local development
}
}
@Profile("prod")
@Configuration
public class ProdConfig {
@Bean
public DataSource mysqlDataSource() {
// production DB
}
}
Activate profiles using [Link] property, environment variables, or command-line arguments
(e.g., --[Link]=dev).
1.5 AOP basics
Aspect-Oriented Programming (AOP) in Spring allows you to implement cross-cutting concerns, such as
logging, transaction management, and security, separately from business logic.
• Aspect: a class that encapsulates cross-cutting logic.
• Join point: a point during execution, such as method invocation.
• Pointcut: a predicate that matches join points.
• Advice: the action taken at a matched join point (before, after, around, etc.).
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* [Link].*.*(..))")
public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
long start = [Link]();
try {
return [Link]();
} finally {
long duration = [Link]() - start;
[Link]([Link]() + " took " + duration + " ms");
}
}
}
2. Spring Boot
Spring Boot builds on Spring Core to provide opinionated auto-configuration, starter dependencies, and
production-ready features that dramatically reduce boilerplate in typical applications.
2.1 Auto-configuration
Auto-configuration attempts to automatically configure Spring beans based on the classpath contents,
existing beans, and configuration properties. It follows the principle of sensible defaults while still allowing
overrides.
• Triggered by @SpringBootApplication, which includes @EnableAutoConfiguration.
• Uses conditional annotations like @ConditionalOnClass, @ConditionalOnMissingBean.
• Configuration can be fine-tuned via properties or by defining your own beans.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
[Link]([Link], args);
}
}
2.2 Starters
Spring Boot starters are curated dependency descriptors that bring in a set of transitive dependencies for
specific functionality, such as web, data access, or security.
• spring-boot-starter-web: Spring MVC, embedded Tomcat, JSON support.
• spring-boot-starter-data-jpa: Spring Data JPA, Hibernate, JPA APIs.
• spring-boot-starter-security: Spring Security for authentication and authorization.
• spring-boot-starter-webflux: Reactive WebFlux stack with Netty/Tomcat.
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.3 Configuration properties and YAML
Spring Boot externalizes configuration via [Link] or [Link] files, environment
variables, and command-line arguments. Properties can be strongly typed using @ConfigurationProperties.
# [Link]
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/app_db
username: app_user
password: secret
@ConfigurationProperties(prefix = "app")
@Component
public class AppProperties {
private String name;
private int threadPoolSize;
// getters and setters
}
Using @ConfigurationProperties encourages hierarchical, type-safe configuration objects that are easy to
validate and test.
2.4 Profiles in Spring Boot
Spring Boot integrates profiles with profile-specific configuration files such as [Link],
[Link], and the [Link] property.
# [Link]
spring:
datasource:
url: jdbc:h2:mem:devdb
# [Link]
spring:
datasource:
url: jdbc:postgresql://prod-db:5432/app_db
You can activate profiles using environment variables (SPRING_PROFILES_ACTIVE), system properties, or
command-line arguments.
2.5 Actuator
Spring Boot Actuator exposes production-ready endpoints for monitoring and managing your application,
such as health, metrics, environment, and thread dumps.
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# [Link]
management:
endpoints:
web:
exposure:
include: health,info,metrics,env
In interviews, highlight how you secure Actuator endpoints, integrate with Prometheus/Micrometer, and use
health checks in Kubernetes or cloud deployments.
2.6 Logging and externalized configuration
Spring Boot uses Logback by default and provides sensible logging configuration out of the box, while still
allowing you to customize patterns, levels, and appenders.
# [Link]
logging:
level:
root: INFO
[Link]: DEBUG
file:
name: [Link]
Configuration can be externalized using environment variables, command-line arguments, config servers, or
mounted configuration files in containerized environments, following the Twelve-Factor App principles.
3. Real-world Spring Boot Scenarios
3.1 Multiple data sources
A common real-world requirement is connecting to multiple databases, for example a transactional OLTP
database and a reporting or audit database.
@Bean
@Primary
@ConfigurationProperties("[Link]")
public DataSourceProperties primaryDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
public DataSource primaryDataSource() {
return primaryDataSourceProperties().initializeDataSourceBuilder().build();
}
@Bean
@ConfigurationProperties("[Link]")
public DataSourceProperties auditDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
public DataSource auditDataSource() {
return auditDataSourceProperties().initializeDataSourceBuilder().build();
}
Each data source can then be associated with its own EntityManagerFactory and transaction manager if
using JPA, or separate JdbcTemplate beans for fine-grained control.
3.2 Configuration per environment
Using Spring profiles and environment-specific property files, you can maintain different configuration for
local, staging, and production environments without changing code.
• [Link] for developer machines.
• [Link] for pre-production testing.
• [Link] for production deployments.
• Override sensitive values (passwords, API keys) via environment variables or secret management
systems.
3.3 Troubleshooting auto-configuration
When auto-configuration does not behave as expected, Spring Boot provides tools to understand what
configurations were applied and why.
• Use the /actuator/conditions (formerly /autoconfig) endpoint to inspect auto-configuration report.
• Enable debug logging (debug=true or --debug) to see conditional evaluation outcomes.
• Check for missing classes, conflicting beans, or properties that are misconfigured.
In interviews, be ready to describe how you debug why a particular starter did not create a bean, or how you
override auto-configured beans with your own.
4. Spring MVC vs WebFlux
Spring MVC and Spring WebFlux are two web stacks built on Spring. MVC is the traditional, blocking,
servlet-based stack, while WebFlux is the reactive, non-blocking stack built on Project Reactor.
4.1 DispatcherServlet vs reactive stack
Spring MVC is built around the DispatcherServlet, which receives HTTP requests, dispatches them to
controllers, and relies on a thread-per-request model over the Servlet API.
Spring WebFlux uses a reactive, non-blocking runtime (typically Netty) and APIs based on Reactor types
Mono and Flux, enabling highly scalable I/O-bound services with fewer threads.
4.2 Mono and Flux basics
Mono represents a reactive publisher that emits zero or one value and then completes, while Flux represents
zero to many values. Both support operators for transformation, filtering, error handling, and composition.
public Mono<User> findUser(String id) {
return [Link](id);
}
public Flux<User> streamUsers() {
return [Link]();
}
In a WebFlux controller, you typically return Mono or Flux directly from handler methods, allowing the
framework to orchestrate backpressure and non-blocking I/O.
4.3 Conceptual backpressure
Backpressure is the ability of a consumer to signal to a producer how much data it can handle, preventing
overwhelming slow consumers or downstream resources.
In Project Reactor and WebFlux, backpressure is part of the Reactive Streams specification: subscribers
request a certain number of elements, and publishers respect this demand, enabling controlled, resilient data
flows.
4.4 Blocking vs reactive controller examples
A typical Spring MVC controller uses blocking types and executes on a servlet container thread per request.
@RestController
@RequestMapping("/mvc")
public class MvcController {
private final UserService userService;
public MvcController(UserService userService) {
[Link] = userService;
}
@GetMapping("/users/{id}")
public User getUser(@PathVariable String id) {
// blocking call
return [Link](id);
}
}
In WebFlux, the controller returns reactive types and must avoid blocking calls on the event-loop threads.
@RestController
@RequestMapping("/reactive")
public class ReactiveController {
private final ReactiveUserService userService;
public ReactiveController(ReactiveUserService userService) {
[Link] = userService;
}
@GetMapping("/users/{id}")
public Mono<User> getUser(@PathVariable String id) {
return [Link](id);
}
@GetMapping("/users")
public Flux<User> getUsers() {
return [Link]();
}
}
When integrating blocking APIs (JDBC, legacy clients) into a reactive stack, offload blocking work to a
dedicated scheduler (e.g., boundedElastic) instead of the event-loop threads.
5. Choosing Spring MVC or WebFlux
Use Spring MVC for traditional servlet-based applications, where you rely heavily on blocking I/O libraries and
do not need the scalability benefits of reactive programming.
Choose WebFlux when building high-throughput, I/O-bound services that can use reactive drivers (R2DBC,
reactive MongoDB, WebClient) and when you are ready to adopt the reactive programming model
end-to-end.