Interview Question Java Developer Fintech

📌 Interview Questions for Shivam Kumar

🔹 Core Java & OOPs

  1. What is the difference between HashMap and ConcurrentHashMap?

  2. How does volatile work in Java memory model? When do you use it?

  3. Explain the difference between synchronized methods vs. ReentrantLock.

  4. How does Garbage Collection work in Java? Can you force GC?

  5. What is the difference between final, finally, and finalize()?

  6. How do you implement immutability in a Java class?


🔹 Multithreading & Concurrency

  1. What is the difference between Runnable and Callable?

  2. What is the difference between CompletableFuture.runAsync() and supplyAsync()?

  3. How do thread pools work? What is the advantage of ExecutorService?

  4. What are daemon threads in Java?

  5. How does ForkJoinPool differ from ThreadPoolExecutor?

 

🔹 Spring Boot & Microservices

  1. What is the difference between @Component, @Service, @Repository, and @Controller?

  2. How does @Transactional work internally?

  3. What are the different scopes of Spring beans?

  4. How do you implement exception handling in REST APIs?

  5. Explain the difference between Monolithic vs. Microservices architecture.

  6. What are Circuit Breakers? How do you implement them in Spring Boot?

  7. How does Spring Boot auto-configuration work?



🔹 Kafka & Event-Driven Systems

  1. Explain the difference between Kafka Producer, Consumer, Broker, and Zookeeper.

  2. How does Kafka ensure exactly-once delivery?

  3. What is the difference between Kafka topic partitions and replication factor?

  4. How do you handle retries & dead-letter topics in Kafka?

  5. What are the use cases of Kafka in payment systems?



🔹 Databases (SQL + NoSQL)

  1. What is an Index? How does database indexing work internally?

  2. Difference between Clustered and Non-Clustered Index.

  3. Write an SQL query to find the nth highest salary.

  4. How do you optimize a slow SQL query?

  5. What is the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN?

  6. Transaction isolation levels: READ COMMITTED, REPEATABLE READ, SERIALIZABLE.

  7. Redis use case in caching transactions.



🔹 Fintech/Payments Domain (for NPCI experience)

  1. How do you ensure idempotency in payment APIs?

  2. How did you handle encryption (AES/RSA) in your project?

  3. What is the difference between real-time processing vs. batch settlement?

  4. How do you design a reconciliation system for cross-border payments?

  5. Explain how you would implement a transaction alert system using Kafka.



DSA & Problem-Solving

  1. Reverse a linked list (iterative & recursive).

  2. Detect a cycle in a linked list.

  3. Find the K largest elements in an array (Heap approach).

  4. Check if a string is a valid palindrome (ignore special characters).

  5. Find the Nth prime number.

  6. Merge two sorted linked lists.

  7. Implement LRU Cache.



🔹 Behavioral / Project Questions

  1. Can you explain a challenging bug you faced in NPCI’s UPI/RuPay system and how you solved it?

  2. How do you ensure security in financial transactions?

  3. What was the biggest performance bottleneck in your project and how did you fix it?

  4. How do you handle API versioning in Microservices?

  5. If a payment service goes down, how do you ensure fault tolerance?



 Question 1

👉 “Can you explain the difference between synchronized, volatile, and ConcurrentHashMap in Java, and when would you use each in a multi-threaded application?”


  • synchronized: Ensures that only one thread can execute a method/block at a time (mutual exclusion) and provides visibility guarantees. It can cause contention and, if used incorrectly, deadlocks.

  • volatile: Ensures visibility of updates to a variable across threads, but does not provide atomicity. Useful for flags and status checks.

  • ConcurrentHashMap: A thread-safe alternative to HashMap that avoids locking the entire map (unlike Hashtable). It uses fine-grained locks and CAS operations so multiple threads can read/write concurrently.

Java Question 2

👉 “Suppose you need to implement a thread-safe counter in Java. Show me three different ways you can do it: using synchronized, using volatile, and using AtomicInteger. Also explain which approach is best and why.”


1- Synchronized used -

package org.example.MultThreading;

public class SynchronisedThread extends Thread {
private int count = 0;

// synchronized method to avoid race condition
synchronized void counter() {
count++;
}

public int getCount() {
return count;
}

public static void main(String[] args) throws InterruptedException {
SynchronisedThread obj = new SynchronisedThread();

Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
obj.counter();
}
});

Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
obj.counter();
}
});

t1.start();
t2.start();

// wait for both threads to finish
t1.join();
t2.join();

System.out.println("Counter: " + obj.getCount()); // should be 2000
}
}



🔹 Key Fixes

  • Added synchronized to counter() → prevents race condition.

  • Used t1.join() and t2.join() → ensures main thread waits until both threads finish.


🔹 Why use AtomicInteger?



🔹 Why AtomicInteger is better than synchronized (in some cases)

1. Performance

  • synchronized → uses locks → only one thread enters the critical section at a time.

  • AtomicIntegerlock-free → uses low-level CAS (Compare-And-Swap) from the CPU.
    👉 This makes AtomicInteger faster in multi-threaded counters.


2. Non-blocking

  • With synchronized, if one thread holds the lock, others must wait (blocked).

  • With AtomicInteger, threads do not block; they retry CAS until success.
    👉 Better throughput under high concurrency.

package org.example.MultThreading;

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicThreadExample {
private AtomicInteger count = new AtomicInteger(0);

void counter() {
count.incrementAndGet(); // atomic increment
}

public int getCount() {
return count.get();
}

public static void main(String[] args) throws InterruptedException {
AtomicThreadExample obj = new AtomicThreadExample();

Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
obj.counter();
}
});

Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
obj.counter();
}
});

t1.start();
t2.start();

// wait for both threads to finish
t1.join();
t2.join();

System.out.println("Counter: " + obj.getCount()); // Always 2000
}
}

incrementAndGet() ensures thread-safety without locks.

All operations like get(), addAndGet(x), decrementAndGet() are atomic.

🔹 What is volatile?

👉 It ensures visibility, but not atomicity.


🔹 Why do we need it?

Without volatile, each thread might cache the variable locally → changes by one thread may not be visible to another.

🔹 Limitations of volatile

  • Only guarantees visibility, not atomicity.


🔹 Step 1: What happens in count++

Suppose we have:

This looks like a single operation, but under the hood it is actually 3 steps:

  1. Read → fetch value of count from memory into CPU/register.

  2. Modify → add 1 to the value in the register.

  3. Write → store the new value back into memory.

❌ Problem: Race condition

If two threads run count++ at the same time:

  • Initial count = 0

  • Thread 1 reads 0, increments to 1

  • Thread 2 also reads 0, increments to 1

  • Both write 1 back → lost update

👉 So instead of 2, final value is 1.

This is why count++ is not atomic.


🔹 Step 2: How AtomicInteger fixes it

AtomicInteger uses Compare-And-Swap (CAS) at the CPU level.

CAS mechanism:

  1. Read the current value (expected).

  2. Try to set a new value.

  3. If the value in memory is still the same as expected, update succeeds.

  4. If another thread already changed it, retry until success.

👉 This ensures atomicity without locks.


🔹 Summary

  • count++ = not atomic (read-modify-write, race condition possible).

  • AtomicInteger.incrementAndGet() = atomic CAS (retry until success, thread-safe, no locks).


Java Question 3 (Volatile)

👉 “What does the volatile keyword do in Java? Can you use it to make a counter thread-safe? Why or why not? Show me with code.”


🔹 What is volatile?

  • volatile is a keyword in Java used for variables that may be modified by multiple threads.

  • It ensures:

    1. Visibility → Changes made by one thread are immediately visible to other threads.

    2. No caching in thread’s local memory (CPU cache) → Always read/write directly from main memory.

⚠️ But note:

  • volatile does NOT guarantee atomicity (operations like count++ are not thread-safe with just volatile).

  • It only ensures latest value visibility.


Summary:








Java Question 4

👉 “Explain how wait() and notify() work in Java. Can you write a small producerconsumer example using them?”


The wait() method is used for interthread communication.

The wait() method is tightly integrated with the synchronization lock

The wait() method is defined in the Object class

When wait() is called on a thread holding the monitor lock, it surrenders the monitor lock and enters the waiting state.


The notify() method is used to wake up a single thread

The notify() method is used to give the notification for one thread for a particular object.

The notify() method does not have any return type value

When the notify() is called on a thread holding the monitor lock, it symbolizes that the thread is soon going to surrender the lock.


wait() and notify() are methods of Object class in Java, used for inter-thread communication.

When one thread is working inside a synchronized block and cannot proceed (e.g., producer waiting for buffer space), it calls wait().

This releases the lock and puts the thread in WAITING state until another thread calls notify() or notifyAll().

notify() is called by another thread (e.g., consumer after consuming an item) to wake up one waiting thread.

Producer-Consumer example:

Producer puts items in a buffer. If buffer is full → wait().

Consumer takes items from buffer. If buffer is empty → wait ().

Each side uses notify () to signal the other.

⚠️ Key point: wait () must be called inside a synchronized block because it needs the monitor lock.


With that in mind, we’ll make use of the following:



package org.example.MultThreading;

public class WaitNotifyExample {
private int data;
boolean available=false;

public synchronized void produce(int value) throws InterruptedException
{

while(available){
wait();//wait until cosumer consumer
}
data =value;
System.out.println("produce "+value);
available=true;
notify();
}

public synchronized int consume() throws InterruptedException
{
while (!available)
{
wait(); // wait until producer produces
}

System.out.println("consume "+data);
available=false;
notify();
return data;
}

public static void main(String[] args) {

WaitNotifyExample obj=new WaitNotifyExample();

Thread producer = new Thread(()->{

try
{
for(int i=1;i<=10;i++)
{
obj.produce(i);
Thread.sleep(500);
}
}
catch(InterruptedException e)
{
e.printStackTrace();
}
});

Thread consumer=new Thread(()->{

try{

for (int i=1;i<=10;i++)
{
obj.consume();
Thread.sleep(500);
}
}catch(Exception e)
{
e.printStackTrace();
}

});

producer.start();
consumer.start();
}

}


🔑 Key Points (for interviews)

  • wait() → releases lock and suspends thread.

  • notify() → wakes up one waiting thread (use notifyAll() if multiple).

  • Always used inside synchronized to avoid race conditions.


🔹 Concept: BlockingQueue

  • A BlockingQueue is a thread-safe queue provided by java.util.concurrent.

  • It is designed for Producer–Consumer problems so you don’t have to manually manage wait() and notify().

  • Two main methods:

    • put() → producer inserts an item. If the queue is full, producer waits automatically.

    • take() → consumer removes an item. If the queue is empty, consumer waits automatically.

  • This removes the need for explicit synchronized, wait(), and notify().

  • Common implementations:


package org.example.MultThreading;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class BlockingQueueExample {

public static void main(String[] args) {

BlockingQueue<Integer> queue= new ArrayBlockingQueue<>(3);

Thread producer = new Thread(() ->{

try
{
for(int i=1;i<=5;i++)
{
queue.put(i);
System.out.println("Produce "+i);
Thread.sleep(100);
}

}catch(InterruptedException e)
{
e.printStackTrace();
}

});

Thread consumer = new Thread(() ->{

try
{
for(int i=1;i<=5;i++)
{
int value=queue.take();
System.out.println("cosume "+value);
Thread.sleep(100);
}

}catch(InterruptedException e)
{
e.printStackTrace();
}

});

producer.start();
consumer.start();
}
}


🔑 Interview Takeaways

  • With BlockingQueue, you don’t manually call wait() or notify().

  • It’s safer, cleaner, and less error-prone.

  • In real-world microservices or fintech systems, BlockingQueue is preferred over raw wait/notify.


👉 “Can you explain the Java Memory Model (JMM) and how it affects multithreading? Specifically, what role do volatile, synchronized, and final play in visibility and ordering of variables?”

Java Memory Model (JMM) 

The Java Memory Model (JMM) defines how threads interact with memory and what guarantees Java provides for visibility, ordering, and atomicity of shared variables.

  • Without JMM, each thread may cache variables locally → leading to stale or inconsistent values.

  • JMM ensures correct interaction through rules like happens-before relationships.

Key constructs in JMM:

  1. volatile → guarantees visibility and prevents instruction reordering, but not atomicity.

  2. synchronized → ensures both mutual exclusion and visibility (via happens-before).

  3. Atomic classes (AtomicInteger, etc.) → use low-level Compare-And-Swap (CAS) to achieve atomicity without locks.

  4. final fields → once an object is constructed and published safely, its final fields are visible correctly to other threads (safe publication).

👉 In short:
JMM solves issues of visibility, ordering, and atomicity, making multithreaded programming in Java safe and predictable.



❓ Interview Question 6:

"What is the difference between Callable and Runnable in Java? Can you show a practical example where Callable is preferred over Runnable?

Runnable

  • Introduced in Java 1.0.

  • Has a single method run().

  • Cannot return a result.

  • Cannot throw checked exceptions.

  • Suitable when you just need to execute a task in a separate thread without expecting any output.

Callable

  • Introduced in Java 5 (as part of java.util.concurrent).

  • Has a single method call().

  • Can return a result (V generic type).

  • Can throw checked exceptions.

  • Usually executed via an ExecutorService using submit().

  • Works with Future to get result, cancel tasks, or check status.

💡 When to use Callable over Runnable:

  • When you need the thread to return a result.

  • When you want to propagate checked exceptions from the task.

  • For long-running tasks where you want to cancel/monitor them using Future.



package org.example.MultThreading;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableVsRunnableExample {

public static void main(String[] args) throws Exception {
// Create thread pool
ExecutorService executorService = Executors.newFixedThreadPool(3);

// Runnable task (does not return result)
Runnable runnableTask = () -> {
System.out.println("Runnable task executed by: " + Thread.currentThread().getName());
};

// Callable task (returns result)
Callable<String> callableTask = () -> {
Thread.sleep(500); // simulate some work
return "Callable task result from: " + Thread.currentThread().getName();
};

// Execute Runnable (no return value)
executorService.execute(runnableTask);

// Submit Callable (returns Future)
Future<String> future = executorService.submit(callableTask);

// Get result from Callable
String result = future.get();
System.out.println("Callable returned: " + result);

// Shutdown executor
executorService.shutdown();
}
}


Difference between sleep() and wait()


Summary in your words:

  • sleep() → Thread pauses but still blocks others from using the lock.

  • wait() → Thread pauses and frees the lock, so other threads can run.




Q: What is the difference between notify() and notifyAll() in Java?



notify() → wakes up only ONE thread that is waiting on the object’s monitor.

notifyAll() → wakes up ALL the threads waiting on the object’s monitor.




 Quick Interview Summary

  • notify() → wakes one random thread from the wait set.

  • notifyAll() → wakes all waiting threads, but only one at a time will acquire the lock and continue.



         Q: What is the difference between synchronized block and ReentrantLock in Java?


  • Synchronized block

    • Easy to use (synchronized keyword).

    • A thread waits indefinitely until the lock is available.

    • Can cause thread starvation (if one thread keeps holding lock).

    • No flexibility (no tryLock, no timed lock, no interruptible lock).

  • ReentrantLock (from java.util.concurrent.locks)

    • Provides the same mutual exclusion, but with more advanced features:

      • lock() → acquire lock (like synchronized).

      • unlock() → release lock (must always call in finally).

      • tryLock() → try to get lock; if not available, thread can go do other work instead of waiting.

      • tryLock(timeout) → wait for some time, then give up if not available.

      • lockInterruptibly() → thread can be interrupted while waiting.

    • Avoids starvation and gives better control.

    👉 See the difference:

    • With synchronized, Thread-2 must wait until Thread-1 releases.

    • With ReentrantLock.tryLock(), Thread-2 does not wait and goes to do something else.



    Q: What is the difference between submit() and execute() in ExecutorService?


    • execute()

      • Defined in Executor interface.

      • Takes only a Runnable.

      • Does not return anything (fire-and-forget).

      • If the task throws an exception, it goes to the thread’s uncaught exception handler.

    • submit()

      • Defined in ExecutorService.

      • Can take Runnable or Callable.

      • Always returns a Future object, which can be used to get result or handle exceptions.

      • More flexible because you can check task status (isDone(), cancel(), etc.).

    👉 In short:

    • Use execute() when you don’t care about result.

    • Use submit() when you need result or exception handling.


    👉 “What is Future and FutureTask in Java, and what’s the difference between them?”



    • Future

      • It is just an interface (object reference) that represents the result of an asynchronous computation.

      • You get it when you call submit() on ExecutorService.

      • With it, you can:

        • get() the result (blocking).

        • cancel() the task.

        • check isDone(), isCancelled().

      • But Future itself does not run anything — it only holds task’s result.

    • FutureTask

      • It is a concrete class that implements both Runnable and Future.

      • You can wrap a Callable (or Runnable) into a FutureTask.

      • Then you can either:

        • submit it to an Executor, or

        • run it directly in a Thread.

      • Useful when you want both: a task to run and a Future handle to manage its result.

    👉 In short:

    • Future → a handle to the result.

    • FutureTask → a task implementation that can be executed and gives you a Future.



    “What are the drawbacks of Future, and how does CompletableFuture improve it?”


    💯 Correct — you got the main drawback of Future right:

    • With Future.get(), the main thread blocks until the result is ready.

    • This can hurt performance and responsiveness.

    👉 That’s why Java introduced CompletableFuture (Java 8).


    🔹 Interview-Ready Explanation

    Drawbacks of Future:

    1. get() blocks → can’t do other work in the meantime.

    2. No simple way to chain multiple async tasks.

    3. No built-in callback mechanism (you have to poll isDone()).

    How CompletableFuture solves it:

    • Non-blocking (async).

    • Supports callbacks (thenApply, thenAccept, thenRun).

    • Supports chaining of multiple tasks.

    • Supports combining multiple futures (thenCombine, allOf, etc.).

    • Provides methods like supplyAsync(), runAsync() to run tasks in the background.


    Summary you can tell in interview:

    • Future → blocking, no chaining, limited.

    • CompletableFuture → non-blocking, supports chaining & callbacks, better for async programming.


    🔹 What is a Callback?

    👉 A callback is a function/method that you pass to another method, which will be executed later when a task completes.

    • Instead of blocking and waiting for the result, you say:
      “Hey, when you’re done, call this method for me.”

    That "method to call later" = callback.


    🔹 Example in Simple Terms

    Imagine you order pizza 🍕:

    • You don’t sit in the shop until it’s ready (blocking).

    • You give them your phone number 📞.

    • When pizza is ready, they call you back.
      That phone number = callback.



    Interview Definition:
    A callback is a function that is invoked automatically after a task completes, instead of waiting synchronously. It improves responsiveness and enables async programming.



    What is the difference between CompletableFuture.runAsync() and CompletableFuture.supplyAsync()?”



     Interview Style Answer

    • runAsync() is used when the task does not return any result.

    • supplyAsync() is used when the task returns a result.

    • Both are non-blocking and run in the ForkJoinPool.commonPool() by default (unless you provide your own Executor).




    "What is the difference between ExecutorService and ForkJoinPool in Java? When would you use one over the other?"


    🔹 What is ExecutorService?

    • In Java, if we create threads using new Thread(), we manually manage lifecycle (start, stop, join).

    • For large applications, this becomes hard to manage.

    • ExecutorService (from java.util.concurrent) solves this by providing a thread pool where threads are reused.

    So instead of creating a new thread every time, you just submit a task (Runnable or Callable) and ExecutorService will handle scheduling, execution, and lifecycle.


    🔹 Key Methods of ExecutorService

    1. execute(Runnable task) → runs a Runnable task (no return).

    2. submit(Callable<T> task) → runs a Callable task and returns a Future<T>.

    3. shutdown() → stops accepting new tasks but lets existing tasks finish.

    4. shutdownNow() → forcefully stops tasks.

    5. invokeAll(Collection<Callable>) → runs multiple tasks and waits for all results.

    6. invokeAny(Collection<Callable>) → runs multiple tasks and returns the result of the first successful one.




    🔹 ExecutorService

    • Part of java.util.concurrent.

    • Provides a thread pool to manage tasks (instead of manually creating threads).

    • You submit tasks (Runnable or Callable) → it schedules them in a fixed number of threads.

    • Good for independent tasks like handling requests, sending emails, DB queries, etc.

    • Example: FixedThreadPool, CachedThreadPool, ScheduledThreadPool.

    👉 Use when tasks are independent and don’t rely on splitting into subtasks.


    🔹 ForkJoinPool

    • Part of java.util.concurrent (since Java 7).

    • Uses work-stealing algorithm → idle threads can "steal" tasks from busy ones.

    • Best for divide-and-conquer / recursive algorithms (parallelism).

    • Example: Parallel streams in Java 8 use ForkJoinPool internally.

    👉 Use when tasks can be broken into smaller subtasks (e.g., parallel sort, Fibonacci, matrix multiplication).




    "What are Java Streams, and how does parallel stream work internally? Can you explain when using a parallel stream might be harmful instead of beneficial?"



    🔹 1. What are Java Streams?

    • Introduced in Java 8 inside java.util.stream.

    • Stream = a sequence of elements that you can process (filter, map, reduce, collect, etc.) in a functional style.

    • Example:

    public class javaStreamExample {
    public static void main(String[] args) {


    List<String> names= Arrays.asList("Annkit","Aadff","Swati","Sneffdf");


    List<String> result= names.stream().filter(name ->name.startsWith("A")).collect(Collectors.toList());

    System.out.println(result);

    }

    }


    • .


    🔹 2. Parallel Streams

    • parallelStream() splits data into chunks and processes them in multiple threads using ForkJoinPool (common pool).


    List<Integer> numbers = IntStream.rangeClosed(1, 10).boxed().toList();

    int sum = numbers.parallelStream()
    .mapToInt(Integer::intValue)
    .sum();

    System.out.println("Sum: " + sum);



    🔹 3. When Parallel Stream is Harmful?

    Parallel streams are not always faster. In fact, they can slow you down if:

    1. Small Data Set → overhead of splitting/joining > benefit.

    2. Shared Mutable State → causes race conditions.


    IO Bound Operations → parallel streams only work well for CPU-bound tasks, not for file/DB operations.

    Custom Thread Pool Needed → parallel stream uses ForkJoinPool.commonPool, which may interfere with other parallel tasks.



    difference between stream() vs parallelStream()?

    • In Java 8, the Stream API is used to process collections of data in a functional and declarative style.

      • Operations like filter(), map(), reduce(), collect() let us transform and aggregate data in a clean way.

      • By default, a stream processes sequentially (one element at a time, in order).

    • A parallel stream splits the data source into multiple chunks and processes them concurrently using the ForkJoinPool framework.

      • Each chunk runs in a separate thread.

      • Finally, results are combined (merged) to produce the output.

    • ⚠️ Trade-offs:

      • Good for large CPU-bound tasks.

      • Bad for small datasets, shared mutable state, or I/O-bound tasks because overhead can hurt performance or cause race conditions.

     =========================================================================
                                                                     Spring And SprinBoot

    What is the difference between @Component, @Service, @Repository, and @Controller in Spring? When would you use each?


    ✅ Spring Stereotype Annotations

    1. @Component

      • Generic stereotype for any Spring-managed bean.

      • Tells Spring: “Create an instance of this class and manage it in the ApplicationContext.”

      • Can be used for any class that doesn’t fall under a more specific stereotype.

    2. @Service

      • Specialization of @Component.

      • Indicates the class contains business logic / service layer code.

      • Mainly used for clarity and readability — Spring treats it the same as @Component.

    3. @Repository

      • Specialization of @Component for DAO / persistence layer.

      • Adds exception translation: Spring automatically converts database exceptions (like SQLException) into Spring’s DataAccessException.

    4. @Controller

      • Specialization of @Component for presentation layer.

      • Handles HTTP requests and returns a view (HTML, JSP, Thymeleaf) or response data.

    💡 Tip for interviews:

    • Use @Component for generic beans.

    • Use @Service for business logic.

    • Use @Repository for database access.

    • Use @Controller for web layer / request handling.

     


    @Controller vs @RestController

    1. @Controller

      • Handles HTTP requests.

      • Typically used to return a view (HTML, JSP, Thymeleaf).

      • If you want to return JSON or XML, you need to annotate the method with @ResponseBody.

    2. @RestController

      • Specialization of @Controller + @ResponseBody.

      • All methods automatically serialize the returned object to JSON or XML.

      • Used for building RESTful APIs.

    @Controller
    public class MyController {
    @GetMapping("/hello")
    public String hello() {
    return "hello.html"; // returns view
    }
    }

    @RestController
    public class MyRestController {
    @GetMapping("/api/hello")
    public String helloApi() {
    return "Hello JSON"; // returns response body directly
    }
    }


    "What is the difference between @RequestParam, @PathVariable, and @RequestBody in Spring Boot? When would you use each?"



    ✅ Spring Boot: @PathVariable, @RequestParam, @RequestBody

    1. @PathVariable

      • Used to extract values from the URI path.

      • Example:


    @GetMapping("/users/{id}")
    public User getUserById(@PathVariable int id) {
    return userService.getById(id);
    }


    URL: /users/5 → id = 5

    2.@RequestParam

    Used to extract query parameters from the URL.

    Example:

    @GetMapping("/users")
    public List<User> getUsers(@RequestParam String name, @RequestParam int age) {
    return userService.filterByNameAndAge(name, age);
    }


    URL: /users?name=shivam&age=22 → name=shivam, age=22

    3.@RequestBody

    Used to bind HTTP request body to a Java object.

    Typically used in POST/PUT requests for sending JSON or XML.

    Example:


    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
    return userService.save(user);
    }

    Request body {
    "name":shiv Ji
    "age":1000000000
    }


    💡 Interview tip:

    • PathVariable → identity / specific resource.

    • RequestParam → filtering / optional query parameters.

    • RequestBody → full object / payload in POST/PUT requests.


    How do you handle exceptions in Spring Boot? Explain the difference between @ExceptionHandler and @ControllerAdvice."


    try-catch

    Basic way to handle exceptions inside a method.

    Works, but not centralized — each method needs its own handling.

    @ExceptionHandler

    Can be used in a specific controller class to handle exceptions thrown by that controller.

    Example:

    @RestController
    public class UserController {

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable int id) {
    if (id <= 0) throw new IllegalArgumentException("Invalid ID");
    return new User(id, "Shivam");
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public String handleIllegalArgument(IllegalArgumentException ex) {
    return ex.getMessage();
    }

    @ControllerAdvice

    Global exception handler for all controllers.

    Centralizes exception handling in one class.

    Example:

    @ControllerAdvice
    public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException ex) {
    return ResponseEntity.badRequest().body(ex.getMessage());
    }
    }







    🔹 Pagination & Sorting in Spring Boot

    Spring Data JPA provides built-in support for pagination and sorting using the Pageable and Sort interfaces.


    ✅ Interview Tips

    • Always mention why pagination is important → avoids loading huge datasets in memory, improves performance.

    • Sorting is straightforward via Sort object.

    • Spring Data JPA makes this very easy with Pageable and Sort.


    1️⃣ Pagination

    • Pageable lets you specify:

      • page → page number (0-based index)

      • size → number of items per page

    • Example repository method:


    public interface UserRepository extends JpaRepository<User, Long> {
    Page<User> findAll(Pageable pageable);
    }

    @RestController
    @RequestMapping("/users")
    public class UserController {

    private final UserRepository userRepository;

    public UserController(UserRepository userRepository) {
    this.userRepository = userRepository;
    }

    @GetMapping
    public Page<User> getUsers(@RequestParam int page, @RequestParam int size) {
    return userRepository.findAll(PageRequest.of(page, size));
    }
    }


    @GetMapping("/sorted")
    public List<User> getUsersSorted() {
    return userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));
    }


    @GetMapping("/paged-sorted")
    public Page<User> getPagedAndSortedUsers(@RequestParam int page,
    @RequestParam int size,
    @RequestParam String sortBy) {
    return userRepository.findAll(PageRequest.of(page, size, Sort.by(sortBy)));
    }





    How do you implement caching in Spring Boot using Redis? Explain the annotations @Cacheable, @CachePut, and @CacheEvict.


    ✅ Spring Boot Caching with Redis

    1. Enable caching

    • Add @EnableCaching in your main Spring Boot application class.

    • This tells Spring to look for caching annotations on methods.


    @SpringBootApplication
    @EnableCaching
    public class MyApplication {
    public static void main(String[] args) {
    SpringApplication.run(MyApplication.class, args);
    }
    }
    1. Connect Redis

    • Add Redis dependency in pom.xml and configure application.properties:


    spring.redis.host=localhost
    spring.redis.port=6379

    1. Caching Annotations

    • @Cacheable

      • Used on a method to store the result in cache.

      • Next time the method is called with the same parameters, the cached value is returned (no DB call).



    @Cacheable(value = "users", key = "#id")
    public User getUserById(Long id) {
    System.out.println("Fetching from DB...");
    return userRepository.findById(id).orElse(null);
    }

    @CachePut

    • Updates cache without skipping method execution.

    • Useful when you want to update both DB and cache.

    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
    return userRepository.save(user);
    }

    @CacheEvict

    • Removes entry from cache.

    • Useful when deleting or updating data.



    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
    userRepository.deleteById(id);
    }




    🔑 Interview Tips

    • Always mention why caching is important → reduces DB calls, improves performance.

    • Know Redis as a key-value store, Spring Boot integrates easily via spring-boot-starter-data-redis.

    • @Cacheable → read caching,
      @CachePut → update caching,
      @CacheEvict → delete from cache.



    🔹 1. Event-Driven Architecture (EDA) Concept

    • In an event-driven system, services communicate via events instead of direct API calls.

    • Producer → publishes an event (message) when something happens.

    • Consumer → subscribes to events and reacts to them asynchronously.

    • Advantages:

      • Loose coupling between services.

      • Asynchronous processing → improves scalability.

      • Can integrate multiple microservices without tight dependencies.

    Example:

    • Payment service publishes PaymentSuccess event.

    • Notification service consumes it to send an email.




    🔹 2. Kafka Overview

    • Apache Kafka is a distributed publish-subscribe messaging system.

    • Concepts:

      • Topic → category of messages.

      • Producer → writes messages to topic.

      • Consumer → reads messages from topic.

      • Broker → Kafka server node.

    • Kafka is high-throughput, fault-tolerant, ideal for microservices.




    Q1: What is Apache Kafka?

    • Kafka is a distributed streaming platform used for high-throughput, fault-tolerant, real-time data pipelines.

    • It can be used as:

      1. Messaging system (like RabbitMQ)

      2. Storage system (durable logs)

      3. Stream processing (via Kafka Streams or consumer apps)


    Q2: What are the main components of Kafka?

    • Producer → sends messages to Kafka topics.

    • Consumer → reads messages from Kafka topics.

    • Broker → Kafka server that stores messages.

    • Topic → category/feed to which messages are published.

    • Partition → a topic can be split into partitions for scalability.

    • ZooKeeper / KRaft → manages cluster metadata (Kafka 2.x uses ZK; Kafka 3.x can use KRaft).

    Q3: What is a Kafka topic and partition?

    • Topic → logical channel to which messages are sent.

    • Partition → physical division of a topic; helps in parallelism & scalability.

    • Offset → each message in a partition has a unique ID (sequence number).

    Q4: What is an offset in Kafka?

    • A unique sequential ID assigned to each message within a partition.

    • Consumers track offsets to know which message they have read.

    Q5: Explain Consumer Groups

    • A consumer group allows multiple consumers to read from a topic in parallel.

    • Rules:

      • Each partition is consumed by only one consumer in a group.

      • Multiple consumers in a group = parallel processing.

      • Multiple consumer groups = independent processing.

    Q6: What is Kafka retention?

    • Kafka can retain messages based on:

      • Time (e.g., 7 days)

      • Size (e.g., 10GB per topic)

    • Allows consumers to read at their own pace.

    Q8: What is the difference between at-most-once, at-least-once, and exactly-once delivery?

    • At-most-once → message may be lost, but never duplicated.

    • At-least-once → message may be duplicated but never lost.

    • Exactly-once → message is neither lost nor duplicated.

    Q9: How does Kafka ensure message ordering?

    🧩 Partition-Level Ordering

    • Messages are ordered within a partition: Kafka guarantees that messages sent to the same partition are stored and delivered in the exact order they were produced.

    • No global ordering across partitions: If a topic has multiple partitions, Kafka does not guarantee ordering across them. So if ordering is critical, you must design your system to use a single partition or ensure related messages go to the same one.

    🛠 Producer Controls

    • Key-based partitioning: Producers can assign a key to each message. Kafka uses this key to consistently route messages to the same partition, preserving order for that key.

    • Synchronous sends: If a producer sends messages synchronously (waiting for acknowledgment before sending the next), it helps maintain strict order.


    📦 Consumer Guarantees

    • Sequential reads: Consumers read messages from a partition in the order they were written.

    • Offset tracking: Kafka uses offsets to track the position of each consumer, ensuring they process messages in sequence.

    Q10: Explain Kafka Producer Acknowledgments (acks)

    • acks=0 → Producer does not wait for broker acknowledgment. (Fastest, may lose data)

    • acks=1 → Leader writes message, producer gets acknowledgment.

    • acks=all / acks=-1 → All in-sync replicas (ISR) acknowledge → safest, slower.


    "acks = all" tells Kafka that the producer should wait for acknowledgment from all in-sync replicas before considering a message successfully sent.

    🔒 Why Use It?

    • Ideal for critical data where durability matters more than latency.

    • Helps prevent data loss in case of broker crashes or network partitions.





    Today
    How does Kafka ensure message ordering?

    Apache Kafka ensures message ordering through a combination of partitioning, producer behavior, and consumer guarantees. Here's how it works:

    🧩 Partition-Level Ordering

    • Messages are ordered within a partition: Kafka guarantees that messages sent to the same partition are stored and delivered in the exact order they were produced.

    • No global ordering across partitions: If a topic has multiple partitions, Kafka does not guarantee ordering across them. So if ordering is critical, you must design your system to use a single partition or ensure related messages go to the same one.

    🛠 Producer Controls

    • Key-based partitioning: Producers can assign a key to each message. Kafka uses this key to consistently route messages to the same partition, preserving order for that key.

    • Synchronous sends: If a producer sends messages synchronously (waiting for acknowledgment before sending the next), it helps maintain strict order.

    📦 Consumer Guarantees

    • Sequential reads: Consumers read messages from a partition in the order they were written.

    • Offset tracking: Kafka uses offsets to track the position of each consumer, ensuring they process messages in sequence.

    ⚠️ Things to Watch Out For

    • Multiple partitions: If you use multiple partitions for scalability, you lose total ordering unless you implement custom logic.

    • Retries and failures: If a producer retries a failed message, it could arrive out of order unless handled carefully.

    • Consumer parallelism: If consumers process messages in parallel, you might need additional logic to preserve order during processing.

    Want to dive deeper into how to design a Kafka topic for ordered processing or how to handle ordering in a distributed consumer group?

    if ack =-1 means

    In Apache Kafka, setting acks = -1 (or more commonly, acks = all) in the producer configuration means:

    Strongest Message Durability Guarantee

    • "acks = all" tells Kafka that the producer should wait for acknowledgment from all in-sync replicas before considering a message successfully sent.

    • This ensures that the message is fully replicated and won’t be lost even if the leader broker fails immediately after the write.

    🔒 Why Use It?

    • Ideal for critical data where durability matters more than latency.

    • Helps prevent data loss in case of broker crashes or network partitions.

    ⚠️ Trade-offs

    • Higher latency: Waiting for multiple replicas takes longer than just waiting for the leader.

    • Throughput impact: Slightly reduced throughput compared to acks=1 or acks=0.

    Quick Comparison

    acks ValueDescriptionDurabilityLatency
    0No ack neededLowVery low
    1Leader ack onlyMediumLow
    -1 or allAll in-sync replicas ackHighHigher

    Let me know if you want to explore how this interacts with retries, idempotence, or Kafka’s replication mechanics.

    what is kafka isr

    In Apache Kafka, ISR stands for In-Sync Replicas—a key concept that ensures data durability and fault tolerance in Kafka’s distributed architecture.

    🧠 What Is an ISR?

    • An In-Sync Replica is a follower replica of a partition that is fully caught up with the leader replica.

    • Kafka maintains a dynamic list of these replicas for each partition. Only replicas in this list are eligible to become the new leader if the current one fails.



    Today
    How does Kafka ensure message ordering?

    Apache Kafka ensures message ordering through a combination of partitioning, producer behavior, and consumer guarantees. Here's how it works:

    🧩 Partition-Level Ordering

    • Messages are ordered within a partition: Kafka guarantees that messages sent to the same partition are stored and delivered in the exact order they were produced.

    • No global ordering across partitions: If a topic has multiple partitions, Kafka does not guarantee ordering across them. So if ordering is critical, you must design your system to use a single partition or ensure related messages go to the same one.

    🛠 Producer Controls

    • Key-based partitioning: Producers can assign a key to each message. Kafka uses this key to consistently route messages to the same partition, preserving order for that key.

    • Synchronous sends: If a producer sends messages synchronously (waiting for acknowledgment before sending the next), it helps maintain strict order.

    📦 Consumer Guarantees

    • Sequential reads: Consumers read messages from a partition in the order they were written.

    • Offset tracking: Kafka uses offsets to track the position of each consumer, ensuring they process messages in sequence.

    ⚠️ Things to Watch Out For

    • Multiple partitions: If you use multiple partitions for scalability, you lose total ordering unless you implement custom logic.

    • Retries and failures: If a producer retries a failed message, it could arrive out of order unless handled carefully.

    • Consumer parallelism: If consumers process messages in parallel, you might need additional logic to preserve order during processing.

    Want to dive deeper into how to design a Kafka topic for ordered processing or how to handle ordering in a distributed consumer group?

    if ack =-1 means

    In Apache Kafka, setting acks = -1 (or more commonly, acks = all) in the producer configuration means:

    Strongest Message Durability Guarantee

    • "acks = all" tells Kafka that the producer should wait for acknowledgment from all in-sync replicas before considering a message successfully sent.

    • This ensures that the message is fully replicated and won’t be lost even if the leader broker fails immediately after the write.

    🔒 Why Use It?

    • Ideal for critical data where durability matters more than latency.

    • Helps prevent data loss in case of broker crashes or network partitions.

    ⚠️ Trade-offs

    • Higher latency: Waiting for multiple replicas takes longer than just waiting for the leader.

    • Throughput impact: Slightly reduced throughput compared to acks=1 or acks=0.

    Quick Comparison

    acks ValueDescriptionDurabilityLatency
    0No ack neededLowVery low
    1Leader ack onlyMediumLow
    -1 or allAll in-sync replicas ackHighHigher

    Let me know if you want to explore how this interacts with retries, idempotence, or Kafka’s replication mechanics.

    what is kafka isr

    In Apache Kafka, ISR stands for In-Sync Replicas—a key concept that ensures data durability and fault tolerance in Kafka’s distributed architecture.

    🧠 What Is an ISR?

    • An In-Sync Replica is a follower replica of a partition that is fully caught up with the leader replica.

    • Kafka maintains a dynamic list of these replicas for each partition. Only replicas in this list are eligible to become the new leader if the current one fails.

    🔁 How ISR Works

    • Leader and Followers: Each Kafka partition has one leader and multiple followers. The leader handles all reads and writes.

    • Replication: Followers replicate data from the leader. If they keep up within a configured lag threshold (replica.lag.time.max.ms), they stay in the ISR list.

    • Lagging Replicas: If a follower falls too far behind, Kafka removes it from the ISR list to prevent stale data from being promoted.

    • Leader Election: If the leader fails, Kafka elects a new leader from the ISR list—ensuring the new leader has the most recent data.


    ⚙️ Key Configurations

    ParameterPurpose
    min.insync.replicasMinimum number of ISRs required to acknowledge a write for it to be considered successful.
    replica.lag.time.max.msMaximum time a follower can lag behind before being removed from ISR.


    Q12: How do you achieve exactly-once semantics in Kafka?

    • Enable idempotent producer (enable.idempotence=true).

    • Use transactions for multiple partitions/topics.



    🧪 1. Idempotent Producer

    • Kafka producers can be configured with enable.idempotence=true.

    • This ensures that duplicate messages are not written, even if retries occur due to network issues or broker failures.

    • Kafka assigns a unique sequence number to each message from a producer, allowing brokers to detect and discard duplicates.




    Q14: How do you handle consumer failure?

    • Kafka tracks offsets → when consumer restarts, it resumes from last committed offset.

    • Can use manual commit (commitSync / commitAsync) for more control.


    Q15: How is Kafka scalable?

    1. Partitions → multiple consumers read in parallel.

    2. Cluster → multiple brokers for fault tolerance.

    3. Replication → ensures high availability.


    ✅ How to create KafkaProducer and KafkaConsumer in Java

    Answer:

    To create a KafkaProducer, I set up properties like server address, key/value serializers, and then use new KafkaProducer<>(props) to send messages. For KafkaConsumer, I set properties like group ID and deserializers, subscribe to topics, and use a loop to poll messages.


    ✅ How to set acks, retries, and batch.size for producer

    Answer:

    I set acks=all to make sure the message is safely stored. I use retries=3 to retry sending if something fails. I set batch.size to control how many messages are sent together to improve performance.


    ✅ How to commit offsets manually vs automatically

    Answer:

    If I use enable.auto.commit=true, Kafka saves the offset automatically. For manual commit, I set enable.auto.commit=false and use commitSync() after processing each message. This gives me more control and avoids losing data.


    ✅ How to use KafkaTemplate in Spring Boot

    Answer:

    In Spring Boot, I use KafkaTemplate to send messages easily. I configure it in a class with @Bean, and then I can call kafkaTemplate.send(topic, message) to publish messages.



     

    what happen when producver star producing message ?


    🛠️ 1. Producer connects to Kafka

    • It uses the broker address (like localhost:9092) to connect to the Kafka cluster.

    📦 2. Message is created

    • The producer prepares a message with a key, value, and topic name.

    🚀 3. Message is sent

    • It sends the message using send() method.

    • Kafka decides which partition the message goes to (based on key or round-robin).

    ✅ 4. Acknowledgment

    • If acks=all, the producer waits for confirmation that all brokers received the message.

    • If acks=0, it doesn’t wait—just sends and moves on.

    🔁 5. Retries if needed

    • If sending fails, and retries is set, it will try again.

    📊 6. Batching and buffering

    • Kafka may group messages into batches to send more efficiently.

    • These are stored in a buffer before sending.


    What is a microservice architecture, and how do you implement service discovery in Spring Boot?


    🔹 1. What is Microservice Architecture?

    • Microservices = breaking a large application into small, independent services, each responsible for a specific business function.

    • Characteristics:

      1. Independent deployment → each service can be deployed without affecting others.

      2. Loosely coupled → services communicate via APIs (REST, messaging).

      3. Scalable → each service can scale independently.

      4. Technology agnostic → each service can use different tech stack if needed.

    Example:

    • An e-commerce app:

      • User Service → manages users

      • Order Service → handles orders

      • Payment Service → handles payments

      • Services communicate asynchronously (via Kafka) or synchronously (via REST API).



    🔹 2. Service Discovery

    • In microservices, services run on dynamic ports and may scale up/down.

    • Service Discovery helps locate available service instances dynamically.

    2.1 How it works

    1. Each service registers itself with a Service Registry.

    2. When a service wants to call another service, it queries the registry to find the instance.

    2.2 Tools

    • Eureka (Netflix OSS) → common in Spring Boot ecosystem

    • Consul / Zookeeper → alternatives


    Configure application.properties



    spring.application.name=user-service
    eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

     

    Now the User Service registers with Eureka.

    Other services can discover it using the service name instead of hard-coded URLs.


    🔍 What Is Service Discovery?

    Imagine you have many microservices—like Payment Service, User Service, and Notification Service—all running on different servers or containers. These services need to talk to each other, but their locations (IP and port) can change anytime due to scaling or redeployment.

    Service discovery solves this by:

    • Automatically registering services when they start.

    • Allowing other services to look up where they are.


    🧩 Key Components

    ComponentRole
    Service ProviderThe microservice that offers functionality (e.g., Payment Service).
    Service RegistryA database that keeps track of all active service instances.
    Service ConsumerThe microservice that wants to use another service (e.g., User Service calling Payment Service).


    🧭 Two Main Approaches

    1. Client-Side Discovery

      • The client (consumer) queries the service registry directly.

      • It chooses which instance to call and handles load balancing.

      • Example: Netflix Eureka with Ribbon.

    2. Server-Side Discovery

      • The client sends a request to a load balancer.

      • The load balancer queries the registry and forwards the request.

      • Example: Kubernetes with its built-in service discovery.



    💡 Why It Matters

    • Makes your system dynamic and scalable.

    • Reduces manual configuration.

    • Helps with load balancing and fault tolerance.



    What is a Circuit Breaker in microservices? How do you implement it in Spring Boot?


    ⚡ What Is a Circuit Breaker?

    Just like an electrical circuit breaker stops the flow of electricity when there's a fault, a software circuit breaker stops requests to a failing service to prevent further damage.


    🧠 Why Is It Used?

    Microservices often depend on each other. If one service starts failing (e.g., it's slow or down), and others keep calling it, it can:

    • Overload the failing service even more

    • Cause delays or failures in the calling services

    • Lead to a domino effect of system-wide outages

    The circuit breaker pattern helps by:

    • Detecting repeated failures

    • “Tripping” the circuit to stop further calls temporarily

    • Allowing recovery time before retrying





    🛠️ Where Is It Used?

    You’ll find circuit breakers in:

    • Cloud-native apps using microservices

    • API gateways to protect downstream services

    • Distributed systems where services rely on external APIs or databases

    Popular tools and libraries:

    • Hystrix (by Netflix, now retired but still influential)

    • Resilience4j (modern Java library)

    • Istio (for Kubernetes service mesh)

    • Spring Cloud Circuit Breaker



    ✅ Circuit Breaker Concept

    • Problem:

      • Service A calls Service B.

      • Service B is down or slow.

      • Continuous calls overload Service B and may cascade failure to Service A → system instability.

    • Solution: Circuit Breaker Pattern

      • Acts like an electrical circuit breaker.

      • Monitors failures of service calls.

      • If failures exceed a threshold → breaker “trips” → stops further calls to the failing service.

      • After a timeout, it allows a few test calls to check if Service B is healthy.

      • If healthy → breaker closes → normal flow resumes.



    🔹 Benefits

    1. Prevents cascading failures.

    2. Improves fault tolerance and system stability.

    3. Works well with API gateways and microservices.




    @Service
    public class PaymentService {

    @CircuitBreaker(name = "paymentService", fallbackMethod = "fallback")
    public String processPayment() {
    // Simulate a call to another microservice
    if(Math.random() > 0.5) {
    throw new RuntimeException("Service B down");
    }
    return "Payment successful";
    }

    // Fallback method
    public String fallback(Throwable t) {
    return "Payment service unavailable, please try later";
    }
    }


    Configure breaker in application.yml


    resilience4j.circuitbreaker:
    instances:
    paymentService:
    registerHealthIndicator: true
    slidingWindowSize: 5
    failureRateThreshold: 50
    waitDurationInOpenState: 10000

    • slidingWindowSize: number of calls monitored.

    • failureRateThreshold: % of failures to trip the breaker.

    • waitDurationInOpenState: time before trying again.


    Imagine slidingWindowSize = 10. That means:

    • The circuit breaker tracks the last 10 calls.

    • If, say, 6 out of those 10 calls failed, the failure rate is 60%.

    • If this rate exceeds the configured failureRateThreshold, the circuit breaker opens (i.e., blocks further calls temporarily).



    🛡️ What Is a Fallback Method?

    A fallback method is a backup plan. If your main method fails (due to timeout, exception, or circuit breaker open), the fallback method is called instead. It helps your app stay responsive and avoid crashing.

    Example use case: If your Payment Service is down, the fallback method might return a message like “Payment service is currently unavailable. Please try again later.”



    🔗 Resilience4j Integration in Spring Boot

    Resilience4j is a lightweight fault-tolerance library that supports:

    • Circuit Breaker

    • Retry

    • Rate Limiter

    • Bulkhead

    • TimeLimiter


                   🧩 Combined Fault-Tolerance Strategy



    1. CircuitBreaker 🛑

    Protects your system from calling a failing service repeatedly.

    • What it does: Monitors the success/failure rate of calls.

    • If failures exceed a threshold, it opens the circuit and blocks further calls temporarily.

    • Fallbacks can be triggered when the circuit is open.

    Think of it like a fuse box: if too many failures happen, it “trips” to prevent further damage.


    2. Retry 🔁

    Automatically retries failed calls before giving up.

    • What it does: If a call fails due to a transient issue (e.g., network glitch), it retries a few times.

    • You can configure max attempts, wait duration, and exceptions to retry.

    Useful when failures are short-lived and likely to succeed on a second or third try.


    3. TimeLimiter ⏱️

    Sets a maximum time limit for a call to complete.

    • What it does: If a service takes too long (e.g., stuck or slow), it times out the call.

    • Prevents your thread from hanging indefinitely.

    • Often used with CompletableFuture or asynchronous calls.

    Helps maintain responsiveness and avoid thread starvation.


    🚀 JWT Authentication Flow in Spring Boot

    1. User Logs In

    • The user sends a POST request with credentials (username/password).

    • Spring Security authenticates the user using a custom UserDetailsService.

    2. Generate JWT

    • If authentication succeeds, a JWT is generated and returned to the client.

    • The token contains user details and expiration time.

    3. Client Stores JWT

    • The client (e.g., browser or mobile app) stores the token (usually in localStorage or sessionStorage).

    • 4. Client Sends JWT with Requests

      • For protected endpoints, the client sends the JWT in the Authorization header:

    5. Validate JWT

    • A filter intercepts incoming requests.

    • The token is extracted, validated (signature, expiration), and user details are loaded.

    • If valid, Spring Security sets the authentication context.


    6. Access Granted

    • The request proceeds to the controller.

    • If the token is invalid or expired, a 401 Unauthorized response is returned.


    ✅ JWT Authentication Flow in Spring Boot

    1. Login / Authentication

      • User sends username and password to the authentication endpoint (/login).

      • Spring Security authenticates the credentials.

    2. JWT Creation

      • If valid, the server creates a JWT (JSON Web Token).

      • Token contains:

        • Username / user ID

        • Roles / authorities

        • Expiration time

      • Token is signed using a secret key.

    3. Token Delivery

      • JWT is sent back to the client (usually in the Authorization header).

      • Client stores it in localStorage or sessionStorage (browser) or in mobile app storage.

    4. Request Authorization

      • For each subsequent request, client sends the JWT in the Authorization: Bearer <token> header.

      • Server validates the JWT:

        • Signature

        • Expiration

        • Roles / permissions

    5. Access Granted / Denied

      • If valid → request proceeds.

      • If invalid / expired → server returns 401 Unauthorized.

     


    🔑 Key Points for Interview

    • JWT is stateless → no server session storage.

    • Includes claims (username, roles, expiration).

    • Sent in Authorization header for each request.

    • Server validates signature and expiration.

    • Common in REST APIs and microservices for scalability.


    What are different caching strategies in Spring Boot? Compare Redis and Caffeine cache."

    ✅ Caching Strategies in Spring Boot

    1. In-Memory Caching

      • Stored in the heap memory of the application.

      • Examples: Caffeine, Guava Cache

      • Fast access but not shared across multiple service instances.

    2. Distributed Caching

      • Shared across multiple service instances.

      • Examples: Redis, Hazelcast, Memcached

      • Good for scaling horizontally in microservices.

    3. Cache-Aside / Lazy Loading

      • Application checks cache first.

      • If missing, load from DB, then populate cache.

      • Example: @Cacheable in Spring Boot.



    Feature Redis Caffeine
    Type Distributed cache In-memory cache
    Scaling Horizontal (multiple nodes) Vertical only (single app heap)
    Eviction Policy LRU (Least Recently Used) Advanced: size, time-based, LRU
    Speed Slightly slower (network access) Very fast (in-process memory)
    Persistence Optional (can persist data) Not persistent
    Use Case Multi-instance microservices Single-instance high-speed cache


    When we say persistent data, we mean:

    • Data that is stored permanently (or long-term) in a database, file system, or disk.

    • It survives application crashes, server reboots, or power failures.

    🧠 Example:

    • If you store user info in RAM, it’s not persistent—it’s gone when the app stops.

    • If you store it in a database or Redis with persistence enabled, it stays saved.

    🔁 In Caching:

    • Persistent cache (like Redis with AOF or RDB) saves data to disk.

    • Non-persistent cache (like Caffeine) keeps data only in memory—fast but temporary.




    What are some common challenges when integrating Kafka with Spring Boot microservices and how do you handle them?"




    ⚙️ 1. Serialization and Deserialization Issues

    Kafka needs to know how to convert your Java objects to bytes and back.

    Challenges:

    • Mismatched serializers between producer and consumer

    • Complex object structures causing deserialization failures

    Solutions:

    • Use KafkaTemplate with proper Serializer and Deserializer classes

    • Prefer formats like JSON or Avro with schema registry for consistency

    • Configure ErrorHandlingDeserializer to gracefully handle bad messages


    🔄 . Message Ordering and Partitioning

    Kafka guarantees order only within a partition.

    Challenges:

    • Messages processed out of order across partitions

    • Uneven load distribution

    Solutions:

    • Use a consistent partition key (e.g., user ID) to maintain order

    • Tune the number of partitions based on throughput and parallelism needs.


    🧯 . Error Handling and Retries

    Spring Boot apps need robust handling for failed message processing.

    Challenges:

    • Infinite retry loops

    • Silent message drops

    Solutions:

    • Configure retry policies and backoff strategies using RetryTemplate

    • Use Dead Letter Topics (DLT) to capture failed messages for later inspection

    • Monitor consumer lag and error rates with tools like Prometheus + Grafana


    🛡️ . Security and Authentication

    Kafka clusters often require authentication and encryption.

    Challenges:

    • Misconfigured SSL/SASL settings

    • Credential management across environments

    Solutions:

    • Use Spring Boot’s support for Kafka security configs

    • Externalize credentials using Spring Cloud Config or Vault

    • Enable TLS encryption and SASL authentication for secure communication.

    📊 Monitoring and Observability

    Kafka is fast—but debugging it can be a nightmare without visibility.

    Challenges:

    • Hard to trace message flow across microservices

    • Limited built-in metrics

    Solutions:

    • Integrate Micrometer with Spring Boot for Kafka metrics

    • Use distributed tracing tools like Zipkin or OpenTelemetry

    • Enable Kafka’s JMX metrics and monitor with Grafana or Datadog

    Scaling Issues

    • Problem: High volume → uneven partition load.

    • Solution:

      • Proper partition strategy for topics

      • Scale consumers horizontally across instances.




    Summary :-




    ✅ Common Challenges with Kafka in Spring Boot & Solutions

    1. Message Lag / Backpressure

      • Problem: Consumers can’t keep up with producers → messages pile up.

      • Solution:

        • Tune consumer concurrency (@KafkaListener(concurrency = "3"))

        • Increase consumer throughput

        • Monitor lag with Kafka monitoring tools (Kafka Manager, Prometheus + Grafana)

    2. Delivery Failures / Retry Issues

      • Problem: Message fails due to temporary network/db issue.

      • Solution:

        • Use retry mechanism in Spring Kafka (RetryTemplate)

        • Configure dead-letter topic (DLT) for messages that fail after max retries.

    3. Security / Encryption

      • Problem: Kafka clusters need secure communication (SSL/TLS, SASL).

      • Solution:

        • Enable SSL/TLS encryption between producer, broker, and consumer

        • Configure authentication & authorization properly.

    4. Monitoring & Observability

      • Problem: Hard to trace failures or message delays in production.

      • Solution:

        • Use centralized monitoring tools like Prometheus + Grafana or Kafka Manager

        • Log message metadata for traceability.

    5. Scaling Issues

      • Problem: High volume → uneven partition load.

      • Solution:

        • Proper partition strategy for topics

        • Scale consumers horizontally across instances.




    🔹 Designing a Spring Boot Microservice with Redis + Kafka

    1️⃣ Use Case Example

    • Imagine a loan microservice in mPokket:

      • Users request a loan application.

      • The service needs fast access to user data and asynchronous processing for notifications or risk scoring.



    2️⃣ Components

    1. Spring Boot Service → handles loan requests.

    2. Redis Cache → stores frequently accessed data (e.g., user profile, loan limits) to reduce DB calls.

    3. Kafka Producer → sends events (e.g., LoanCreated) to other services asynchronously.

    4. Kafka Consumer → listens to events from other services (e.g., CreditScoreUpdated) to update data.



    3️⃣ Flow

    1. Incoming Request: User submits loan request via REST API.

    2. Cache Lookup:

      • Check Redis for user data.

      • If present → use cached data.

      • If not → fetch from DB, then store in Redis (@Cacheable).

    3. Business Processing: Validate request, calculate eligibility, store in DB.

    4. Event Publishing:

      • Use Kafka Producer to publish LoanCreated event.

      • Other microservices (notification, credit scoring) consume it asynchronously.

    5. Response: Return loan approval/rejection to the user.


    4️⃣ Benefits

    FeatureBenefit
    Redis CachingReduces DB load, improves response time
    Kafka MessagingAsynchronous processing, decouples services
    Microservices DesignIndependent, scalable, maintainable
    Event-Driven FlowResilient to failures; other services can react without blocking



    ⚙️ What Is Asynchronous Processing?

    Asynchronous processing means your method runs in the background, allowing the main thread to continue without waiting. This is useful when:

    • You don’t need an immediate result

    • The task takes time (e.g., sending emails, calling external APIs, processing files)


    ✅ Asynchronous Processing in Spring Boot

    1. Purpose

      • Allows a method to run in a separate thread without blocking the caller.

      • Useful for long-running tasks like sending emails, processing files, or calling external services.

    2. Implementation with @Async



     use @Async in Spring Boot to run non-blocking tasks like email notifications or external service calls. It improves performance and user experience by freeing up the main thread. I enable it with @EnableAsync and annotate methods I want to run in the background.”



    🚀 How to Implement @Async in Spring Boot

    ✅ 1. Enable Async Support

    Add this annotation to your configuration class:


    @EnableAsync
    @Configuration
    public class AsyncConfig {
    }



    ✅ 2. Use @Async on Methods

    @Service
    public class NotificationService {

    @Async
    public void sendEmail(String to, String message) {
    // Simulate delay
    Thread.sleep(3000);
    System.out.println("Email sent to " + to);
    }
    }


    ✅ 3. Call the Async Method



    @Autowired
    private NotificationService notificationService;

    public void triggerEmail() {
    notificationService.sendEmail("user@example.com", "Welcome!");
    System.out.println("Main thread continues...");
    }



    The sendEmail() method runs in a separate thread, so the main thread doesn’t wait for it to finish.




    How would you implement an asynchronous Kafka producer in Spring Boot to ensure non-blocking message publishing?



    🔹 Asynchronous Kafka Producer in Spring Boot

    1️⃣ Concept

    • Normally, Kafka producer.send() is already asynchronous, but combining it with @Async in Spring Boot ensures that the calling thread is not blocked at all.

    • Useful for high-throughput systems, like fintech apps, where you don’t want API response to wait for Kafka delivery.


    3️⃣ Key Points for Interview

    1. @Async + KafkaTemplate.send() → completely non-blocking.

    2. Kafka is event-driven, so combining async helps high throughput.

    3. Always handle success/failure callbacks to log or retry failed messages.

    4. Useful in fintech scenarios where user requests should not wait for background processing.



    How do you handle message failures in Kafka consumers using retries and Dead-Letter Queues in Spring Boot?

    🔹 Handling Kafka Consumer Failures with Retries & DLQ

    1️⃣ Problem

    • Sometimes a consumer fails to process a message (e.g., invalid data, DB down).

    • If unhandled, the message may block the consumer or cause data loss.


    2️⃣ Solution

    1. Retries

      • Automatically retry consuming the message a fixed number of times.

      • Spring Kafka provides SeekToCurrentErrorHandler or DefaultErrorHandler in newer versions.

      • Example: retry 3 times before moving the message to DLQ.

    2. Dead-Letter Queue (DLQ)

      • If retries fail, the message is sent to a special topic (DLQ) for later inspection.

      • Ensures no data is lost and failures can be analyzed and fixed.



    A Dead Letter Queue (DLQ) is a special queue used to handle messages that cannot be processed successfully by a consumer. Instead of losing or endlessly retrying failed messages, DLQs give you a safe place to store them for later inspection or reprocessing.


    I use DLQs to handle failed messages gracefully. Instead of retrying forever or losing data, I route them to a separate queue. This helps with debugging, ensures reliability, and allows safe reprocessing later.


    3. DLQ Handling

    • The failed message is sent to the Dead Letter Queue.

    • The DLQ is a separate queue/topic where you can inspect or reprocess failed messages.


    Processing DLQ Messages

    • DLQ messages are not automatically retried unless you explicitly set up a consumer for the DLQ.

    • You can:

      • Manually inspect and fix the data.

      • Reprocess messages by moving them back to the main queue.

      • Set up a separate consumer to handle DLQ messages with custom logic.


    🚀 How to Optimize Spring Boot + PostgreSQL + JPA


    ✅ 1. Use Pagination for Large Queries

    Don’t load thousands of records at once.


    ✅ 2. Avoid N+1 Query Problem

    Happens when JPA loads related entities one by one.

    • Use @EntityGraph or JOIN FETCH to load related data in a single query.

    ✅ 3. Use Indexes in PostgreSQL

    Indexes make searches faster.

    • Add indexes to frequently queried columns like email, created_at, etc.

    • Example: @Column + @Index in JPA or native SQL indexing.


    ✅ 4. Batch Inserts and Updates

    JPA by default sends one query per record. That’s slow.

    spring.jpa.properties.hibernate.jdbc.batch_size=30
    spring.jpa.properties.hibernate.order_inserts=true
    spring.jpa.properties.hibernate.order_updates=true


    • Use saveAll() instead of save() in loops.



    ✅ 7. Caching Frequently Used Data

    Avoid hitting the database for the same data repeatedly.

    ✅ 8. Profile Your Queries

    Use tools like Hibernate Statistics, pg_stat_statements, or Spring Actuator to find slow queries.

    • Enable SQL logging:

    spring.jpa.show-sql=true
    spring.jpa.properties.hibernate.format_sql=true


    ✅ 6. Connection Pooling

    Reusing DB connections improves performance.

    • Use HikariCP (default in Spring Boot).

    • Tune settings:

    spring.datasource.hikari.maximum-pool-size=20
    spring.datasource.hikari.minimum-idle=5


    🧩 1. Batch Inserts and Updates

    🔍 What it means:

    Instead of sending one SQL query for each record, you send multiple records together in a single batch. This reduces the number of round-trips to the database and improves performance.

    🧠 Why it's important:

    Imagine inserting 1000 users. Without batching, JPA sends 1000 separate queries. With batching, it sends maybe 10 queries with 100 users each.

    ✅ How to enable it in Spring Boot:

    Add these properties in application.properties:

    spring.jpa.properties.hibernate.jdbc.batch_size=30
    spring.jpa.properties.hibernate.order_inserts=true
    spring.jpa.properties.hibernate.order_updates=true




    🧩 3. Connection Pooling

    🔍 What it means:

    Instead of opening and closing a new database connection every time, a pool of connections is kept ready. When your app needs a connection, it borrows one from the pool.

    🧠 Why it's important:

    Opening a DB connection is slow. Pooling makes it fast and efficient.



    🧩 2. Use DTOs Instead of Entities for Read-Only Data

    🔍 What it means:

    DTO = Data Transfer Object. It’s a simple class with only the fields you need. You use it when you don’t need the full entity or don’t want to load relationships.

    🧠 Why it's important:

    Entities can be heavy—they may load related tables, lazy proxies, etc. DTOs are lightweight and faster for read-only operations.


    explain scneario when you implement multhreading and address concurrency issue in your project ?



    🛠️ Scenario: Building a File Upload Service

    I was working on a backend service that allowed users to upload large files. The system had to:

    • Accept multiple uploads at the same time

    • Process each file (e.g., virus scan, metadata extraction)

    • Save results to a shared database

    ⚠️ Concurrency Issue

    The problem came when multiple threads tried to write to the database at the same time. Sometimes:

    • Data got overwritten

    • Incomplete records were saved

    • Race conditions caused weird bugs

    ✅ How I Solved It

    Here’s how I addressed the concurrency issues:

    1. Synchronized Access

    I used synchronization (e.g., synchronized blocks in Java or locks in Python) to make sure only one thread could write to the database at a time.

    2. Thread-safe Queues

    I introduced a thread-safe queue where threads placed processed data, and a single thread handled database writes.

    3. Connection Pooling

    Used a connection pool to manage database connections efficiently without collisions.

    4. Atomic Operations

    For counters and flags, I used atomic variables to avoid race conditions.



    Example 2-



    ✅ Scenario where I implemented Multithreading

    Suppose I was working on a payment processing system (UPI/Card settlement) where multiple transactions come at the same time.

    • If we process them sequentially, response time would be very high.

    • So, we implemented multithreading using ExecutorService or CompletableFuture to handle parallel requests (for example, validating transactions, fraud checks, updating balances).

    👉 Example:

    • Thread 1: Validate card details

    • Thread 2: Fraud detection

    • Thread 3: Balance deduction and DB update

    This way, multiple tasks run concurrently, reducing processing time.

    ✅ Concurrency Issue faced

    While processing transactions in multiple threads, we faced race conditions:

    • Two threads were trying to update the same user account balance at the same time.

    • Example:

      • User A has ₹1000.

      • Transaction 1 deducts ₹200.

      • Transaction 2 deducts ₹500.

      • Without concurrency handling → both threads read balance = 1000, and both try to update → final balance may become 500 instead of 300.

    This is called a concurrency issue.


    ✅ How I solved it

    We used multiple techniques depending on the case:

    1. Synchronized blocks / Locks

      • In Java, used synchronized or ReentrantLock to ensure only one thread updates the account at a time.

    2. Atomic variables

      • For counters/logs → used AtomicInteger instead of normal int to avoid race conditions.

    3. Optimistic Locking (Database level)

      • Used Hibernate @Version annotation (Optimistic Locking) so that if two threads try to update the same row, one will fail and retry.

    4. Thread-safe collections

      • Instead of HashMap/ArrayList, used ConcurrentHashMap and CopyOnWriteArrayList.


    ✅ Final Answer (Interview Style)

    "In my payments project, we had a scenario where multiple threads were processing transactions in parallel for performance improvement. The issue we faced was concurrency while updating account balances — sometimes two threads tried updating the same account simultaneously, leading to incorrect balances. To handle this, we used synchronization and database-level optimistic locking with Hibernate’s @Version to ensure data consistency. For counters and logs, we used AtomicInteger, and for caching, we used thread-safe collections like ConcurrentHashMap. This ensured both high performance and data integrity."


                                  🏦 Payment Project Example with Design Patterns


    1. Singleton Pattern (One instance shared everywhere)

    👉 When to use: For shared resources (connection pools, cache clients, logger).


    Example in Payment Project:

    • Only one Redis Cache Client or Database Connection Pool should exist.

    • If multiple are created → memory wastage, inconsistent state.


    📝 Singleton ensures only one object exists and is reused everywhere.


    2. Factory Pattern (Decide which object to create)

    👉 When to use: When we want to create objects dynamically without many if else.

    Example in Payment Project:

    • A user can pay with UPI, Card, Net Banking.

    • Instead of hardcoding logic, we use a PaymentFactory to return the right payment method.


    3. Strategy Pattern (Multiple algorithms, choose one at runtime)

    👉 When to use: When you have multiple algorithms for a task, and you want to switch at runtime.

    Example in Payment Project:

    • Different discount/coupon strategies.

      • New User → 10% Off

      • Festival Offer → 20% Off

      • No Discount → 0%

    4. Observer Pattern (One-to-many notification)

    👉 When to use: When one event should trigger multiple actions automatically.

    Example in Payment Project:

    • When a transaction is successful:

      • Send Email

      • Send SMS

      • Push to Kafka for audit

    5. Builder Pattern (Step-by-step object creation)

    👉 When to use: When object has too many parameters → instead of messy constructor, build it step by step.

    Example in Payment Project:

    • When creating a Payment Request object with many optional fields:



    PaymentRequest request = new PaymentRequest.Builder()
            .userId("123")
            .paymentType("CARD")
            .amount(5000)
            .coupon("FEST20")
            .build();

    📝 Builder makes complex object creation clean, readable, and flexible.

    🎯 Easy Summary (Interview One-liners)

    • SingletonI used it for DB connection pool / Redis client → ensures single instance across app.

    • FactoryI used it in Payment system to create different payment methods dynamically.

    • StrategyI applied it for different discount/coupon rules at checkout, selected at runtime.

    • ObserverI used it for notification system → one payment triggers Email, SMS, Kafka event.

    • BuilderI used it for creating complex PaymentRequest objects step by step with optional fields.




    can you provide use case where you use static and non static variable in your project ?


    💳 Use Case: Transaction Processing System

    Imagine you're building a backend service that handles UPI transactions. You need to track global metrics (like total transactions) and also manage individual transaction details.


    🔁 Static Variable: Global Transaction Counter

    Purpose: Track how many transactions have been processed across the entire system.

    public class TransactionManager {
        // Static variable shared across all instances
        public static int totalTransactions = 0;
    
        public static void incrementTransactionCount() {
            totalTransactions++;
        }
    
        public static int getTotalTransactions() {
            return totalTransactions;
        }
    }
  • Why static? Because this counter is shared globally and doesn’t depend on any specific transaction object.

  • 📊 Useful for analytics, monitoring, or load balancing.



  • 🔒 Static Variable

    • Belongs to the class, not to any specific object.

    • Shared across all instances of that class.

    • Only one copy exists in memory.


    📦 Non-Static Variable

    • Belongs to an individual object.

    • Each object has its own copy.

    • Used to store data that’s unique to each instance.

    📦 Non-Static Variable: Individual Transaction Details

    Purpose: Store data specific to each transaction, like amount, sender, receiver, and timestamp.


  • Why non-static? Because each transaction has its own unique data.

  • 🧾 Used for logging, auditing, and user notifications.


  • ===========================


    What is a Collection? 


    📌 What is a Collection in Java?

    A Collection in Java is a framework (a set of classes & interfaces) that provides an architecture to store, manipulate, and retrieve groups of objects efficiently.

    • Located in java.util package.

    • It does not deal with primitive types (int, double, etc.), only objects.
      👉 But autoboxing helps (e.g., intInteger).

    📌 Why Collection Framework?

    Before Java 2 (JDK 1.2), developers used arrays, Vectors, Hashtables, but they had problems:

    • Fixed size (array).

    • Lack of built-in sorting, searching, iteration.

    • Different classes with different methods (no uniformity).

    Collection Framework solved this by providing:

    • Interfaces → Set, List, Queue, Deque.

    • Implementations → ArrayList, HashSet, LinkedList, PriorityQueue, etc.

    • Algorithms → Sorting, searching (Collections.sort(), Collections.binarySearch()).


                    Iterable (root interface)
                         |
                     Collection
            ------------------------------
            |             |             |
           List          Set          Queue
            |             |             |
       ArrayList,    HashSet,       LinkedList,
       LinkedList,   TreeSet,       PriorityQueue
       Vector        LinkedHashSet




    What is the difference between ArrayList and LinkedList ?

    ArrayList = Fast access, slow insert/delete


    LinkedList = Slow access, fast insert/delete


  • ArrayList is based on dynamic array → so it gives fast random access (O(1)) but insertion/deletion in middle is costly (O(n)) because elements need shifting.

  • LinkedList is based on a doubly linked list → so it gives fast insertion/deletion (O(1)) if we already have node reference, but random access is slow (O(n)) since it must traverse nodes.



  • ArrayList vs Vector in Java

    1. Synchronization

    • ArrayList → ❌ Not synchronized → faster (but not thread-safe).

    • Vector → ✅ Synchronized → thread-safe, but slower.


    What is a Functional Interface?

    A Functional Interface is an interface that contains exactly one abstract method.

    • It can have multiple default methods and static methods (these are not abstract).

    • Functional Interfaces are the foundation of lambda expressions and method references in Java 8+.


    One-liner to remember:
    Functional Interface = 1 abstract method + multiple default/static methods allowed.



    ✅ What if we have more than one abstract method?

    • If you add more than one abstract method, the compiler will throw an error saying:



    That means your interface is no longer a Functional Interface.

    However, it’s still a normal interface (just can’t be used with lambdas).

    ✅ Easy Interview Answer

    A Functional Interface can only have one abstract method because it’s designed to work with lambdas, which can implement only one method.
    If you add more abstract methods, it’s just a normal interface, not a functional one, and can’t be used with lambdas.





    What is Dependency Injection?

    👉 Dependency Injection means giving the objects (dependencies) that a class needs from outside, instead of the class creating them itself.

    • Without DI → Class creates its own dependencies (tight coupling).

    • With DI → Dependencies are "injected" (supplied) by a container like Spring (loose coupling).



    When does Spring create beans of the dependencies?

    By default, in Spring, beans are created at application startup (container initialization).

    • If the bean scope is singleton (default) →
      Spring creates the bean eagerly at startup, when the ApplicationContext is initialized.

    • If the bean scope is prototype
      Spring creates a new bean only when you request it (getBean() or when injected).



    @Component
    class Engine {
        Engine() {
            System.out.println("Engine Bean Created!");
        }
    }

    Lazy Initialization

    If you don’t want Spring to create beans at startup, you can use @Lazy.


    • Short Interview Answer

      Spring creates beans:

      • By default, eagerly at container startup for singleton scope.

      • On demand for prototype scope.

      • You can use @Lazy to delay creation until first use.



     What if an interface has two implementation classes and we’re annotating Interface with @Autowired? 


    Short Interview Answer

    If an interface has multiple implementations and we use @Autowired on the interface, Spring cannot decide which bean to inject → it throws NoUniqueBeanDefinitionException.

    We resolve it using:

    • @Primary → mark one bean as default.

    • @Qualifier → explicitly choose which bean to inject.

    • Or match bean name with the variable name.


    Load Balancer

    • What it does → Distributes incoming traffic across multiple servers or service instances.

    • GoalScalability & high availability (no single server overloaded).

    • Works at → Network level (Layer 4) or Application level (Layer 7).

    • Examples → Nginx, HAProxy, AWS ELB, Azure Load Balancer.


    API Gateway

    • What it does → Acts as a single entry point for clients to access microservices.

    • Responsibilities:

      • Routing requests to correct microservice

      • Authentication & Authorization

      • Rate limiting / throttling

      • Logging / monitoring

      • Protocol translation (HTTP ↔ WebSocket, etc.)

    • GoalManage, secure, and route APIs.

    • Examples → Spring Cloud Gateway, Netflix Zuul, Kong, AWS API Gateway.

     
     





    What have you used to communicate with DB?”


    Possible Answers

    You can tailor depending on your project:

    1. JDBC (Java Database Connectivity).

    2. Hibernate (ORM)

    3. JPA (Java Persistence API) with Hibernate implementation

    4. Spring Data JPA




    Interview Answer (Ready to Say)

    “In my projects, I’ve mostly used Spring Data JPA with Hibernate.

    • JDBC is low-level, requires writing queries and managing connections manually. Developer writes SQL manually ,

      Manual  Transaction handling with try/catch

    • Hibernate is an ORM that maps Java objects to DB tables and auto-generates SQL. and manage transaction not using try catch.

    • JPA is just a specification; Hibernate is one of its implementations.

    • Spring Data JPA is built on JPA and Hibernate; it simplifies things further by providing repository interfaces for CRUD operations, so I can focus on business logic instead of boilerplate code.”


     How do we define query in Spring Data Jpa? 

    Ways to Define Queries in Spring Data JPA

    1. Derived Query Methods (Method Name Convention)

    Spring Data JPA can create queries just from method names in the repository interface.

    public interface UserRepository extends JpaRepository<User, Long> {

        // Derived queries

        List<User> findByName(String name);

        User findByEmail(String email);

        List<User> findByAgeGreaterThan(int age);

    }


    ✅ Why use @Query in Spring Data JPA?


    “In Spring Data JPA, most queries can be created automatically from method names like findByEmail or findByAgeGreaterThan. But sometimes we need custom or complex queries which cannot be handled by method names. In those cases, we use the @Query annotation to define the query manually. So basically, simple queries = method names, complex/custom queries = @Query.”



    how to use the @Query annotation in Spring Data JPA to execute both JPQL and native SQL queries.


    🔍 What is @Query?

    @Query lets you write custom queries directly in your repository interface—no need to create separate query files.


    "In Spring Data JPA, the @Query annotation allows us to define custom queries directly in the repository interface. We can use it for both JPQL and native SQL queries.

    public interface UserRepository extends JpaRepository<User, Long> {
    
        @Query("SELECT u FROM User u WHERE u.email = ?1")
        User findByEmail(String email);
    }

    User is the entity class
    
    u.email is the field in that class
    
    ?1 means the first method parameter

    Kafka + Elasticsearch Together

    Many companies use Kafka + Elasticsearch together in real-time systems:

    1. Kafka collects and transports real-time data (e.g., user clicks, logs, transactions).

    2. Elasticsearch stores and indexes this data for fast searching and analytics.

    👉 Example (Interview scenario)

    • In a payments/UPI system:

      • All transactions events go into Kafka.

      • A consumer reads from Kafka and pushes data into Elasticsearch.

      • Elasticsearch allows support teams to search transactions quickly (by ID, user, status) and generate dashboards.


    One-liner difference you can give in interview:

    • Kafka → “For moving/streaming data in real-time.”

    • Elasticsearch → “For searching and analyzing data quickly.”

    Example: DevOps teams use Elasticsearch to find errors in millions of logs quickly

    Supports real-time analytics on data streams (e.g., payment transactions, user activity).


    ✅ 1. Why DB Optimization?

    • Databases become a bottleneck when:

      • High traffic

      • Complex queries

      • Large datasets

    • Optimizing DB improves performance, scalability, and cost.

    ✅ 2. Common DB Optimization Areas (Interview Friendly)

    🔹 (A) Query Optimization

    1. Use Indexes

      • Helps speed up SELECT queries.

      • Example: Instead of scanning entire table → index finds rows faster.

      • Types: B-Tree Index, Hash Index, Composite Index.

      • 👉 Interview point: "I create indexes on columns used in WHERE, JOIN, ORDER BY."

    2. **Avoid SELECT ***

      • Only fetch required columns.

      • Bad: SELECT * FROM users;

      • Good: SELECT id, name FROM users;

    3. Joins Optimization

      • Use proper join order.

      • Avoid joining too many tables unnecessarily.

    4. Limit Data

      • Use LIMIT for pagination instead of fetching everything.


    🔹 (B) Schema Design

    1. Normalization

      • Removes redundancy (1NF, 2NF, 3NF).

      • Example: Store user address in a separate table.

    2. Denormalization

      • For read-heavy systems, store some duplicate data to avoid joins.

      • Example: Store user’s full name in loan table.

    3. Partitioning / Sharding

      • Split large tables into smaller ones (e.g., by region or time).


        Q: What is Indexing?
        👉 "Index is like a book index. Instead of scanning full table, DB uses B+ tree index to find rows faster. It improves read performance but slows down writes. We create indexes on frequently searched/joined columns."

        Q: Difference between Join and Subquery?
        👉 "Both combine data, but a join merges tables horizontally, whereas subquery is a query inside another. Subqueries are sometimes slower; joins are often optimized better."

        Q: When to use Window Functions?
        👉 "When I need row-wise analytics without grouping rows. For example, finding top-N salaries per department or running totals."


      • C. Window Functions

        Used for ranking, running totals, partitions.



    B. Subquery

    • A query inside another query.

    👉 Example: Find employees earning more than average salary:


    SELECT name, salary

    FROM employees

    WHERE salary > (SELECT AVG(salary) FROM employees);



    1. What is an Index?

    • An index is like a book index. Instead of scanning the whole table, DB uses index to jump directly to the data.

    • Implemented internally using B+ Trees (most common) or Hash Indexes.

    2. How it Works

    • Without index: DB scans all rows (full table scan).

    • With index: DB goes through the index tree, finds row pointer quickly.



    🔹 Types of Indexes

    1. Primary Index → Automatically on Primary Key.

    2. Unique Index → Ensures uniqueness.

    3. Composite Index → On multiple columns (first_name, last_name).

    4. Clustered Index → Actual table rows stored in sorted order (only 1 per table).

    5. Non-Clustered Index → Separate structure that points to table rows.




    ✅ Q2: SQL Questions

    A. Joins

    1. INNER JOIN → Only matching rows.

    2. LEFT JOIN → All from left + matching right.

    3. RIGHT JOIN → All from right + matching left.

    4. FULL OUTER JOIN → All rows from both, unmatched = NULL.


     1. Find the second highest salary (classic interview Q)

    SELECT MAX(salary) 
    FROM employees
    WHERE salary < (SELECT MAX(salary) FROM employees);


    🔹 2. Find employees who earn more than their manager

    SELECT e.name AS employee, m.name AS manager
    FROM employees e
    JOIN employees m ON e.manager_id = m.id
    WHERE e.salary > m.salary;



    7. Find duplicate emails

    SELECT email, COUNT(*) 
    FROM users
    GROUP BY email
    HAVING COUNT(*) > 1;

     9. Retrieve highest salary in each department



    SELECT department_id, MAX(salary) AS max_salary
    FROM employees
    GROUP BY department_id;


    🔹 Q1: Find users who made more than 3 transactions in a day


    SELECT user_id, DATE(txn_date) AS txn_day, COUNT(*) AS txn_count
    FROM transactions
    GROUP BY user_id, DATE(txn_date)
    HAVING COUNT(*) > 3;





    🔹 Q2: Get users with failed payments but no successful payments

    SELECT DISTINCT user_id
    FROM transactions
    WHERE status = 'FAILED'
      AND user_id NOT IN (
          SELECT DISTINCT user_id
          FROM transactions
          WHERE status = 'SUCCESS'
      );


    🔹 Q3: Find the average transaction amount per user per month.



    SELECT user_id,
           DATE_TRUNC('month', txn_date) AS txn_month,
           AVG(amount) AS avg_amount
    FROM transactions
    WHERE status = 'SUCCESS'
    GROUP BY user_id, DATE_TRUNC('month', txn_date)
    ORDER BY user_id, txn_month;


    🔹 Q5: Find users who never made any transactions



    SELECT u.id, u.name
    FROM users u
    LEFT JOIN transactions t ON u.id = t.user_id
    WHERE t.id IS NULL;


    The SQL GROUP BY Statement

    The GROUP BY statement groups rows that have the same values into summary rows, like "find the number of customers in each country".

    The GROUP BY statement is often used with aggregate functions (COUNT()MAX()MIN()SUM()AVG()) to group the result-set by one or more columns.



    The SQL HAVING Clause

    The HAVING clause was added to SQL because the WHERE keyword cannot be used with aggregate functions.






    DFVDsgbdjngh

    Comments

    Popular posts from this blog

    Two Sum II - Input Array Is Sorted

    Comparable Vs. Comparator in Java

    Increasing Triplet Subsequence