Advanced Java & Spring Boot
Production-focused patterns for modern Java development ā concurrency, reactive programming, testing, and cloud integration.
Concurrency is a cornerstone of high-performance Java applications. Beyond basic Thread and Runnable, modern Java provides powerful abstractions for parallelism and async execution.
Key Abstractions
- ExecutorService: Thread pool management ā fixed pools for predictable loads, cached pools for bursty traffic.
- ForkJoinPool: Divide-and-conquer parallel processing ā breaking large tasks into subtasks that execute in parallel.
- CompletableFuture: Non-blocking async operations with chaining ā
thenApply(),thenCombine(),exceptionally(). - Virtual Threads (Java 21+): Lightweight threads for high-concurrency I/O workloads ā millions of threads without OS overhead.
Best Practices
- Use appropriate pool sizes: CPU-bound tasks ā cores count; I/O-bound ā larger pools.
- Avoid shared mutable state. Prefer immutable objects,
AtomicReference, orConcurrentHashMap. - Use
CompletableFuturefor I/O parallelization ā e.g., fetch user and order data concurrently in a REST endpoint. - Monitor thread pools with JMX or VisualVM to identify contention and bottlenecks.
Example ā Async Data Fetching
@GetMapping("/fetch-data")
public CompletableFuture<String> fetchData() {
CompletableFuture<String> userData = CompletableFuture.supplyAsync(
() -> externalService.getUserData(), executor);
CompletableFuture<String> orderData = CompletableFuture.supplyAsync(
() -> externalService.getOrderData(), executor);
return userData.thenCombine(orderData,
(user, order) -> "User: " + user + ", Order: " + order)
.exceptionally(ex -> "Error: " + ex.getMessage());
} Custom starters encapsulate reusable logic (auth, logging, metrics) into modular, shareable components across multiple projects ā eliminating boilerplate configuration.
- Auto-Configuration:
@Configurationclass with@ConditionalOnClassand@ConditionalOnMissingBeanfor smart defaults. - Registration:
spring.factoriesor@AutoConfiguration(Spring Boot 3+) to register with the framework. - Properties:
@ConfigurationPropertiesfor externalised configuration. - Example: A metrics starter that auto-configures an API call counter bean ā injected into any service automatically.
Key patterns for building high-performance REST APIs in Spring Boot.
- Caching:
@Cacheablewith TTL to avoid redundant database lookups. - Pagination: Spring Data's
Pageable+Page<T>for efficient data retrieval without loading entire collections. - Async Processing:
@AsyncandCompletableFuturereturn types for non-blocking endpoints. - Compression: Enable gzip response compression for large payloads.
- DTOs: Use Data Transfer Objects to control what's serialised ā avoid exposing internal entities.
- Connection Pooling: HikariCP (Spring Boot default) with tuned pool size for your database connection profile.
Understanding JVM tuning is essential for production reliability. Key areas:
- Heap Sizing: Set
-Xmsand-Xmxto the same value to avoid resize pauses. Size based on workload profiling, not guessing. - Garbage Collection:
- G1GC (default since Java 9): Good general-purpose collector ā balances throughput and latency.
- ZGC / Shenandoah: Ultra-low pause for latency-sensitive applications (<1ms pauses).
- Metaspace: Set
-XX:MaxMetaspaceSizeto prevent unbounded classloader growth, especially in apps using reflection or dynamic proxies. - Monitoring: JMX, VisualVM, Prometheus + Micrometer for runtime metrics. GC logs (
-Xlog:gc*) for tuning decisions.
WebFlux provides a non-blocking, event-driven programming model using Project Reactor ā ideal for high-concurrency I/O-bound applications.
- Mono<T>: 0 or 1 element ā replaces
Optionalin reactive chains. - Flux<T>: 0 to N elements ā replaces
Listfor streaming data. - Backpressure: Consumer controls emission rate when producer is faster ā prevents memory overflow.
- Reactive data stores: Spring Data R2DBC (SQL), ReactiveMongoRepository (MongoDB), ReactiveCassandraRepository.
- When to use: High-concurrency I/O workloads (API gateways, streaming). For CPU-bound or simple CRUD, stick with Spring MVC.
Essential patterns for building resilient distributed systems.
Circuit Breaker
- Prevents cascading failures ā stops calling a failing service and returns a fallback response.
- States: Closed (normal) ā Open (failure threshold reached, stops calls) ā Half-Open (tests recovery).
- Implementation:
Resilience4jwith@CircuitBreaker(name = "service", fallbackMethod = "fallback").
Event-Driven
- Kafka: Producer ā Topic ā Consumer Groups for decoupled, scalable asynchronous communication.
- Saga Pattern: Coordinate distributed transactions without 2PC ā compensating actions on failure.
- Outbox Pattern: Atomic writes to database + event publishing ā prevents message loss in event-driven flows.
Securing Spring Boot applications with modern authentication and authorization patterns.
- JWT (JSON Web Tokens): Stateless authentication ā token issued on login, validated on each request via a custom filter.
- OAuth2 / OIDC: Delegate authentication to identity providers (Google, Okta, Keycloak).
spring-boot-starter-oauth2-clientor resource-server. - RBAC: Role-based access control ā
@PreAuthorize("hasRole('ADMIN')")on controller methods. - CSRF: Disable only for stateless APIs. Enable for session-based web apps.
- CORS: Configure origins, methods, and headers in
SecurityFilterChainā don't usepermitAll()as a workaround. - Password Storage:
BCryptPasswordEncoderā never store plain text or MD5 hashes.
Testcontainers runs real Docker containers (databases, message brokers) during integration tests ā eliminating mocks and in-memory fakes.
- PostgreSQL:
@Testcontainers+PostgreSQLContainerfor real JPA integration tests. - Kafka:
KafkaContainerfor producer/consumer integration tests. - Redis:
GenericContainer("redis:7")for cache layer tests. - Best Practice: Use
@DynamicPropertySourceto inject container connection properties. - Slower than unit tests but catches integration issues that mocks miss ā run in CI pipeline.
Patterns for integrating Spring Boot applications with AWS services and Kubernetes deployments.
- AWS SDK v2:
S3Client,SqsClient,DynamoDbClientā inject as Spring beans. - S3 File Upload:
PutObjectRequestwith multipart upload for large files. - SQS / SNS: Spring Cloud AWS for message-driven microservices ā
@SqsListener. - Kubernetes Deployment: Container image ā Deployment manifest ā Service ā Ingress.
- Health Probes: Spring Actuator's
/health/livenessand/health/readinessfor K8s pod lifecycle management. - Config: K8s ConfigMaps/Secrets mapped to Spring Boot environment variables or mounted as files.
Design Patterns
Reusable solutions to common software design problems. Organised into three categories with Java implementations.
Patterns that deal with object creation mechanisms ā increasing flexibility and reuse.
- Singleton: Ensures a class has only one instance. Use: Configuration managers, connection pools, loggers. Implementation: private constructor + static instance (thread-safe with
enumor double-checked locking). - Factory Method: Define an interface for creating objects, let subclasses decide which class to instantiate. Use: When creation logic varies by type (e.g.,
NotificationFactorycreates Email, SMS, or Push). - Abstract Factory: Create families of related objects without specifying concrete classes. Use: Cross-platform UI toolkits (e.g., Windows vs macOS button + checkbox families).
- Builder: Construct complex objects step by step. Use: Objects with many optional parameters (e.g.,
User.builder().name("Vinay").email("...").build()). - Prototype: Create new objects by cloning an existing instance. Use: When instantiation is expensive (e.g., deep-copying configuration templates).
Patterns that deal with object composition ā simplifying relationships between entities.
- Adapter: Convert one interface into another that a client expects. Use: Integrating legacy code or third-party libraries with incompatible interfaces.
- Bridge: Decouple abstraction from implementation so both can vary independently. Use: Separate shape drawing from rendering engine (e.g., Circle renders on OpenGL or DirectX).
- Composite: Compose objects into tree structures to represent part-whole hierarchies. Use: File systems (files and folders), UI component trees, organisation charts.
- Decorator: Dynamically add behaviour to an object without modifying its class. Use: Wrapping streams (BufferedInputStream), adding logging/caching to services.
- Facade: Provide a simplified interface to a complex subsystem. Use: Wrap complex library calls into a clean API (e.g.,
OrderService.placeOrder()hiding inventory, payment, shipping). - Flyweight: Share common state between many objects to save memory. Use: Character rendering in text editors, game tile maps, cached immutable objects.
- Proxy: Control access to an object via a surrogate. Use: Lazy loading, access control, logging, remote proxies (RMI).
Patterns that deal with communication between objects ā defining how they interact and distribute responsibility.
- Observer: One-to-many dependency notification. Use: Event systems, UI listeners, pub/sub messaging.
- Strategy: Define a family of interchangeable algorithms. Use: Sorting strategies, payment processing (credit card vs PayPal), compression algorithms.
- Command: Encapsulate a request as an object. Use: Undo/redo, task queues, macro recording.
- Template Method: Define an algorithm skeleton ā let subclasses override specific steps. Use: Abstract test fixtures, data processing pipelines.
- Mediator: Centralise complex communication between objects. Use: Chat rooms, air traffic control, form validation coordinators.
- Chain of Responsibility: Pass requests along a chain of handlers. Use: Logging levels, middleware pipelines, approval workflows.
- State: Object behaviour changes based on internal state. Use: Vending machines, order status workflows, connection states.
- Visitor: Add operations to objects without modifying them. Use: AST traversal, report generation, serialisation.
Data Structures & Algorithms
Essential data structures and algorithm patterns for problem-solving and technical interviews.
- Arrays: Contiguous memory, O(1) access by index. Foundation for most algorithms.
- ArrayList: Dynamic array ā auto-resizes. Amortised O(1) append, O(n) insert/delete in middle.
- LinkedList: Node-based ā O(1) insert/delete at head/tail, O(n) access by index. Use when frequent insertions/deletions are needed.
- Stack (LIFO): Push/pop in O(1). Use: Expression evaluation, undo operations, DFS traversal, parentheses matching.
- Queue (FIFO): Enqueue/dequeue in O(1). Use: BFS traversal, task scheduling, buffering.
- HashMap: Key-value store ā O(1) average get/put. Handles collisions via chaining or open addressing. Java's
HashMapuses tree bins for long chains (Java 8+). - Binary Search Tree (BST): O(log n) search/insert/delete when balanced. Degrades to O(n) if skewed. Self-balancing variants: AVL, Red-Black Tree.
- Heap (Priority Queue): Complete binary tree ā O(log n) insert/extract. Min-heap for smallest first, max-heap for largest. Java's
PriorityQueue. - Graph: Vertices + edges ā adjacency list (sparse) or adjacency matrix (dense). Directed/undirected, weighted/unweighted.
Sorting
- QuickSort: O(n log n) average, O(n²) worst ā in-place, divide-and-conquer with pivot partitioning. Fastest in practice for most inputs.
- MergeSort: O(n log n) guaranteed ā stable, divide-and-conquer. Requires O(n) extra space. Best for linked lists and external sorting.
Searching
- Binary Search: O(log n) on sorted arrays. Variants: lower/upper bound, rotated array search, search insert position.
- DFS (Depth-First Search): Explore as deep as possible before backtracking. Use: Cycle detection, topological sort, connected components, maze solving.
- BFS (Breadth-First Search): Explore all neighbours before going deeper. Use: Shortest path in unweighted graphs, level-order traversal.
Dynamic Programming
- Fibonacci / Climbing Stairs: Classic overlapping subproblems ā memoisation or tabulation.
- 0/1 Knapsack: Maximise value within weight constraint ā 2D DP table or space-optimised 1D.
- Longest Common Subsequence: String comparison, diff algorithms.
Greedy
- Activity Selection: Maximum non-overlapping intervals ā sort by end time, greedily pick.
- Huffman Coding: Optimal prefix-free encoding using a priority queue to build the tree.
Graph Algorithms
- Dijkstra's: Single-source shortest path for non-negative weights ā O((V+E) log V) with min-heap.
- Kruskal's: Minimum spanning tree ā sort edges by weight, add if no cycle (Union-Find).
- Topological Sort: Linear ordering of DAG vertices ā Kahn's algorithm (BFS) or DFS-based.
Interview Preparation
Structured preparation for technical interviews ā coding, system design, and behavioural.
Common problem categories and techniques to master:
Array & String Patterns
- Two Pointers: Two Sum II, Container With Most Water, Trapping Rain Water
- Sliding Window: Longest Substring Without Repeating, Minimum Window Substring
- Prefix Sum / Product: Product Except Self, Subarray Sum Equals K
- Sorting + Greedy: Merge Intervals, Meeting Rooms
Linked List Patterns
- Fast/Slow Pointer: Detect cycle, find middle, palindrome check
- Reversal: Reverse linked list, reverse in groups of K
- Merge: Merge two sorted lists, merge K sorted lists
Tree & Graph Patterns
- DFS/BFS Traversal: Inorder, preorder, level-order, zigzag order
- Path Problems: Path sum, max depth, diameter, lowest common ancestor
- Graph: Number of islands, course schedule (topological sort), clone graph
Dynamic Programming Patterns
- 1D DP: Climbing stairs, house robber, word break, longest increasing subsequence
- 2D DP: Unique paths, edit distance, longest common subsequence, knapsack
Common system design interview problems and the concepts they test.
- URL Shortener: Hashing, base62 encoding, database sharding, caching, rate limiting.
- Chat System: WebSockets, presence, message ordering, read receipts, horizontal scaling.
- Rate Limiter: Token bucket, sliding window, distributed rate limiting with Redis.
- LRU Cache: HashMap + doubly linked list for O(1) get/put ā cache eviction strategy.
- News Feed: Fan-out on write vs read, ranking algorithms, eventual consistency.
- Notification Service: Push (FCM/APNS), pull, message queues, delivery guarantees.
- Payment Gateway: Idempotency, distributed transactions, exactly-once processing, PCI compliance.
- Search Engine: Inverted index, ranking (TF-IDF), crawling, sharding, result caching.
Core Concepts to Know
- Horizontal vs vertical scaling, load balancing, CDN
- Database sharding, replication, consistency models (strong, eventual)
- Message queues (Kafka, SQS), event-driven architecture
- Caching strategies (write-through, write-back, cache-aside)
- Consistent hashing for distributed data partitioning
- CAP theorem trade-offs in practice
Structure behavioural answers as Situation ā Task ā Action ā Result.
Common Question Categories
- Problem Solving: Debugging a production outage, optimising a slow query, resolving a race condition.
- Teamwork: Cross-team collaboration, resolving conflicts with teammates, mentoring junior developers.
- Leadership: Driving technical decisions, pushing back on unrealistic deadlines, championing code quality.
- Failure & Learning: Post-mortem culture, recovering from a deployment failure, learning from a wrong architectural decision.
- Prioritisation: Balancing tech debt vs features, scoping work under tight timelines, saying no to scope creep.
Tips
- Prepare 5-8 stories that cover multiple categories.
- Quantify results where possible ("reduced latency by 40%", "cut deployment time from 2 hours to 15 minutes").
- Show self-awareness ā acknowledge mistakes and what you learned.
- Keep answers to 2-3 minutes. Don't ramble.
Java Best Practices
Conventions and patterns for clean, maintainable, and performant Java code.
- Classes: PascalCase ā
OrderService,UserRepository. - Methods/Variables: camelCase ā
getUserById(),totalAmount. - Constants: UPPER_SNAKE_CASE ā
MAX_RETRY_COUNT,DEFAULT_TIMEOUT. - Packages: Lowercase, reversed domain ā
com.winaykumar.service. - Single Responsibility: Each class should have one reason to change. Split large classes into focused services.
- SOLID Principles: Apply consistently for maintainable, testable, extensible code.
- Use try-with-resources for all
AutoCloseableresources (streams, connections, readers). - Catch specific exceptions, not
ExceptionorThrowable. - Log exceptions with context ā
log.error("Failed to process order {}", orderId, ex). - Use custom exceptions for domain-specific error conditions (e.g.,
InsufficientFundsException). - Never swallow exceptions silently ā always log or rethrow.
- Use
@ControllerAdvice+@ExceptionHandlerfor consistent REST API error responses.
- Use
StringBuilderfor string concatenation in loops ā avoid+operator. - Set
HashMapinitial capacity when size is known ā avoids rehashing overhead. - Use Streams wisely ā
parallelStream()only for CPU-heavy operations on large collections. - Avoid premature optimisation ā profile first (JProfiler, VisualVM), then optimise hot paths.
- Use
Optionalto avoid null checks ā but don't use it for fields or method parameters. - Cache expensive computations ā
Caffeinefor local cache,Redisfor distributed.
Advanced Java Topics
- Heap: Object storage. Young Gen (Eden + Survivor) and Old Gen. GC primarily targets Young Gen.
- Stack: Per-thread, stores local variables and method call frames. Fixed size (
-Xss). - Metaspace: Class metadata storage (replaced PermGen in Java 8). Grows dynamically but limit with
-XX:MaxMetaspaceSize. - Happens-Before: Memory visibility guarantees ā
volatilewrites are visible to subsequent reads,synchronizedblocks establish happens-before ordering. - Garbage Collection: Mark-and-sweep. G1GC (general), ZGC (low-pause), Shenandoah (low-pause). Tune based on latency vs throughput requirements.
- Stream Pipeline: Source ā Intermediate ops (lazy) ā Terminal op (triggers execution).
- Key Operations:
filter(),map(),flatMap(),reduce(),collect(),groupingBy(). - Parallel Streams: Use
parallelStream()only for CPU-intensive, stateless operations on large datasets. Avoid for I/O. - Collectors:
Collectors.toList(),toMap(),groupingBy(),partitioningBy(),joining(). - Method References:
String::toLowerCase,this::processItemā cleaner syntax for single-method lambdas. - Optional: Container for nullable values ā
map(),flatMap(),orElse(),orElseThrow().
Reflection
- Runtime inspection and modification of classes, methods, and fields via
java.lang.reflect. - Use cases: Serialisation frameworks (Jackson), ORM (Hibernate), dependency injection (Spring).
- Caution: Slow, bypasses access checks, breaks encapsulation. Use sparingly in application code.
Annotations
- Built-in:
@Override,@Deprecated,@SuppressWarnings,@FunctionalInterface. - Custom Annotations: Define with
@interface, control retention (@Retention(RUNTIME)) and targets (@Target(METHOD)). - Spring's annotation-driven model:
@Component,@Service,@Repository,@RestController,@Transactional. These are processed via reflection + proxying at startup.
Development Tools
Essential tools for Java development, CI/CD, and cloud deployment.
- IntelliJ IDEA: Premier Java IDE ā refactoring, debugging, Spring support, database tools. Learn keyboard shortcuts for 2-3x productivity gain.
- SonarQube: Static code analysis ā security vulnerabilities, code smells, coverage tracking. Integrate with CI.
- Checkstyle / SpotBugs: Enforce coding standards and find common bugs at build time.
- JProfiler / VisualVM: CPU and memory profiling, thread analysis, heap dumps ā essential for production debugging.
- Gradle: Modern build tool ā incremental builds, Kotlin DSL, multi-project support. Preferred over Maven for new projects.
- GitHub Actions: CI/CD workflows ā build, test, and deploy on every push. YAML-based, integrated with GitHub.
- Jenkins: Self-hosted CI/CD ā Jenkinsfile pipelines, extensive plugin ecosystem.
- Docker: Containerise applications ā consistent environments from dev to prod. Multi-stage builds for smaller images.
- Kubernetes: Orchestrate containers at scale ā deployments, services, auto-scaling, rolling updates.
- Prometheus: Metrics collection ā pull-based model, PromQL queries, alerting rules.
- Grafana: Visualisation dashboards ā connect to Prometheus, CloudWatch, or any data source.
- ELK Stack (Elasticsearch + Logstash + Kibana): Centralised logging ā aggregate, search, and visualise application logs.
- SLF4J + Logback: Structured logging in Java ā MDC for request tracing, log levels for filtering.
- JMeter: Load testing ā simulate concurrent users, measure throughput and response times.
- Micrometer: Application metrics facade for Spring Boot ā exports to Prometheus, Datadog, CloudWatch.