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.
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 ...");
}
}
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.
@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>";
}
}
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 ...
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