Annotations are the heart of Spring and and Spring boot framework. When the Spring framework launched initially, all the definitions were in xml files. That led to complex configurations, hard to understand.

That’s why, subsequent versions of Spring and the Spring boot framework came with a bundle of Annotations. These are easy to understand, and doesn’t need additional declarations in xml files.

Let’s dive into the most popular annotations used in Spring boot.

Using the @SpringBootApplication Annotation

Use a single annotation called @SpringBootApplication to enable all the below three annotations at the entry point of the application.

@EnableAutoConfiguration

@ComponentScan

@Configuration

See below how Spring boot uses this annotation at the entry point, or the first application class that is loaded.

package com.sbstarter.springbootstarter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootstarterApplication {

	@Autowired
	public Cat cat;

	public static void main(String[] args) {
		SpringApplication.run(SpringbootstarterApplication.class, args);
		System.out.println("Hello world application ...");
	}

}

Class based Components

You can define any Spring managed Object as with a @Component annotation.

Use @Service, @Controller and @Repository to categorise the Application Objects in the right type, in order to use point cuts or join points to insert advise to the framework.

Technically, using @Service or @Component doesn’t make any difference, unless you want to categorise the objects properly. However, that is where we can use Aspects (AOP), which is one of the most powerful Spring feature.

Other Class based and Method based Annotations

@RestController with @RequestMapping

This is a type of Controller that is specifically used for RestAPI, that returns some data or text. It can be JSON as well.

package com.sbstarter.springbootstarter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class SpringbootstarterApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootstarterApplication.class, args);
		System.out.println("Hello world application ...");
	}

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String loginUser() {
		System.out.println("loginUser method called ....");
		return "Hello world. Login Successful";
	}

}

@Controller with @RequestMapping and @ResponseBody

Use these annotations to return an HTML response to the user

package com.sbstarter.springbootstarter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@SpringBootApplication
@Controller
public class SpringbootstarterApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootstarterApplication.class, args);
		System.out.println("Hello world application ...");
	}

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	@ResponseBody
	public String loginUser() {
		System.out.println("loginUser method called ....");
		return "<h1>Hello world</h1>";
	}

}

Using Dependancy Injection with @Autowired

Let’s define a @Component, and see how to inject that in another declaration using @Autowired.

Cat.java

package com.sbstarter.springbootstarter;

import org.springframework.stereotype.Component;

@Component
public class Cat {
    
    public void getName() {
        System.out.println("Animal name is Cat ...");
    }
    
}

Looking at the @Component annotation, Spring boot instantiates an object of Cat. It stores the object in the ApplicationContext pool. ApplicationContext is the object that is first initialised by Spring boot, which in turn scans and initialises all other Components.

SpringbootstarterApplication.java

package com.sbstarter.springbootstarter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@SpringBootApplication
@Controller
public class SpringbootstarterApplication {

	@Autowired
	public Cat cat;

	public static void main(String[] args) {
		SpringApplication.run(SpringbootstarterApplication.class, args);
		System.out.println("Hello world application ...");
	}

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	@ResponseBody
	public String loginUser() {
		System.out.println("loginUser method called ....");
		cat.getName();
		return "<h1>Hello world</h1>";
	}

}

Cat.java

package com.sbstarter.springbootstarter;

import org.springframework.stereotype.Component;

@Component
public class Cat {
    
    public void getName() {
        System.out.println("Animal name is Cat ...");
    }
    
}

When the application loads, an instance of the Cat, which is a @Component, is created and kept in the ApplicationContext pool.

Next, when you use the object with @Autowired annotation, the same instance is injected. Hence, you it is able to call the method getName()

Look at the below output.

loginUser method called ....
Animal name is Cat ...

Dependancy Injection using @Autowired with multiple implementations of the same Interface

Let’s consider the below inheritance.

In the above hiararchy, Animal is the top level interface.

Cat, Horse and Dog are defined as @Component and implements Animal.

Look at the code below.

Animal.java

package com.sbstarter.springbootstarter;

public interface Animal {
    public void getName();
}

Cat.java

package com.sbstarter.springbootstarter;
import org.springframework.stereotype.Component;

@Component
public class Cat implements Animal {
    
    public void getName() {
        System.out.println("Animal name is Cat ...");
    }
    
}

Dog.java

package com.sbstarter.springbootstarter;
import org.springframework.stereotype.Component;

@Component
public class Dog implements Animal {

    public void getName() {
        System.out.println("Animal name is Dog");
    }
    
}

Horse.java

package com.sbstarter.springbootstarter;
import org.springframework.stereotype.Component;

@Component
public class Horse implements Animal {

    public void getName() {
        System.out.println("Animal name is Horse ...");
    }
    
}

Now let’s try to use a dependancy injection in SpringbootstarterApplication.java

SpringbootstarterApplication.java

package com.sbstarter.springbootstarter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@SpringBootApplication
@Controller
public class SpringbootstarterApplication {

	@Autowired
	public Animal animal;

	public static void main(String[] args) {
		SpringApplication.run(SpringbootstarterApplication.class, args);
		System.out.println("Hello world application ...");
	}

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	@ResponseBody
	public String loginUser() {
		System.out.println("loginUser method called ....");
		animal.getName();
		return "<h1>Hello world</h1>";
	}

}

When Spring boot tries to inject an object of type Animal, it sees that it has Cat, Dog and Horse, and all of them implements the Animal interface. That’s where the dependancy injection fails with below error. It clearly states that it expects one Animal object type, but it got three.

java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'springbootstarterApplication': Unsatisfied dependency expressed through field 'animal'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.sbstarter.springbootstarter.Animal' available: expected single matching bean but found 3: cat,dog,horse
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.sbstarter.springbootstarter.Animal' available: expected single matching bean but found 3: cat,dog,horse

@Qualifier comes to the rescue. You can use this annotation to instruct Spring boot to load the type of Animal you want. Look at the new code for SpringbootstarterApplication.java

package com.sbstarter.springbootstarter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@SpringBootApplication
@Controller
public class SpringbootstarterApplication {

	@Autowired
	@Qualifier("dog")
	public Animal animal;

	public static void main(String[] args) {
		SpringApplication.run(SpringbootstarterApplication.class, args);
		System.out.println("Hello world application ...");
	}

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	@ResponseBody
	public String loginUser() {
		System.out.println("loginUser method called ....");
		animal.getName();
		return "<h1>Hello world</h1>";
	}

}

Now, here is the output.

loginUser method called ....
Animal name is Dog

Note that the @Qualifier name, be default will be the same as the Class, starting with lower case. Do Dog becomes dog. That’s how Spring stores the references.

If you want to use a different @Qualifier name, then use the @Bean annotation with a value.

Dog.java

package com.sbstarter.springbootstarter;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class Dog implements Animal {

    public void getName() {
        System.out.println("Animal name is Dog");
    }

    @Bean("mydog")
    public Dog getDog() {
        return new Dog();
    }
    
}

See how the name “mydog” is used in SpringbootstarterApplication.java

package com.sbstarter.springbootstarter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@SpringBootApplication
@Controller
public class SpringbootstarterApplication {

	@Autowired
	@Qualifier("mydog")
	public Animal animal;

	public static void main(String[] args) {
		SpringApplication.run(SpringbootstarterApplication.class, args);
		System.out.println("Hello world application ...");
	}

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	@ResponseBody
	public String loginUser() {
		System.out.println("loginUser method called ....");
		animal.getName();
		return "<h1>Hello world</h1>";
	}

}

Below is the Output.

loginUser method called ....
Animal name is Dog

Leave a Reply

Your email address will not be published. Required fields are marked *