@Component vs @Bean Annotations in Spring

@Component vs @Bean Annotations in Spring

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.