Introduction
In this article we'll explore two annotations @Bean and @Component. Both these annotations are used to define Spring Beans - objects that'll be managed by Spring IoC container. However, they're used in different contexts as per project's requirements.
@Bean Annotation
We use "@Bean" annotation when more control is required over creation and configuration of objects. Using this method Beans can be explicitly registered inside "@Configuration" annotated classes.
Example
We'll continue with the same example as in our previous article Loose Coupling PART 2 but with a slight enhancement.
Step 1 : Create classes that'll be managed as Beans by Spring
package com.mypackage.engine;
public class PetrolEngine implements Engine{
public String start() {
return "Petrol engine started";
}
public String stop() {
return "Petrol engine stopped";
}
}
public class DieselEngine implements Engine{
public String start() {
return "Diesel engine started";
}
public String stop() {
return "Diesel engine stopped";
}
}
package com.mypackage.engine;
public class ElectricEngine implements Engine{
public String start() {
return "Electric engine started";
}
public String stop() {
return "Electric engine stopped";
}
}
Step 2 : Create an interface to achieve loose coupling
package com.mypackage.engine;
public interface Engine {
public String start();
public String stop();
}
Step 3 : Create a class that'll use Interface instead of objects of concrete classes created in Step 1.
package com.mypackage.car;
import com.mypackage.engine.Engine;
public class MyCar {
private Engine engine;
public MyCar(Engine engine) {
this.engine = engine;
}
public String startCar() {
return "Starting car's engine ..." + engine.start();
}
public String stopCar() {
return "Stopping car's engine ..." + engine.stop();
}
}
Step 4 : Create a configuration class to define Beans.
package com.mypackage.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.mypackage.car.MyCar;
import com.mypackage.engine.DieselEngine;
import com.mypackage.engine.ElectricEngine;
import com.mypackage.engine.Engine;
import com.mypackage.engine.PetrolEngine;
@Configuration
public class CarConfig {
/*
* Simply return objects of respective concrete implementations of Engine
* interface using @Bean for PetrolEngine, DieselEngine and EelectricEngine
*/
@Bean
public PetrolEngine petrolEngine() {
return new PetrolEngine();
}
@Bean
public DieselEngine dieselEngine() {
return new DieselEngine();
}
@Bean
public ElectricEngine electricEngine() {
return new ElectricEngine();
}
/*
* If multiple beans of same type (Engine) are present
* Use @Primary for giving preference to the object that should be used by spring
* Use @Qualifier for specifying a particular type of object
*/
@Bean
@Primary
public Engine engine1() {
//using an existing bean
return petrolEngine();
}
@Bean
@Qualifier("DieselEngineQualifier")
public Engine engine2() {
//not using an existing bean
return new DieselEngine();
}
@Bean
@Qualifier("ElectricEngineQualifier")
public Engine engine3() {
return electricEngine();
}
//No Qualifier specified, @Primary i.e
Petrol Engine will be used
@Bean
public MyCar myPetrolCar(Engine engine) {
return new MyCar(engine);
}
@Bean
public MyCar myDieselCar(@Qualifier("DieselEngineQualifier") Engine engine) {
return new MyCar(engine);
}
@Bean
public MyCar myElectricCar(@Qualifier("ElectricEngineQualifier") Engine engine) {
return new MyCar(engine);
}
}
Step 5 : In Main class, launch context and call methods on bean
package com.mypackage;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.mypackage.car.MyCar;
import com.mypackage.config.CarConfig;
public class MyApplication {
public static void main(String[] args) {
try (
var context = new AnnotationConfigApplicationContext(CarConfig.class)) {
MyCar myPetrolCar = (MyCar) context.getBean("myPetrolCar");
System.out.println(myPetrolCar.startCar());
MyCar myDieselCar = (MyCar) context.getBean("myDieselCar");
System.out.println(myDieselCar.startCar());
MyCar myElectricCar = (MyCar) context.getBean("myElectricCar");
System.out.println(myElectricCar.startCar());
}
}
}
Output
Starting car's engine ...Petrol engine started
Starting car's engine ...Diesel engine started
Starting car's engine ...Electric engine started
@Component Annotation
You might've already noticed that using @Bean to define Spring Beans allows reusing existing Beans inside newly created Beans. But we're still making use of new keyword to explicitly create objects of respective classes. You might want to know if there's any way wherein the framework automatically creates and performs auto-wiring of the objects. Using @Component annotation can help achieve this objective.
Example
Step 1 : Define classes that'll be used as a Component, its objects will be managed by Spring. Use @Primary and @Qualifier annotations as decribed in comments of code section above.
package com.mypackage.engine;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component
@Primary
public class PetrolEngine implements Engine{
public String start() {
return "Petrol engine started";
}
public String stop() {
return "Petrol engine stopped";
}
}
package com.mypackage.engine;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("DieselEngineQualifier")
public class DieselEngine implements Engine{
public String start() {
return "Diesel engine started";
}
public String stop() {
return "Diesel engine stopped";
}
}
package com.mypackage.engine;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("ElectricEngineQualifier")
public class ElectricEngine implements Engine{
public String start() {
return "Electric engine started";
}
public String stop() {
return "Electric engine stopped";
}
}
Step 2 : Create an interface to achieve loose coupling
package com.mypackage.engine;
public interface Engine {
public String start();
public String stop();
}
Step 3 : Create a class that'll use Interface instead of objects of concrete classes created in Step 1.
package com.mypackage.car;
import org.springframework.stereotype.Component;
import com.mypackage.engine.Engine;
@Component
public class MyCar {
private Engine engine;
public MyCar(Engine engine) {
this.engine = engine;
}
public String startCar() {
return "Starting car's engine ..." + engine.start();
}
public String stopCar() {
return "Stopping car's engine ..." + engine.stop();
}
}
Step 4 : Use @ComponentScan annotation to scan for classes with @Component annotation.
package com.mypackage;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.mypackage.car.MyCar;
@ComponentScan("com.mypackage")
public class MyApplication {
public static void main(String[] args) {
try (
var context = new AnnotationConfigApplicationContext(MyApplication.class)) {
System.out.println(context.getBean(MyCar.class).startCar());
System.out.println(context.getBean(MyCar.class).stopCar());
}
}
}
Output
Starting car's engine ...Petrol engine started
Stopping car's engine ...Petrol engine stopped
You can use @Qualifier in class MyCar to decide which Engine object should be used. For example:
public MyCar(@Qualifier("DieselEngine") Engine engine) { this.engine = engine; }
Summary
In this article, we'll compare @Bean and @Component annotations in Spring for managing Beans. @Bean provides more control over Bean creation and configuration, allowing the explicit declaration of Beans inside @Configuration classes. On the other hand, @Component enables automatic Bean creation and autowiring through classpath scanning. We've demonstrated both approaches using a car engine example, illustrating how to set up Beans and achieve loose coupling with interfaces.