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)

Similarities between TreeMap and TreeSet in Java

  1. Both TreeMap and TreeSet are sorted data structure, which means they keep there element in predefined Sorted order. Sorting order can be natural sorting order defined by Comparable interface or custom sorting Order defined by Comparator interface. Both TreeMap and TreeSet has overloaded constructor which accept a Comparator, if provided all elements inside TreeSet or TreeMap will be compared and Sorted using this Comparator.
  2. Both TreeSet and TreeMap implements base interfaces e.g. TreeSet implements Collection and Set interface so that they can be passed to method where a Collection is expected and TreeMap implements java.util.Map interface, which means you can pass it when a Map is expected
  3. TreeSet is practically implemented using TreeMap instance, similar to HashSet which is internally backed by HashMap instance.
  4. Both TreeMap and TreeSet are non synchronized Collection, hence can not be shared between multiple threads. You can make both TreeSet and TreeMap synchronized by wrapping them into Synchronized collection by calling Collections.synchroinzedMap() method.
  5. Iterator returned by TreeMap and TreeSet are fail-fast, means they will throw ConcurrentModificationException when TreeMap or TreeSet is modified structurally once Iterator is created.
  6. Both TreeMap and TreeSet are slower than there Hash counter part like HashSet and HashMap and instead of providing constant time performance for add, remove and get operation they provide performance in O(log(n)) order.

TreeSet vs TreeMap

  1. Major difference between TreeSet and TreeMap is that TreeSet implements Set interface while TreeMap implements Map interface in Java.
  2. TreeMap and TreeSet is the way they store objects. TreeSet stores only one object while TreeMap uses two objects called key and Value. Objects in TreeSet are sorted while keys in TreeMap remain in sorted Order.
  3. former implements NavigableSet while later implements NavigableMap in Java.
  4. duplicate objects are not allowed in TreeSet but duplicates values are allowed in TreeMap.

1.How to get elements from HashMap

public static void printMap(Map mp) 
{
    Iterator it = mp.entrySet().iterator();

    while (it.hasNext()) 
    {
        Map.Entry pairs = (Map.Entry)it.next();
        System.out.println(pairs.getKey() + " = " + pairs.getValue());

        //Avoids a ConcurrentModificationException
        it.remove(); 
    }
}

2.Adding keys to HashMap Finding Next Key
To find the next key while using HashMap with Integer as Key the following function can be used.

  1. Iterate through List of Keys
  2. Sort the Keys
  3. Find the Highest value by looking into Key at size-1
  4. The next key to be used is received by adding 1 to Key(lastMaxElem) at size-1
private Integer getNextKey()
{
    List<Integer> keyList = new ArrayList<Integer>();
    int lastMaxElem = 0;
		
    HashMap WaterfallHM = (HashMap) getFromWorkFlowScope("WaterfallHM");		
    Set<Integer> keys = WaterfallHM.keySet();
        
    for ( Integer key : keys) {
	keyList.add(key);
    }
		
    Collections.sort(keyList); // Sort the arraylist
    lastMaxElem = keyList.get(keyList.size() - 1);
    lastMaxElem++; 
		
    return new Integer(lastMaxElem);
}

3.How to Initialize a Constants in HashMap

public class Test 
{
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<Integer, String>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}

4.Why Map Interface doesnot extend Collections Framework
Collection assume elements of one value. Map assumes entries of key/value pairs. They could have been engineered to re-use the same common interface however some methods they implement are incompatible e.g.

Collection.remove(Object) - removes an element.
Map.remove(Object) - removes by key, not by entry.

There are some methods in common; size(), isEmpty(), clear(), putAll/addAll()

Collection interface is largely incompatible with the Map interface. If Map extended Collection, what would the add(Object) method do

5.Why need ConcurrentHashMap and CopyOnWriteArrayList
he synchronized collections classes, Hashtable, and Vector, and the synchronized wrapper classes, Collections.synchronizedMap() and Collections.synchronizedList(), provide a basic conditionally thread-safe implementation of Map and List. However, several factors make them unsuitable for use in highly concurrent applications, for example, their single collection-wide lock is an impediment to scalability and it often becomes necessary to lock a collection for a considerable time during iteration to prevent ConcurrentModificationException.ConcurrentHashMap(uses Segments) and CopyOnWriteArrayList implementations provide much higher concurrency while preserving thread safety, with some minor compromises in their promises to callers.

In Spring MVC Exceptions can be handled at three levels.

1.Controller Based
We can define exception handler methods in our controller classes. All we need is to annotate these methods with @ExceptionHandler annotation. This annotation takes the Exception class as an argument. So if we have defined one of these for Exception class, then all the exceptions thrown by our request handler method will have handled.
These exception handler methods are just like other request handler methods and we can build error response and respond with a different error page. We can also send a JSON error response, that we will look later on in our example.

@ExceptionHandler({SpringException.class})
.
.
@RequestMapping(value = "/addStudent", method = RequestMethod.POST)
@ExceptionHandler({SpringException.class})
public String addStudent(@ModelAttribute("HelloWeb") Student student, ModelMap model) 
{
 if (student.getName().length() < 5)  
  throw new SpringException("Given name is too short");
 else
  model.addAttribute("name", student.getName());
 
 if (student.getAge() < 10)
  throw new SpringException("Given age is too low");
 else
  model.addAttribute("age", student.getAge());

  model.addAttribute("id", student.getId());
  return "result";
}
.
.
.

If there are multiple exception handler methods defined, then handler method that is closest to the Exception class is used. For example, if we have two handler methods defined for IOException and Exception and our request handler method throws IOException, then the handler method for IOException will get executed.

2.Global Exception Handler-The handler methods in Global Controller Advice is same as Controller based exception handler methods and used when controller class is not able to handle the exception.@ControllerAdvice is a annotation provided by Spring allowing you to write global code that can be applied to a wide range of controllers, varying from all controllers to a chosen package or even a specific annotation. The annotation could be applied at Package Level, Class Level and at Annotation Level.

Package Level Application

@ControllerAdvice("my.chosen.package")
@ControllerAdvice(value = "my.chosen.package")
@ControllerAdvice(basePackages = "my.chosen.package")

Package Level Application – This will apply across all the classes inside the package where MyClass.class is placed.

@ControllerAdvice(basePackageClasses = MyClass.class)

Controller Level
Controller advice can be limited to certain controllers (not methods) by using one of the values of the @ControllerAdvice annotation, e.g.

@ControllerAdvice(assignableTypes = {MyController1.class, MyController2.class})

Controller identified by Annotation
If you want to apply it to controllers with certain annotations. The below snippet would only assist controllers annotated with @RestController (which it covers by default) but will not include @Controller annotated classes.

@ControllerAdvice(annotations = RestController.class)

3.HandlerExceptionResolver – can be implemented by the application to resolve exceptions thrown during processing an HTTP request. The exception can be thrown by one of the application’s handler methods or outside of it but during processing a request. The method, HandlerExceptionResolver#resolveException(), returns an instance of ModelAndView specifying an error page. The Implementations are typically registered as beans in the application context. The application registered HandlerExceptionResolvers will only be invoked if the exception is not already handled by the default HandlerExceptionResolvers. We can, however, change the order of the resolvers so that a given resolver can be invoked first.

@ExceptionHandler vs HandlerExceptionResolver vs @ControllerAdvice
@ExceptionHandler works at the Controller level and it is only active for that particular Controller, not globally for the entire application.

@ControllerAdvice used for global error handling in the Spring MVC application.It also has full control over the body of the response and the status code.

HandlerExceptionResolver-This will resolve any exception thrown by the application. It is used to resolve standard Spring exceptions to their corresponding HTTP Status Codes. It does not have control over the body of the response, means it does not set anything to the body of the Response.It does map the status code on the response but the body is null.

Posted in MVC.