A structured, phase-by-phase guide from beginner to production-grade microservices engineer.
- Phase 1 – Spring Boot Fundamentals
- Phase 2 – Intermediate (Professional API Developer)
- Phase 3 – Advanced Spring Boot (Microservices Entry)
- Phase 4 – Microservices Communication & Resilience
- Phase 5 – Deployment & DevOps
- Phase 6 – What's New & Often Missed (2026 Additions)
- 🏆 Final Project
- 📅 Suggested Timeline
- 🔥 Top Interview Questions
🎯 Goal: Understand how Spring Boot works and build CRUD REST APIs with SQL + NoSQL.
Learn:
- What is Spring vs Spring Boot
- Auto-configuration internals
- Starter dependencies (
spring-boot-starter-*) - Embedded servers: Tomcat, Jetty, Undertow
- Convention over configuration
Build:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, Spring Boot 2026!";
}
}Learn:
- Spring Initializr (
start.spring.io) - Project structure (
src/main/java,resources) - Maven vs Gradle
application.propertiesvsapplication.yml
Practice – Create project with:
- Spring Web
- Spring Data JPA
- H2 (in-memory DB)
Learn:
| Annotation | Purpose |
|---|---|
@RestController |
Combines @Controller + @ResponseBody |
@RequestMapping |
Maps URL prefix |
@GetMapping |
Read |
@PostMapping |
Create |
@PutMapping |
Update |
@DeleteMapping |
Delete |
@PathVariable |
URL segment extraction |
@RequestParam |
Query param extraction |
@RequestBody |
Deserialize JSON body |
Build CRUD API for: User · Product · Order
SQL (Mandatory):
- H2 (in-memory for dev/testing)
- MySQL / PostgreSQL (production)
Entity Mapping:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders;
}Relationships: @OneToMany · @ManyToOne · @ManyToMany · @OneToOne
Repositories:
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);
}Build: E-commerce style CRUD app
Choose one: MongoDB · Couchbase · Redis (as document store)
@Document(collection = "products")
public class Product {
@Id
private String id;
private String name;
private double price;
}
public interface ProductRepository extends MongoRepository<Product, String> {}# application-dev.yml
spring:
datasource:
url: jdbc:h2:mem:devdb
# application-prod.yml
spring:
datasource:
url: jdbc:postgresql://localhost/proddb@Profile("dev")
@Bean
public DataLoader devDataLoader() { ... }Activate via: --spring.profiles.active=dev
- Hot reload on classpath change
- LiveReload browser plugin support
- Automatic restart behavior
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>@EnableScheduling
@SpringBootApplication
public class App { ... }
@Scheduled(cron = "0 0 * * * *") // Every hour
public void runTask() { ... }Spring Batch basics: Job → Step → ItemReader → ItemProcessor → ItemWriter
✅ After Phase 1 you can:
- Build production-level CRUD APIs
- Connect SQL & NoSQL databases
- Use profiles for environments
- Handle basic scheduling
🎯 Goal: Build secure, tested, and versioned APIs.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex, HttpServletRequest request) {
ErrorResponse error = new ErrorResponse(
LocalDateTime.now(), 404, "Not Found", ex.getMessage(), request.getRequestURI()
);
return ResponseEntity.status(404).body(error);
}
}Standard Error Response:
{
"timestamp": "2026-01-01T10:00:00",
"status": 400,
"error": "Bad Request",
"message": "Validation failed",
"path": "/api/users"
}Basics:
- Authentication vs Authorization
- Basic Auth
- Role-based access (
ROLE_USER,ROLE_ADMIN)
Advanced:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.sessionManagement(s -> s.sessionCreationPolicy(STATELESS));
return http.build();
}
}JWT Flow: Login → Generate Token → Attach to Header → Validate on Each Request
Password encoding:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}- HATEOAS: Add links to responses for discoverability
- API Versioning strategies:
| Strategy | Example |
|---|---|
| URI | /api/v1/users |
| Header | X-API-Version: 1 |
| Parameter | /api/users?version=1 |
| Media type | Accept: application/vnd.app.v1+json |
- Pagination & Sorting: Use
PageablewithJpaRepository - DTOs: Never expose entities directly — use mapper layer (MapStruct recommended)
Unit Testing:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void shouldReturnUserById() {
when(userRepository.findById(1L)).thenReturn(Optional.of(new User()));
assertNotNull(userService.getUser(1L));
}
}Integration Testing:
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void shouldReturn200ForGetUsers() throws Exception {
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk());
}
}Test: Controllers · Services · Repositories · Security filters
management:
endpoints:
web:
exposure:
include: health,metrics,info,prometheus
endpoint:
health:
show-details: alwaysKey endpoints: /actuator/health · /actuator/metrics · /actuator/prometheus
Integrate with Prometheus → visualize in Grafana
✅ After Phase 2 you can:
- Build secure JWT-protected APIs
- Write production-level tests
- Monitor applications with Prometheus & Grafana
🎯 Goal: Understand distributed systems basics.
- Environment-specific beans with
@Profile - Profile-based config file loading
@ConditionalOnPropertyfor feature toggles
| Component | Purpose |
|---|---|
| Config Server | Centralized configuration |
| Eureka | Service discovery |
| Gateway | API routing |
| Cloud Bus | Config broadcasting |
| LoadBalancer | Client-side load balancing |
// Eureka Server
@EnableEurekaServer
@SpringBootApplication
public class EurekaServer { ... }
// Client Service
@EnableDiscoveryClient
@SpringBootApplication
public class UserService { ... }# Client application.yml
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20Features: Route predicates · Custom filters · Rate limiting · JWT validation at gateway
# Config Server application.yml
spring:
cloud:
config:
server:
git:
uri: https://github.com/your-org/config-repoRefresh config dynamically: POST /actuator/refresh or via Spring Cloud Bus
Build – Microservices Project:
├── eureka-server (port 8761)
├── config-server (port 8888)
├── api-gateway (port 8080)
├── user-service (port 8081)
├── order-service (port 8082)
└── product-service (port 8083)
✅ After Phase 3: You understand microservices architecture basics.
🎯 Goal: Build production-grade distributed systems.
Synchronous:
// OpenFeign (preferred)
@FeignClient(name = "inventory-service")
public interface InventoryClient {
@GetMapping("/api/inventory/{productId}")
InventoryResponse checkStock(@PathVariable String productId);
}Asynchronous: Kafka / RabbitMQ (see Event-Driven section)
@CircuitBreaker(name = "inventoryService", fallbackMethod = "fallbackStock")
@Retry(name = "inventoryService")
@TimeLimiter(name = "inventoryService")
public CompletableFuture<InventoryResponse> checkInventory(String productId) {
return CompletableFuture.supplyAsync(() -> inventoryClient.checkStock(productId));
}
public CompletableFuture<InventoryResponse> fallbackStock(String productId, Throwable t) {
return CompletableFuture.completedFuture(new InventoryResponse(false));
}Patterns: Circuit Breaker · Retry · Rate Limiter · Bulkhead · Time Limiter
2026 Recommended Stack: Micrometer Tracing + Zipkin (Spring Cloud Sleuth is deprecated)
management:
tracing:
sampling:
probability: 1.0
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spansEvery request gets a traceId and spanId propagated across services automatically.
- Broadcasts config refresh events across all instances
- Backed by RabbitMQ or Kafka
# Trigger refresh across all services
POST /actuator/busrefresh// Producer (Order Service)
@Service
public class OrderService {
@Autowired
private KafkaTemplate<String, OrderEvent> kafkaTemplate;
public void placeOrder(Order order) {
kafkaTemplate.send("order-events", new OrderEvent(order.getId(), "PLACED"));
}
}
// Consumer (Inventory Service)
@KafkaListener(topics = "order-events", groupId = "inventory-group")
public void handleOrderEvent(OrderEvent event) {
inventoryService.reserveStock(event.getOrderId());
}Supported brokers: Apache Kafka · RabbitMQ
✅ After Phase 4: You can build scalable distributed systems.
🎯 Goal: Ship to production like a real company.
Dockerfile (multi-stage):
# Build stage
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# Runtime stage
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]Docker Compose:
services:
user-service:
build: ./user-service
ports: ["8081:8080"]
environment:
- SPRING_PROFILES_ACTIVE=docker
depends_on: [postgres, kafka]
postgres:
image: postgres:16
environment:
POSTGRES_DB: userdb
POSTGRES_PASSWORD: secret
kafka:
image: confluentinc/cp-kafka:latestGitHub Actions workflow:
name: Build & Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { java-version: '21', distribution: 'temurin' }
- run: mvn test
- run: mvn package -DskipTests
- run: docker build -t myapp:${{ github.sha }} .
- run: docker push myapp:${{ github.sha }}Tools: GitHub Actions · Jenkins · GitLab CI · CircleCI
# deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
spec:
containers:
- name: user-service
image: user-service:latest
ports: [{ containerPort: 8080 }]
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
readinessProbe:
httpGet:
path: /actuator/health
port: 8080Key resources: Pods · Deployments · Services · ConfigMaps · Secrets · Ingress · HPA
ELK Stack: Logstash → Elasticsearch → Kibana
Structured logging:
logging:
pattern:
console: '{"time":"%d","level":"%p","traceId":"%X{traceId}","msg":"%m"}%n'Recommended: Loki + Grafana (lighter alternative to ELK in 2026)
@EnableCaching
@SpringBootApplication
public class App { ... }
@Cacheable(value = "users", key = "#id")
public User getUser(Long id) { ... }
@CacheEvict(value = "users", key = "#user.id")
public User updateUser(User user) { ... }Redis config:
spring:
cache:
type: redis
data:
redis:
host: localhost
port: 6379Also learn: HikariCP connection pool tuning · @Async for non-blocking methods · Lazy loading vs Eager loading
- HTTPS with TLS certificates (Let's Encrypt / cert-manager in K8s)
- CSRF protection (for browser clients)
- Rate limiting at Gateway level
- Secure HTTP headers (
X-Content-Type-Options,X-Frame-Options) - OAuth2 + Keycloak / Auth0 for identity management
- Secrets management: HashiCorp Vault or K8s Secrets
- OWASP Top 10 awareness
✅ After Phase 5: You can deploy and operate production systems.
These topics were missing from the original roadmap but are critical in 2026.
Spring Boot 3.2+ supports virtual threads out of the box, replacing traditional thread-per-request with lightweight virtual threads — massively improving throughput.
spring:
threads:
virtual:
enabled: trueCompile Spring Boot apps to native executables for near-instant startup and low memory use — critical for serverless/Lambda deployments.
mvn -Pnative native:compileAdd spring-boot-starter-aot and configure @NativeHints for reflection-heavy code.
For high-concurrency, non-blocking APIs:
@GetMapping("/users/{id}")
public Mono<User> getUser(@PathVariable Long id) {
return userRepository.findById(id);
}
@GetMapping("/users")
public Flux<User> getAllUsers() {
return userRepository.findAll();
}When to use: Real-time apps, streaming data, high I/O scenarios.
@Controller
public class UserGraphQLController {
@QueryMapping
public User userById(@Argument Long id) {
return userService.getUser(id);
}
@MutationMapping
public User createUser(@Argument UserInput input) {
return userService.create(input);
}
}Add spring-boot-starter-graphql — great for flexible client queries, BFF pattern.
Spring Boot 3.x ships with Micrometer Observation API. Prefer OpenTelemetry (OTel) over Sleuth for vendor-neutral tracing.
management:
otlp:
tracing:
endpoint: http://otel-collector:4318/v1/tracesNever run raw DDL manually. Manage schema changes as versioned scripts:
-- db/migration/V1__create_users_table.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);Replace Springfox (deprecated) with SpringDoc:
@Operation(summary = "Get user by ID")
@ApiResponse(responseCode = "200", description = "User found")
@GetMapping("/users/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) { ... }Auto-generates Swagger UI at /swagger-ui.html.
public record CreateUserRequest(
@NotBlank @Email String email,
@NotBlank @Size(min = 8) String password,
@NotNull @Min(18) Integer age
) {}
@PostMapping("/users")
public ResponseEntity<UserDTO> createUser(@Valid @RequestBody CreateUserRequest request) { ... }@Mapper(componentModel = "spring")
public interface UserMapper {
UserDTO toDTO(User user);
User toEntity(CreateUserRequest request);
}Avoids manual mapping errors; generates type-safe code at compile time.
For high-performance inter-service communication (alternative to REST):
@GrpcService
public class UserGrpcService extends UserServiceGrpc.UserServiceImplBase {
@Override
public void getUser(UserRequest request, StreamObserver<UserResponse> responseObserver) {
// ...
}
}Build: "Production-Ready E-Commerce Microservices System"
📦 e-commerce-platform/
├── 🔐 auth-service (JWT + OAuth2 + Keycloak)
├── 👤 user-service (CRUD + PostgreSQL + Flyway)
├── 📦 product-service (CRUD + MongoDB)
├── 🛒 order-service (Kafka Producer + Postgres)
├── 📊 inventory-service (Kafka Consumer + Redis cache)
├── 🔔 notification-service (Email/SMS on order events)
├── 🌐 api-gateway (Spring Cloud Gateway + rate limiting)
├── 🗂️ config-server (Git-backed configs)
├── 🔍 eureka-server (Service discovery)
├── 📈 monitoring/ (Prometheus + Grafana + Loki)
└── 🐳 docker-compose.yml
Tech checklist:
- [ ] JWT authentication & refresh tokens
- [ ] API Gateway with rate limiting
- [ ] Eureka service discovery
- [ ] Config Server with Git backend
- [ ] Circuit breaker (Resilience4j) on all Feign calls
- [ ] Kafka event streaming (order → inventory → notification)
- [ ] Dockerized with multi-stage builds
- [ ] Kubernetes manifests (Deployments, Services, Ingress, HPA)
- [ ] Prometheus + Grafana dashboards
- [ ] ELK or Loki centralized logging
- [ ] OpenAPI/Swagger documentation
- [ ] Database migrations with Flyway
- [ ] Distributed tracing with Zipkin/Jaeger
| Phase | Duration | Focus |
|---|---|---|
| Phase 1 – Fundamentals | 3–4 weeks | Core Spring Boot, CRUD, JPA |
| Phase 2 – Intermediate | 3 weeks | Security, Testing, Monitoring |
| Phase 3 – Microservices Entry | 2 weeks | Spring Cloud basics |
| Phase 4 – Resilience | 3 weeks | Kafka, Resilience4j, Tracing |
| Phase 5 – DevOps | 2–3 weeks | Docker, K8s, CI/CD |
| Phase 6 – 2026 Topics | Ongoing | Loom, Native, Reactive |
Total: ~3–4 months of consistent learning (2–3 hours/day)
💡 Maximum ROI Focus: Security · Testing · Docker · Kubernetes · Distributed systems fundamentals — these separate average devs from high-paying backend engineers.
-
What is the difference between
@Component,@Service,@Repository, and@Controller?They're all
@Componentstereotypes.@Repositoryadds exception translation;@Servicemarks business logic;@Controllermarks web layer. Use the most specific one for clarity and AOP. -
How does Spring Boot auto-configuration work?
@EnableAutoConfigurationreadsMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, conditionally loading beans based on@ConditionalOnClass,@ConditionalOnMissingBean, etc. -
What is the difference between
@SpringBootApplicationand@EnableAutoConfiguration?@SpringBootApplication=@Configuration+@EnableAutoConfiguration+@ComponentScan. It's a convenience shortcut. -
Explain
application.propertiesvsapplication.yml. Which is preferred?Both configure the app; YAML supports hierarchical structure and multiple documents. YAML is preferred for complex configs; properties for simple key-value.
-
What is a Spring Bean scope? Name all scopes.
Singleton (default), Prototype, Request, Session, Application, WebSocket.
-
What is the difference between
@Autowired,@Inject, and@Resource?@Autowiredis Spring-specific (type-then-name).@Injectis JSR-330 (type-only).@Resourceis JSR-250 (name-then-type). Constructor injection is preferred over field injection.
-
What is the N+1 query problem and how do you solve it?
When fetching a list triggers N additional queries for associations. Solve with
JOIN FETCHin JPQL,@EntityGraph, or@BatchSize. -
What is the difference between
EAGERandLAZYloading?EAGER loads associations immediately; LAZY loads on access. LAZY is default for collections; EAGER can cause performance issues.
-
Explain
@Transactional. What happens without it?Without it, each repository call is its own transaction.
@Transactionalwraps a method in a single transaction, rolling back onRuntimeExceptionby default. -
What is the difference between
save()andsaveAndFlush()in JPA?save()stages the operation;saveAndFlush()immediately synchronizes with the database within the current transaction.
-
How does JWT authentication work in Spring Boot?
Client sends credentials → server validates and returns JWT → client sends JWT in
Authorization: Bearerheader → server validates token on each request via aOncePerRequestFilter. -
What is the difference between authentication and authorization?
Authentication = who are you? Authorization = what can you do?
-
How do you prevent SQL injection in Spring Boot?
Use parameterized queries (JPA/JPQL does this by default). Avoid string concatenation in native queries.
-
What is CSRF and when should you disable it?
Cross-Site Request Forgery. Safe to disable for stateless REST APIs using JWT (no cookies). Keep it enabled for server-rendered apps with session cookies.
-
What is the difference between Eureka and a load balancer?
Eureka = service registry (where are services?). Load balancer = how to distribute traffic. Spring Cloud LoadBalancer uses Eureka to find instances.
-
What is a circuit breaker? When does it open?
Prevents cascading failures. Opens when failure rate exceeds threshold (e.g., 50% in 10 calls), redirects to fallback for a wait duration, then tries half-open state.
-
Explain the difference between synchronous and asynchronous communication in microservices.
Synchronous (REST/gRPC): caller waits for response, tight coupling. Asynchronous (Kafka/RabbitMQ): fire-and-forget, loose coupling, better resilience.
-
What is Saga pattern?
Manages distributed transactions across microservices without 2PC. Choreography (events) or Orchestration (central coordinator) patterns.
-
How do you handle distributed transactions in microservices?
Use the Saga pattern with compensating transactions. Avoid distributed ACID transactions; prefer eventual consistency.
-
What is an API Gateway and why do you need it?
Single entry point for all clients. Handles cross-cutting concerns: auth, rate limiting, routing, SSL termination, logging — keeps individual services clean.
-
What is the difference between
@Mockand@MockBean?@Mock(Mockito): creates mock without Spring context.@MockBean: creates mock and registers it in Spring application context — use for integration tests. -
What does
@SpringBootTestload?The full application context. Use
@WebMvcTestfor controller-only tests or@DataJpaTestfor repository-only tests — they're faster. -
How do you test a Kafka consumer in Spring Boot?
Use
@EmbeddedKafkawith@SpringBootTestto spin up an in-memory Kafka broker for integration tests.
-
How do you optimize Spring Boot startup time?
Lazy initialization (
spring.main.lazy-initialization=true), GraalVM native image, reduce component scan scope, use@Importover scanning. -
What is HikariCP and why is it the default connection pool?
High-performance JDBC connection pool. Fastest available due to minimal bytecode and lock-free design. Spring Boot auto-configures it.
-
How do you monitor a Spring Boot application in production?
Spring Actuator exposes metrics → Prometheus scrapes them → Grafana visualizes → alerts on anomalies. Add distributed tracing via Micrometer + Zipkin/Jaeger.
-
What is the difference between horizontal and vertical scaling? Which does Spring Boot favor?
Vertical = bigger server. Horizontal = more instances. Spring Boot (especially with microservices) favors horizontal scaling — stateless design + Kubernetes HPA.
-
How do you implement zero-downtime deployments?
Rolling updates in Kubernetes, readiness probes to gate traffic, graceful shutdown with
server.shutdown=graceful, database migration compatibility (expand-contract pattern).
Happy learning! Remember: build projects, not just tutorials. The best portfolio is working code. 🎯