CodeGym /Courses /Module 5. Spring /Practice: creating configuration classes and beans

Practice: creating configuration classes and beans

Module 5. Spring
Level 2 , Lesson 7
Available

Configuration classes in Java replace the old XML configuration approach. This makes setting up Spring applications more readable and convenient.

A configuration class is a Java class used to describe your application's configuration. In Spring these classes are annotated with @Configuration. That tells the IoC-container that the class contains methods that create beans. Those methods are annotated with @Bean.

You can think of a configuration class like the menu at your favorite cafe. With @Bean you describe what will be served (the objects), and the IoC-container acts like the waiter that brings everything you need (dependency injection) to your table.

Example of a simple configuration class


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public String welcomeMessage() {
        return "Welcome to the Spring Framework!";
    }
}

In our example welcomeMessage is a bean that returns a string. The configuration class AppConfig contains a method annotated with @Bean, and Spring knows that this method creates a bean managed by the IoC-container.

But don't worry — we won't stop at a bean that just says "hi". We'll ramp things up and create more complex configurations.


Creating beans and working with dependencies

Configuration classes are useful when creating objects that have dependencies. For example, if we have a Car class that depends on an Engine, we can wire that up in a configuration class.

Example with dependencies


public class Engine {
    private String type;

    public Engine(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}

public class Car {
    private Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        System.out.println("Car is driving with " + engine.getType() + " engine.");
    }
}

Now let's create a configuration class for these objects:


@Configuration
public class VehicleConfig {

    @Bean
    public Engine engine() {
        return new Engine("V8");
    }

    @Bean
    public Car car() {
        return new Car(engine());
    }
}

In the car method we call the engine method to pass it into the Car constructor. This lets Spring know that the Car bean depends on the Engine bean. Now the IoC-container will take care of wiring our object correctly.

How does this work?

  1. Spring creates an Engine bean with type V8.
  2. Then Spring creates the Car bean, and the Engine bean is automatically passed into its constructor.

This handy process reduces the amount of code we'd have to write manually. Honestly, nobody enjoys wiring up all dependencies by hand.

Declaring dependencies between beans explicitly

If for some reason you want to explicitly tell the Car bean that it needs the Engine bean, you can use the @Qualifier annotation (spoiler: you'll need it in later lectures).


Complex dependencies: beans depending on multiple other beans

Say we're building a flight booking service. We need to combine several dependencies, like a client for working with a database and a payment service. You can wire all of that in a configuration class.


public class PaymentService {
    public void pay() {
        System.out.println("Payment processed successfully!");
    }
}

public class BookingService {
    private PaymentService paymentService;

    public BookingService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void bookFlight() {
        System.out.println("Booking completed.");
        paymentService.pay();
    }
}

Let's configure this with a configuration class:


@Configuration
public class BookingConfig {

    @Bean
    public PaymentService paymentService() {
        return new PaymentService();
    }

    @Bean
    public BookingService bookingService() {
        return new BookingService(paymentService());
    }
}

Now we get:

  • Encapsulation of object-creation logic.
  • Less boilerplate in the main application logic.
  • Flexibility: configuration changes are easy to adapt to.

Avoiding bean duplication problems

One common mistake is accidentally creating duplicate beans. If you try to declare two beans with the same name, Spring will throw an error: "Found multiple candidates for dependency injection". To avoid this, make sure your @Bean methods have unique names and are correctly defined.


Practice: setting up a real project

Let's take a step toward our example app. We'll create a service to manage books in a library using a configuration class.

Business logic


public class Book {
    private String title;
    private String author;

    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }

    @Override
    public String toString() {
        return "Book: " + title + " by " + author;
    }
}

public class LibraryService {
    private Book book;

    public LibraryService(Book book) {
        this.book = book;
    }

    public void displayBookInfo() {
        System.out.println(book.toString());
    }
}

Configuration class


@Configuration
public class LibraryConfig {

    @Bean
    public Book defaultBook() {
        return new Book("Clean Code", "Robert C. Martin");
    }

    @Bean
    public LibraryService libraryService() {
        return new LibraryService(defaultBook());
    }
}

Using it in the main application


import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(LibraryConfig.class);

        LibraryService libraryService = context.getBean(LibraryService.class);
        libraryService.displayBookInfo();
    }
}

Program output:


Book: Clean Code by Robert C. Martin

Congrats! We've covered how to use configuration classes and create Spring Beans. Now you can construct objects, wire their dependencies, and make the IoC-container work for you.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION