1. Thread Constructor(new Thread()) takes runnable as argument but not callable
  2. Thread instance can use either start() or run() methods to start execution of thread. start() method spawns new thread other than main to complete execution whereas run method uses the main thread to carry out execution.
  3. Managing Thread lifecycle is difficult hence ExecutionService framework was introduced
  4. When you run thread using ExecutionService it takes either instance of Ruunal

Thread Implementation Using Traditional Threads- Old Way

Task.java

public class Task implements Runnable{
    @Override
    public void run() {
        System.out.println("Hello from Task.class, executed by " + currentThread().getName() + " thread");
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new Task());
        thread.run();
    }
}

Output

Hello from Task.class, executed by main thread

Thread Implementation Using ExecutionService – New Way
ExecutorService is an interface in Java that provides a higher-level replacement for using raw threads. It manages a pool of threads to handle asynchronous tasks more efficiently, without the need to manually create or manage individual threads.

Simple Program using Executor Service taking Runnable as Argument

ExecutorService is a framework which allows to create thread. Threads can be created from FixedThreadPool, CachedThreadPool and ScheduledThreadPool. submit() method takes runnable or callable object (Functional Interface Type) as argument.

Main.java

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

public class Main {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);  
        executorService.submit(new Task());
    }
}

Output

Hello from Task.class, executed by pool-1-thread-1 thread

Why we need ExecutorService when we can directly call Thread using run() or start() method?

Problem with using old Thread Methods
Poor Resource Management i.e. It keep on creating new resource for every request. No limit to creating resource. Using Executor framework we can reuse the existing resources and put limit on creating resources.
Not Robust : If we keep on creating new thread we will get StackOverflowException exception consequently our JVM will crash.
Overhead Creation of time : For each request we need to create new resource. To creating new resource is time consuming. i.e. Thread Creating > task. Using Executor framework we can get built in Thread Pool.

  1. Use of Thread Pool reduces response time by avoiding thread creation during request or task processing.
  2. Use of Thread Pool allows you to change your execution policy as you need. you can go from single thread to multiple thread by just replacing ExecutorService implementation.
  3. Thread Pool in Java application increases stability of system by creating a configured number of threads decided based on system load and available resource.
  4. Thread Pool frees application developer from thread management stuff and allows to focus on business logic.

Runnable vs Callable
Using Runnable doesn’t returns any value whereas callable returns value to the main thread

HelloThread1.java

public class TaskRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("Hello from "+ this.getClass().getSimpleName() + ", executed by " + currentThread().getName() + " thread");
    }
}

HelloThread2.java

public class TaskCallable implements Callable {
    @Override
    public String call() throws Exception {
        System.out.println("Hello from "+ this.getClass().getSimpleName() + ", executed by " + currentThread().getName() + " thread");
        return "Hello from Callable";
    }
}

Main.java

public class Main {
    public static void main(String[] args) throws Exception {
        TaskRunnable objTaskRunnable = new TaskRunnable(); //Instance of Runnable
        TaskCallable objTaskCallable = new TaskCallable(); //Instance of Callable
       
        String strText =  objTaskCallable.call();
        System.out.println(strText); //I am a Blocking Operation
        objTaskRunnable.run();
    }
}

Output

Hello from TaskCallable, executed by main thread
Hello from Callable
Hello from TaskRunnable, executed by main thread

Now the same code can be executed using ExecutionService Framework by taking thread from thread pool as below

Main.java

public class Main {
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(3);  // Pool of 3 threads// Submit tasks to the executor
        TaskCallable objTaskCallable = new TaskCallable();
        TaskRunnable objTaskRunnable = new TaskRunnable();

        Future<String> strText = executorService.submit(objTaskCallable);
        System.out.println(strText.get()); //I am a Blocking Operation
        executorService.submit(objTaskRunnable);

        executorService.shutdown();
    }
}

Output

Hello from TaskCallable, executed by pool-1-thread-1 thread
Hello from Callable
Hello from TaskRunnable, executed by pool-1-thread-2 thread
  1. NEW – a newly created thread that has not yet started the execution
  2. RUNNABLE – either running or ready for execution but it’s waiting for resource allocation
  3. BLOCKED – waiting to acquire a monitor lock to enter or re-enter a synchronized block/method
  4. WAITING – waiting for some other thread to perform a particular action without any time limit
  5. TIMED_WAITING – waiting for some other thread to perform a specific action for a specified period
  6. TERMINATED – has completed its execution

NEW Thread (or a Born Thread) is a thread that’s been created but not yet started.
It remains in this state until we start it using the start() method

NewState.java

public class NewState implements Runnable{
    public void run(){
        System.out.println("I am in new State");
    }
}

Main.java

public class Main {
    public static void main(String[] args) throws InterruptedException {
       Thread objThread = new Thread(new NewState());
       System.out.println(objThread.getState());
    }
}

Output

NEW

Runnable When we’ve created a new thread and called the start() method on that, it’s moved from NEW to RUNNABLE state. Threads in this state are either running or ready to run, but
they’re waiting for resource allocation from the system. In a multi-threaded environment, the Thread-Scheduler (which is part of JVM) allocates a fixed amount of time to each thread. So it runs for a particular amount of time, then leaves the control to other RUNNABLE threads.

RunnableState .java

public class RunnableState implements Runnable{
    public void run(){
        System.out.println("I would be in Runnable State");
    }
}

Main.java

public class Main {
    public static void main(String[] args) throws InterruptedException {
       Thread objRThread = new Thread(new RunnableState());
       objRThread.start();
       System.out.println(objRThread.getState());
    }
}

Output

RUNNABLE
I would be in Runnable State

This is the state of a dead thread. It’s in the TERMINATED state when it has either finished execution or was terminated abnormally.
TerminatedState.java

public class TerminatedState implements Runnable{
    public void run(){
        Thread objNewState = new Thread(new NewState());
        objNewState.start();
    }
}

Main.java

public class Main {
    public static void main(String[] args) throws InterruptedException {
       Thread objTState = new Thread(new TerminatedState());
       objTState.start();
       objTState.sleep(1000);
       System.out.println("T1 : "+ objTState.getState());
    }
}

Output

I am in new State
T1 : TERMINATED

A thread is in the BLOCKED state when it’s currently not eligible to run. It enters this state when it is waiting for a monitor lock and is trying to access a section of code that is locked by some other thread.
BlockedState.java

public class BlockedState implements Runnable{
    public void run(){
      blockedResource();
    }

    public static synchronized void blockedResource(){
        while(true){
            //Do Nothing
        }
    }
}

Main.java

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread objB1Thread = new Thread(new BlockedState());
        Thread objB2Thread = new Thread(new BlockedState());

        objB1Thread.start();
        objB2Thread.start();

        Thread.sleep(1000);

        System.out.println(objB1Thread.getState());
        System.out.println(objB2Thread.getState());
        System.exit(0);
    }
}

Output

RUNNABLE
BLOCKED

A thread is in WAITING state when it’s waiting for some other thread to perform a particular action. According to JavaDocs, any thread can enter this state by calling any one of the following
object.wait() (or) thread.join() (or) LockSupport.park()

WaitingState.java

public class WaitingState implements Runnable{
    public void run(){
        Thread objWaitState = new Thread(new SleepState());

        objWaitState.start();

        try {
            objWaitState.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

SleepState.java

public class SleepState implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

Main.java

public class Main {
    public static void main(String[] args) throws InterruptedException {
       Thread objWaitingThread = new Thread(new WaitingState());
       objWaitingThread.start();
       objWaitingThread.sleep(1000);
       System.out.println("T1 : "+ objWaitingThread.getState());
       System.out.println("Main : "+Thread.currentThread().getState());
    }
}

Output

T1 : WAITING
Main : RUNNABLE

A thread is in TIMED_WAITING state when it’s waiting for another thread to perform a particular action within a stipulated amount of time. According to JavaDocs, there are five ways to put a thread on TIMED_WAITING state:
thread.sleep(long millis) (or) wait(int timeout) (or) wait(int timeout, int nanos) thread.join(long millis) (or) LockSupport.parkNanos (or) LockSupport.parkUntil

TimedWaitState.java

public class TimedWaitState implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

Main.java

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread objTWState = new Thread(new TimedWaitState());
        objTWState.start();
        Thread.sleep(2000);
        System.out.println("T1 : "+ objTWState.getState());
    }
}

Output

T1 : TIMED_WAITING
  1. We use ReentrantLock for locking the Resource(totalSeats)
  2. Incase anything goes wrong (Exception being thrown etc.) you want to make sure the lock is released no matter what.
  3. Calling the reserveSeats method should be done inside separate threads

ReservationSystem.java

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReservationSystem {
    private Integer totalSeats;
    private final Lock lock = new ReentrantLock();

    public ReservationSystem(Integer totalSeats){
        this.totalSeats = totalSeats;
    }

    public Integer getTotalSeats(){
        return totalSeats;
    }

    public void reserveSeats(String userName, int numOfSeats){
        lock.lock();

        try{
            if(numOfSeats >0 && totalSeats>numOfSeats){
                totalSeats -= numOfSeats;
                System.out.println(userName + " has reserved "+ numOfSeats + " with " + totalSeats + " still available");
            }else{
                System.out.println("Seats not Available");
            }
        }finally {
            lock.unlock();
        }
    }
}

BookSeat.java

public class BookSeat {
    public static void main(String[] args) {
        ReservationSystem objResSys = new ReservationSystem(100);

        System.out.println("Total available Seats "+ objResSys.getTotalSeats());

        Thread objThread1 = new Thread(() -> {objResSys.reserveSeats("User1", 10);});
        Thread objThread2 = new Thread(() -> {objResSys.reserveSeats("User2", 20);});
        Thread objThread3 = new Thread(() -> {objResSys.reserveSeats("User3", 5);});


        objThread1.start();
        objThread2.start();
        objThread3.start();

        try {
            objThread1.join();
            objThread2.join();
            objThread3.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("Remaining available Seats "+ objResSys.getTotalSeats());
    }
}


Total available Seats 100
User2 has reserved 20 with 80 still available
User1 has reserved 10 with 70 still available
User3 has reserved 5 with 65 still available
Remaining available Seats 65

Banking System

  1. We have Bank Account with 2 Fields – balance and Account Number
  2. We have Transaction class implementing Runnable
  3. We create object for account with some initial balance and try to pass as parameter to runnable Transaction Object

BankAccount.java

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BankAccount {
    private Integer balance;
    private Integer accountNumber;

    private final Lock reLock = new ReentrantLock();

    public BankAccount(Integer balance, Integer accountNumber){
        this.balance = balance;
        this.accountNumber = accountNumber;
    }

    public void debitAmount(Integer amount){
        reLock.lock();

        try{
            balance -= amount;
        }finally {
            reLock.unlock();
        }

    }

    public void creditAmount(Integer amount){
        reLock.lock();

        try{
            balance += amount;
        }finally {
            reLock.unlock();
        }
    }

    public Integer getAccountNumber(){
        return this.accountNumber;
    }

    public Integer getBalance(){
        return this.balance;
    }

}

BankTransaction.java

public class BankTransaction implements Runnable{
    public Integer transAmount;
    public BankAccount bankAccount;

    public BankTransaction(Integer transAmount, BankAccount bankAccount){
        this.transAmount  = transAmount;
        this.bankAccount  = bankAccount;
    }


    @Override
    public void run() {
        if(transAmount >= 0){
            bankAccount.creditAmount(transAmount);
        }else{
            bankAccount.debitAmount(Math.abs(transAmount));
        }
    }
}

BankSystem.java

public class BankSystem {
    public static void main(String[] args) {
        BankAccount objAcc1 = new BankAccount(1000, 101);
        BankAccount objAcc2 = new BankAccount(2000, 102);

        Thread objThread1 = new Thread(new BankTransaction(50, objAcc1));
        Thread objThread2 = new Thread(new BankTransaction(-150, objAcc2));
        Thread objThread3 = new Thread(new BankTransaction(250, objAcc2));
        Thread objThread4 = new Thread(new BankTransaction(250, objAcc1));

        objThread1.start();
        objThread2.start();
        objThread3.start();
        objThread4.start();

        try{
            objThread1.join();
            objThread2.join();
            objThread3.join();
            objThread4.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("Final Balance in Account " + objAcc1.getAccountNumber() + " with balance " + objAcc1.getBalance());
        System.out.println("Final Balance in Account " + objAcc2.getAccountNumber() + " with balance " + objAcc2.getBalance());
    }
}

Output

Final Balance in Account 101 with balance 1300
Final Balance in Account 102 with balance 2100

Communication Models

  1. Synchronous + Blocking – Calling Customer Service and waiting for response online
  2. Asynchronous- Asking My Friend to Call Customer Service and I carry forward with my work
  3. Non-Blocking- Asking Call back from Customer Service and I carry forward with my work
  4. Asynchronous + Non Blocking – I am Calling customer Service and asking to call back My Friend and I carry forward with my work

request -> response
request -> streaming response (Stock Price in Stock Market App, Heart Beat for Health Check in Spring Boot Appp)
streaming request -> response (Using Google Docs and updating in drive in regular time intervals)
streaming request -> streaming response (Playing Game Online)

Reactive Stream Specification
Process Stream of Messages in a Non Blocking Asynchronous manner with Observer Design Pattern(Observe and React incase of change)

  1. Publisher: Emits a sequence of elements to its subscribers.
    void subscribe(Subscriber<? super T> s)
    
  2. Subscriber: Consumes elements provided by a Publisher.
    void onSubscribe(Subscription s)
    void onNext(T t)
    void onError(Throwable t)
    void onComplete()
    
  3. Subscription: Represents a one-to-one lifecycle of a Subscriber subscribing to a Publisher.
    void request(long n)
    void cancel()
    
  4. Processor: Represents a processing stage, which is both a Subscriber and a Publisher.Inherits both Subscriber and Publisher interfaces.

There would be one Publisher at Top, similar to root of tree and there would be 0 to N intermediate processors(subscriber + publisher) and there would be leaf Subscriber

Publisher, Subscriber and Subscription

public interface Publisher<T> {
   public void subscribe(Subscriber<? super T> s);
}
public interface Subscription {
   public void request(long n);
   public void cancel();
}
public interface Subscriber<T> {
   public void onSubscribe(Subscription s);
   public void onNext(T t);
   public void onError(Throwable t);
   public void onComplete();
}
  1. Publisher will have subscribe method through which we would pass the subscriber instance. The Subscription object would be returned by Publisher
  2. The Publisher hands over subscription object to Subscriber. Subscriber uses onSubscribe method to accept subscription.
  3. Subscriber could use subscription object using request method and could cancel subscription. Communication between Publisher and Subscriber happens using subscription object
  4. Subscriber can request N items using subscription object. The Publisher can iterate to N object using onNext method. Publisher only give 3 items if 3 items is requested by subscriber.
  5. If the Publisher has completed transferring all Items, then Publisher can call onComplete() method in Subscriber to notify the subscriber that its work is done
  6. Publisher calls onError() method to notify error.

  1. publisher does not produce data unless subscriber requests for it.
  2. publisher will produce only <= subscriber requested items. publisher can also produce 0 items!
  3. subscriber can cancel the subscription. producer should stop at that moment as subscriber is no longer interested in consuming the data
  4. producer can send the error signal

  1. PublisherImpl instance would call the subscribe method and pass instance of SubscriberImpl
  2. PublisherImpl subscribe method would inturn call the onSubscribe method using the instance of SubscriberImpl passed.
  3. SubscriberImpl would get the same subscription which it has been passed to publisherImpl subscribe method earlier
  4. PublisherImpl has following methods
    • subscribe – takes subscriber as argument and creates new subscription and notify the subscriber by calling the onSubscribe method
      public void subscribe(Subscriber subscriber) {
              var subscription = new SubscriptionImpl(subscriber);
              subscriber.onSubscribe(subscription);
      }
      
  5. SubscriberImpl has following methods
    • onSubscribe – To get the same subscription passed to publisher subscribe method. This is inturn called from publisherImpl
    • onNext – called from subscriptionImpl to pass the data during iteration
    • onError – called from subscriptionImpl during error
    • onComplete – called from when all data is completed
    @Override
        public void onSubscribe(Subscription subscription) {
            this.subscription = subscription;
        }
    
        @Override
        public void onNext(String emailId) {
            logger.info("Received {}", emailId);
        }
    
        @Override
        public void onError(Throwable throwable) {
            logger.info("---------------------------------------------");
            logger.info("Received error {}", throwable.getMessage());
        }
    
        @Override
        public void onComplete() {
            logger.info("Subscription ended");
        }
    
  6. SubscriptionImplhas following methods
    • request – you can request date using subscriptionImpl instance by passing no of records
    • cancel – cancel subscription
    @Override
        public void request(long requestedItemCnt) {
            if(isCancelled){
                return;
            }
    
            logger.info("Subscriber has requested {} items ", requestedItemCnt);
    
            if(requestedItemCnt >MAX_ITEMS){
                this.subscriber.onError(new RuntimeException(" Items requested is more than Total Items Available"));
                this.isCancelled = true;
                return;
            }
    
            //Check if all items(MAX_ITEMS) were sent
            for(int idx=0;idx<requestedItemCnt && count<MAX_ITEMS; idx++){
                count++;
                this.subscriber.onNext(this.faker.internet().emailAddress());
            }
    
            //If all items were sent complete subscription
            if(count == MAX_ITEMS){
                logger.info("No More data from Producer");
                this.subscriber.onComplete();
                isCancelled = true;
            }
        }
    
        @Override
        public void cancel() {
            logger.info("Cancelling Subscription... . . .");
            isCancelled = true;
        }
    

pom.xml

  <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor.netty</groupId>
            <artifactId>reactor-netty-core</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor.netty</groupId>
            <artifactId>reactor-netty-http</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <dependency>
            <groupId>com.github.javafaker</groupId>
            <artifactId>javafaker</artifactId>
            <version>${faker.version}</version>
        </dependency>

PublisherImpl.java

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;

public class PublisherImpl implements Publisher {
    @Override
    public void subscribe(Subscriber subscriber) {
        var subscription = new SubscriptionImpl(subscriber);
        subscriber.onSubscribe(subscription);
    }
}

SubscriptionImpl.java

public class SubscriptionImpl implements Subscription {
    private static final Logger logger = LoggerFactory.getLogger(SubscriptionImpl.class);
    private final Subscriber<? super String> subscriber;
    private boolean isCancelled = false;
    private final Faker faker;

    private final int MAX_ITEMS = 10;
    private static int count = 0;


    public SubscriptionImpl(Subscriber subscriber){
        this.subscriber = subscriber;
        this.faker = Faker.instance();
    }

    @Override
    public void request(long requestedItemCnt) {
        if(isCancelled){
            return;
        }

        logger.info("Subscriber has requested {} items ", requestedItemCnt);

        if(requestedItemCnt >MAX_ITEMS){
            this.subscriber.onError(new RuntimeException(" Items requested is more than Total Items Available"));
            this.isCancelled = true;
            return;
        }

        //Check if all items(MAX_ITEMS) were sent
        for(int idx=0;idx<requestedItemCnt && count<MAX_ITEMS; idx++){
            count++;
            this.subscriber.onNext(this.faker.internet().emailAddress());
        }


        //If all items were sent complete subscription
        if(count == MAX_ITEMS){
            logger.info("No More data from Producer");
            this.subscriber.onComplete();
            isCancelled = true;
        }
    }

    @Override
    public void cancel() {
        logger.info("Cancelling Subscription... . . .");
        isCancelled = true;
    }
}

SubscriberImpl.java

import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SubscriberImpl implements Subscriber<String> {
    private static final Logger logger = LoggerFactory.getLogger(SubscriberImpl.class);
    private Subscription subscription;

    @Override
    public void onSubscribe(Subscription subscription) {
        this.subscription = subscription;
    }

    @Override
    public void onNext(String emailId) {
        logger.info("Received {}", emailId);
    }

    @Override
    public void onError(Throwable throwable) {
        logger.info("---------------------------------------------");
        logger.info("Received error {}", throwable.getMessage());
    }

    @Override
    public void onComplete() {
        logger.info("Subscription ended");
    }

    public Subscription getSubscription() {
        return subscription;
    }
}

Main.java

import org.mugil.publisher.PublisherImpl;
import org.mugil.subscriber.SubscriberImpl;

import java.time.Duration;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        getMessages();
    }

    public static void getMessages() throws InterruptedException {
        var objPublisher = new PublisherImpl();
        var objSubscriber = new SubscriberImpl();

        objPublisher.subscribe(objSubscriber);
        objSubscriber.getSubscription().request(1);
        Thread.sleep(Duration.ofSeconds(2));

        objSubscriber.getSubscription().request(2);
        Thread.sleep(Duration.ofSeconds(2));

        objSubscriber.getSubscription().request(3);
        Thread.sleep(Duration.ofSeconds(2));

        objSubscriber.getSubscription().cancel();

        objSubscriber.getSubscription().request(1);
    }
}

Output

18:00:57.240 [main] INFO org.mugil.publisher.SubscriptionImpl -- Subscriber has requested 1 items 
18:00:57.534 [main] INFO org.mugil.subscriber.SubscriberImpl -- Received jani.mante@gmail.com
18:00:59.537 [main] INFO org.mugil.publisher.SubscriptionImpl -- Subscriber has requested 2 items 
18:00:59.541 [main] INFO org.mugil.subscriber.SubscriberImpl -- Received sunny.quigley@yahoo.com
18:00:59.544 [main] INFO org.mugil.subscriber.SubscriberImpl -- Received hang.gutkowski@yahoo.com
18:01:01.546 [main] INFO org.mugil.publisher.SubscriptionImpl -- Subscriber has requested 3 items 
18:01:01.548 [main] INFO org.mugil.subscriber.SubscriberImpl -- Received malik.thiel@hotmail.com
18:01:01.549 [main] INFO org.mugil.subscriber.SubscriberImpl -- Received andre.purdy@gmail.com
18:01:01.550 [main] INFO org.mugil.subscriber.SubscriberImpl -- Received kim.greenfelder@gmail.com
18:01:03.560 [main] INFO org.mugil.publisher.SubscriptionImpl -- Cancelling Subscription... . . .

Mono is a type of instant Publisher that represents a single or empty value. This means it can emit only one value at most for the onNext() request and then terminates with the onComplete() signal. In case of failure, it only emits a single onError() signal.Most Mono implementations are expected to immediately call onComplete on their Subscriber after having called onNext.a combination of onNext and onError is explicitly forbidden.

Overridden Lambda implementation available in mono

subscribe();  //1

subscribe(Consumer<? super T> consumer);  //2

subscribe(Consumer<? super T> consumer,
          Consumer<? super Throwable> errorConsumer);  //3

subscribe(Consumer<? super T> consumer,
          Consumer<? super Throwable> errorConsumer,
          Runnable completeConsumer);  //4

subscribe(Consumer<? super T> consumer,
          Consumer<? super Throwable> errorConsumer,
          Runnable completeConsumer,
          Consumer<? super Subscription> subscriptionConsumer); //5

//1

        Publisher<String> rctMono  = Mono.just("Hello React"); // Simple Mono Publisher using Just
        var subs = new SubscriberImpl();
        rctMono.subscribe(subs);
        subs.getSubscription().request(10);

Output

08:21:46.241 [main] INFO org.mugil.subscriber.SubscriberImpl -- Received Hello React
08:21:46.252 [main] INFO org.mugil.subscriber.SubscriberImpl -- Subscription ended

//5


       Mono<Integer> rctMono2 = Mono.just(1)
                                    .map(i -> i/0);
       rctMono2.subscribe(i -> System.out.println(i),   //Consumer
                          err -> System.out.println("Error Msg -" + err.getMessage()), //onError, Not Mandatory
                          () -> System.out.println("Completed"), //onComplete, Not Mandatory
                          subscription -> subscription.request(1)); //onRequest, Not Mandatory

Output

Error Msg -/ by zero

Simple code which returns Mono based on switch case

    public static Mono<String> getUserName(Integer num){
        return switch (num){
            case 1 -> Mono.just("How are you");
            case 2 -> Mono.empty();
            default -> Mono.error(new RuntimeException("Invalid Input"));
        };
    }

Using mono.just would invoke the sumOfNums as it always fetches value from JVM memory rather than streaming

public static void main(String[] args) {
  List<Integer> lstNums = List.of(1,2,3);
  Mono.just(sumOfNums(lstNums)); //Mono.just takes the value from memory so wont be suitable for streaming data incase large data should be handled
}

public static int sumOfNums(List<Integer> lstNums){
   System.out.println("Sum invoked");
   return lstNums.stream().mapToInt(num -> num).sum();
}

Output

Sum invoked

Using mono.fromSupplier would invoke the sumOfNums during Terminal Operation rather than Intermediate Operation

List<Integer> lstNums = List.of(1,2,3);
        Mono.fromSupplier(() -> sumOfNums(lstNums)); // Intermediate Operation                 


  public static int sumOfNums(List<Integer> lstNums){
        System.out.println("Sum invoked");
        return lstNums.stream().mapToInt(num -> num).sum();
  }

Output


Mono.fromSupplier vs Mono.fromCallable
fromCallable calls a checked exception where as fromSupplier doesnot throws checked exception. So if you substitute supplier in place of callable you should also write try catch block to handle exception

Why we need Dependency Injection?
Its to overcome monotony behavior. If you are asking for Apple(from Apple.java) you would be served apple object. If you are asking for Mango(from Mango.java) you would be served mango object. Now in case, you are having Fruit and now you are asking Fruit(from Fruit.java) you would be served either apple or mango. Lets take a simple example as below

  1. I have a banking application where I have Account Interface with calculateIntereest Method
  2. I have different account types like SavingsAccount, PersonalLoanAccount and vehicleLoanAccount, HousingLoanAccount
  3. I have a Customer class where he would own a particular account type which wont be known until runtime

Accounts.java

package com.mugil.core;

public interface Accounts 
{
 public Integer calculateInterest();
}

SavingsAccount.java

package com.mugil.core;

public class SavingsAccount implements Accounts 
{
 public Integer calculateInterest() 
 {
  return 8;
 }
}

Customer.java

package com.mugil.core;

public class Customer {
 Accounts accountType;

 public Customer(Accounts paccountType) {
  this.accountType = paccountType;
 }

 public Accounts getAccountType() {
  return accountType;
 }

 public void setAccountType(Accounts accountType) {
  this.accountType = accountType;
 }
}

GetInterestFactory.java

package com.mugil.core;

public class GetInterestFactory {
 Accounts objAccount = null;

 public static void main(String[] args) {
  GetInterestFactory objGetInterest = new GetInterestFactory();
  objGetInterest.showInterestRate();
 }

 public void showInterestRate() {
  String strAccType = "Savings";

  switch (strAccType) {
   case "Savings":
    this.objAccount = new SavingsAccount();
    break;
   case "Vehicle":
    this.objAccount = new VehicleLoanAccount();
    break;
   default:
    this.objAccount = new SavingsAccount();
    break;
  }

  System.out.println("Interest Rate - " + this.objAccount.calculateInterest());
 }
}

Output

Interest Rate - 8
  1. In the above java code we decide the Account Type only when the switch case is executed
  2. Until then the account type is kept as generic value using interface

Now what spring does is the same code can be rewritten to determine the Account Type during runtime in setters and constructors as below

Dependency Injection without Spring using Java Constructor

  1. I have created a new class VehicleLoanAccount.java which has a different interest rate
  2. Now I am going to decide the account type in the Person.java in its constructor as below

VehicleLoanAccount.java

package com.mugil.core;

public class VehicleLoanAccount implements Accounts {
 public Integer calculateInterest() {
  return 11;
 }

}

Dependency Injection without Spring using Java Constructor
GetInterestConsWithoutSpring .java

package com.mugil.core;

public class GetInterestConsWithoutSpring {
 public static void main(String[] args) {
  Customer objPerson = new Customer(new SavingsAccount());
  System.out.println("Interest Rate - " + objPerson.getAccountType().calculateInterest());
 }
}

Output

Interest Rate - 11

Dependency Injection without Spring using Java Setter

  1. Now I am going to decide the account type in the GetInterest.java in its setter method
  2. I would be passing the value of the actual type inside the setter at runtime to decide the account type
  3. The Only thing which I have changed in the addition of new constructor to the Customer Class
  4. Setter method would be passed with specific account type during runtime

Customer.java

package com.mugil.core;

public class Customer {
 Accounts accountType;

 public Customer(Accounts paccountType) {
  this.accountType = paccountType;
 }

 public Customer() {}

 public Accounts getAccountType() {
  return accountType;
 }

 public void setAccountType(Accounts accountType) {
  this.accountType = accountType;
 }
}

Dependency Injection without Spring using Setter Method
GetInterestSettWithoutSpring .java

package com.mugil.core;

public class GetInterestSettWithoutSpring {
 public static void main(String[] args) {
  Customer customer = new Customer();
  customer.setAccountType(new SavingsAccount());
  System.out.println("Interest Rate - " + customer.getAccountType().calculateInterest());
 }
}

Output

Interest Rate - 8

Ways of Bean Injection using Spring
In Spring we can let the container create the bean in two ways

  1. Bean definition in XML
  2. Bean definition using @Component
  3. Bean definition using Java

Bean definition in XML
beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-2.5.xsd">
	<bean id="customer" class="com.mugil.core.Customer" autowire="constructor">				
	</bean>
	<bean  id="savingsType" class="com.mugil.core.SavingsAccount"/>
	<bean  id="vehicleLoanAccount" class="com.mugil.core.VehicleLoanAccount"/>		
</beans>

context:component-scan used for detecting bean
Bean definition using @Component
beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-2.5.xsd">
	<context:annotation-config />
	<context:component-scan base-package="com.mugil.core"></context:component-scan>			
</beans>

@Component marking bean
Customer.java

package com.mugil.core;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

@Component
public class Customer 
{

 @Autowired
 @Qualifier("savings")
 Accounts accountType;
.
.
.
}

@Component marking bean
SavingsAccount.java

package com.mugil.core;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("savings")
public class SavingsAccount implements Accounts
{	
	public Integer calculateInterest() 
	{
		return 9;
	}
}

Bean definition using Java

Ways of Bean Injection using Spring
Now, what if we do the same thing from XML and using annotations. Spring offers 3 ways by which dependency injection could be done

  1. XML
    1. Constructor
    2. Setter
    3. Autowiring
      1. byType
      2. byName
      3. Constructor
      4. No
  2. Annotation Based
    1. byType
    2. byName
  3. Java Based

Constructor Based – Dependency Injection using XML

  1. In the below code beans are loaded when the application is deployed and the JVM starts
  2. The beans are uniquely identified using their IDS, in our case it is customer
  3. The parameter for constructor is defined inside constructor-arg in XML

Beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <bean id="customer" class="com.mugil.core.Customer">
      <constructor-arg>
         <bean id="savingAccount" class="com.mugil.core.SavingsAccount" />
      </constructor-arg>
   </bean>
</beans>

GetInterestConsWithSpring.xml

package com.mugil.core;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class GetInterestConsWithSpring {
 public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext("SpringBeans.xml");

  Customer customer = (Customer) context.getBean("customer");

  System.out.println("Interest Rate - " + customer.getAccountType().calculateInterest());
 }
}

Output

Interest Rate - 8

Setter Based – Dependency Injection using XML

  1. For setter injection the only things we need to change is XML
  2. XML should be modified to take value by setter rather than constructor as before using property tag

Beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <bean id="customer" class="com.mugil.core.Customer">
      <property name="accountType" ref="savingAccount" />
   </bean>
   <bean id="savingAccount" class="com.mugil.core.SavingsAccount" />
</beans>

GetInterestSettWithSpring.xml

package com.mugil.core;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class GetInterestSettWithSpring {
 public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext(
   "SpringBeans.xml");

  Customer customer = (Customer) context.getBean("customer");

  System.out.println("Interest Rate - " + customer.getAccountType().calculateInterest());
 }
}

Output

Interest Rate - 8

Autowiring byName

  1. In autoWiring byName the Name of the Instance Varable(accountType) and the ID of the bean in XML should be same
  2. If there is no bean matching the name is found it will throw NullPointerException

Customer.java

package com.mugil.core;

public class Customer {
 Accounts accountType;

 public Customer(Accounts paccountType) {
  this.accountType = paccountType;
 }

 public Customer() {}

 public Accounts getAccountType() {
  return accountType;
 }

 public void setAccountType(Accounts accountType) {
  this.accountType = accountType;
 }
}

Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<bean id="customer" class="com.mugil.core.Customer" autowire="byName">				
	</bean>
	<bean id="accountType" class="com.mugil.core.SavingsAccount"/>
	<bean id="vehicleLoanAccount" class="com.mugil.core.VehicleLoanAccount"/>
</beans>

Output

Interest Rate - 9

What if Bean of Correct Name is notdefined in XML? Inour case it is account Type
Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<bean id="customer" class="com.mugil.core.Customer" autowire="byName">				
	</bean>
	<bean id="savingAccount" class="com.mugil.core.SavingsAccount"/>
	<bean id="vehicleLoanAccount" class="com.mugil.core.VehicleLoanAccount"/>
</beans>

Output

Exception in thread "main" java.lang.NullPointerException
	at com.mugil.core.GetInterestConstAutoWiringXML.main(GetInterestConstAutoWiringXML.java:13)

Autowiring byType

  1. In autoWiring is byType then there should be at least one bean defined for the matching type, in our case it is Accounts
  2. If there is no bean defined of the type then it will throw null pointer exception
  3. If there is more than one matching bean of the same type is found it will throw No unique bean of type

Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<bean id="customer" class="com.mugil.core.Customer" autowire="byType">				
	</bean>
	<bean id="savingsType" class="com.mugil.core.SavingsAccount"/>
</beans>

Output

Interest Rate - 9

What if No bean of right Type is defined in XML or More than one bean of same type defined?
Beans.xml
No bean of right Type is defined in XML

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<bean id="customer" class="com.mugil.core.Customer" autowire="byType">				
	</bean>
</beans>

Output

Exception in thread "main" java.lang.NullPointerException
	at com.mugil.core.GetInterestConstAutoWiringXML.main(GetInterestConstAutoWiringXML.java:13)

Beans.xml
More than one bean of same type defined

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<bean id="customer" class="com.mugil.core.Customer" autowire="byType">				
	</bean>
	<bean id="savingsType" class="com.mugil.core.SavingsAccount"/>
	<bean id="vehicleLoanAccount" class="com.mugil.core.VehicleLoanAccount"/>
</beans>

Output

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customer' defined in class path resource [SpringBeans.xml]: Unsatisfied dependency expressed through bean property 'accountType': : No unique bean of type [com.mugil.core.Accounts] is defined: expected single matching bean but found 2: [accountType, vehicleLoanAccount]; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.mugil.core.Accounts] is defined: expected single matching bean but found 2: [accountType, vehicleLoanAccount]

Autowiring Contructor

  1. In autoWiring using Constructor spring tries to find bean using type.In our case it is Account type
  2. If there is no bean defined of the type then spring will not guess and it will throw null pointer exception
  3. If there is more than one matching bean then spring will not guess and it will throw null pointer exception
  4. If there is more than one constructor spring wont guess the bean and it will throw null pointer exception

Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<bean id="customer" class="com.mugil.core.Customer" autowire="constructor">				
	</bean>
	<bean id="savingsType" class="com.mugil.core.SavingsAccount"/>
</beans>

Output

Interest Rate - 9

Beans.xml
Two bean of same type in constructor injection

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<bean id="customer" class="com.mugil.core.Customer" autowire="constructor">				
	</bean>
	<bean id="savingsType" class="com.mugil.core.SavingsAccount"/>
<bean id="vehicleLoanAccount" class="com.mugil.core.VehicleLoanAccount"/>
</beans>

Beans.xml
No bean of matching type in constructor injection

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<bean id="customer" class="com.mugil.core.Customer" autowire="constructor">				
	</bean>
</beans>

Output

Exception in thread "main" java.lang.NullPointerException
	at com.mugil.core.GetInterestConstAutoWiringXML.main(GetInterestConstAutoWiringXML.java:13)

Autowiring using autodetect
When the bean is configured to autowire by autodetect spring will attempt to autowire by constructor first.If no suitable constructor to bean is found then spring will attempt to autowire byType.
Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<bean id="customer" class="com.mugil.core.Customer" autowire="autodetect">				
	</bean>
</beans>

Annotation Based – byType

  1. In Annotation based autowiring the beans would be injected by xml and would be available in container
  2. context:annotation-config is used to activate annotations in beans already registered in the application context (no matter if they were defined with XML or by package scanning)
  3. Now by using @Autowired tag we inject the bean as dependency where it is required.It is used either over variable in class or over setter or over constructor
  4. The default @Autowired decides the bean based on its type.If more than one bean if found it will throw no unique bean found exception.If no bean found it will throw nullpointer exception
  5. Incase of more than one bean of same type, we can narrow down the selection by using @Qualifier annotation and converting to byName @Autowiring

Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-2.5.xsd">

	<context:annotation-config />
	<bean id="customer" class="com.mugil.core.Customer" autowire="constructor">				
	</bean>
	<bean id="savingsType" class="com.mugil.core.SavingsAccount"/>	
</beans>

Customer.java

package com.mugil.core;

import org.springframework.beans.factory.annotation.Autowired;

public class Customer 
{
 @Autowired
 private Accounts accountType;

 public Customer(Accounts paccountType) {
  this.accountType = paccountType;
 }

 public Customer() {}

 public Accounts getAccountType() {
  return accountType;
 }
 
 public void setAccountType(Accounts accountType) {
  this.accountType = accountType;
 }
}

Annotation Based – byName

  1. In the below code we have two bean of same type
  2. Using @autowired would try to find bean byType.Since there are two beans it would throw no unique bean found exception
  3. Now we need to use @Qualifier passing the name (or) id of the bean as parameter
  4. Incase only Id of bean is there then same would be taken for name, If Name is there then name of bean would be given preference, incase no bean matches name then Id would be given preference

Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-2.5.xsd">

	<context:annotation-config />
	<bean id="customer" class="com.mugil.core.Customer" autowire="constructor">				
	</bean>
	<bean name="savings" id="savingsType" class="com.mugil.core.SavingsAccount"/>
	<bean name="vehicle" id="vehicleLoanAccount" class="com.mugil.core.VehicleLoanAccount"/>		
</beans>

Customer.java

package com.mugil.core;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class Customer 
{

 @Autowired
 @Qualifier("savings")
 Accounts accountType;
.
.
.
}

The below code will work by taking bean id into consideration despite the name doesn’t match.
Customer.java

package com.mugil.core;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class Customer 
{

 @Autowired
 @Qualifier("savingsType")
 Accounts accountType;
.
.
.
}

What if there are two bean with the Same Name?
It will not throw exception during compilation but during runtime it will throw bean name already in use exception
Output

Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Bean name 'savings' is already used in this file
Offending resource: class path resource [SpringBeans.xml]

What if there are two bean with the Same ID?

  1. Eclipse will complain for violating ID should be unique and you are violating
  2. If you build the code still builds but when you run will endup with Caused by: org.xml.sax.SAXParseException; lineNumber: 13; columnNumber: 69; cvc-id.2: There are multiple occurrences of ID value ‘savingsType’

Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-2.5.xsd">

	<context:annotation-config />
	<bean id="customer" class="com.mugil.core.Customer" autowire="constructor">				
	</bean>
	<bean id="savingsType" class="com.mugil.core.SavingsAccount"/>
	<bean id="savingsType" class="com.mugil.core.VehicleLoanAccount"/>		
</beans>

Using @Component Scan to detect and load beans

  1. We can make spring to detect beans on its own by using @component annotation rather then defining in XML with bean tags
  2. Using context:component-scan with base package pointed to beans package will load the bean marked with @component annotation
  3. If there is bean of one type then it would work fine during autowiring, if there is more than one bean of same type then we should uniquely identify the bean using @qualifier annotation
  4. @qualifier annotation should be used both in the place where the bean is referred and also in the place where it is defined.In our case it is Customer.java and SavingsAccount.java

Beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-2.5.xsd">
	<context:annotation-config />
        <context:component-scan base-package="com.mugil.core"></context:component-scan>	
</beans>

Customer.java

package com.mugil.core;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

@Component
public class Customer 
{

 @Autowired
 @Qualifier("savings")
 Accounts accountType;
.
.
.
}

SavingsAccount.java

package com.mugil.core;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("savings")
public class SavingsAccount implements Accounts
{	
	public Integer calculateInterest() 
	{
		return 9;
	}
}

What is the difference between applicationcontext and webapplicationcontext in Spring?

  1. The WebApplicationContext is an extension of the plain ApplicationContext that has some extra features necessary for web applications. It differs from a normal ApplicationContext in that it is capable of resolving themes (see Using themes), and that it knows which Servlet it is associated with (by having a link to the ServletContext). The WebApplicationContext is bound in the ServletContext, and by using static methods on the RequestContextUtils class you can always look up the WebApplicationContext if you need access to it.
  2. ApplicationContext (Root Application Context) : Every Spring MVC web application has an applicationContext.xml file which is configured as the root of context configuration. Spring loads this file and creates an applicationContext for the entire application. This file is loaded by the ContextLoaderListener which is configured as a context param in web.xml file. And there will be only one applicationContext per web application.

    WebApplicationContext : WebApplicationContext is a web aware application context i.e. it has servlet context information. A single web application can have multiple WebApplicationContext and each Dispatcher servlet (which is the front controller of Spring MVC architecture) is associated with a WebApplicationContext. The webApplicationContext configuration file *-servlet.xml is specific to a DispatcherServlet. And since a web application can have more than one dispatcher servlet configured to serve multiple requests, there can be more than one webApplicationContext file per web application.

  3. Spring allows you to build multilevel application context hierarchies, so the required bean will be fetched from the parent context if it’s not present in the current application context. In web apps as default there are two hierarchy levels, root and servlet
  4. Web Application context extended Application Context which is designed to work with the standard javax.servlet.ServletContext so it’s able to communicate with the container.
    public interface WebApplicationContext extends ApplicationContext {
        ServletContext getServletContext();
    }
    
  5. Beans, instantiated in WebApplicationContext will also be able to use ServletContext if they implement ServletContextAware interface
    package org.springframework.web.context;
    public interface ServletContextAware extends Aware 
    { 
         void setServletContext(ServletContext servletContext);
    }
    

How to define RootApplicationContext?
This two level separation comes out of the box when you use the spring servlet classes: to configure the root application context you should use context-param tag in your web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/root-context.xml
            /WEB-INF/applicationContext-security.xml
    </param-value>
</context-param>

The rootapplicationcontext is created by ContextLoaderListener which is declared in web.xml and servletApplicationContexts using servlet tag as below

<!-- rootapplicationcontext-->
<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> 

<!-- servletApplicationContext-->
<servlet>
   <servlet-name>myservlet</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>app-servlet.xml</param-value>
   </init-param>
</servlet>

1.Why we are unable to add primitives as generic type
Allowed

 List<Integer> arrAges = new ArrayList<Integer>();

Not allowed

 List<int> arrAges = new ArrayList<int>();

This is to maintain backwards compatibility with previous JVM runtimes in the sense it could be referred by parent class instance Object. Generics in Java are encountered at compile time The compiler converts from generic type to right type as shown in the example below

List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);

will be turned in to

List list = new ArrayList();
list.Add(new ClassA());
ClassA a = (ClassA)list.get(0);

So anything that is used as generics has to be convert able to Object (in this example get(0) returns an Object), and the primitive types aren’t. So they can’t be used in generics.

2.How to have a Ordered Collections

  1. keep the insertion order: LinkedHashSet and CopyOnWriteArraySet (thread-safe)
  2. keep the items sorted within the set: TreeSet, EnumSet (specific to enums) and ConcurrentSkipListSet (thread-safe)