SPRING CONCEPTS

Factory methods and @Bean

Spring Concepts / Factory methods and @Bean

A factory method creates or obtains an object and returns its reference to the caller.

Understand the idea

In this tutorial, the @Bean methods in AppConfig are factory methods. Spring calls them when it needs to create their beans. A method may call a constructor internally, configure the resulting object, and return it. The method and constructor have different jobs.

A concrete example

public TodoService createTodoService(TodoStore store) {
    return new TodoService(store);
}

In the application

The Spring objects demo uses todoStore(), todoService(TodoStore store), and todoPrinter(TodoService service) as explicit bean factory methods. The complete runnable files are in Spring Foundation, section 2.6.

Factory methods are ordinary Java methods. @Bean makes a method part of Spring bean configuration when its configuration is registered. A factory method does not have to be static and does not inherently guarantee a new object on every call.

Stage 12: why use @Bean if @Component already works?

Stage 12 teaches another way to supply an object for Spring to manage. ProductFileReader still uses @Component. Its constructor now receives the JsonMapper supplied by Spring instead of creating its own mapper.

JsonMapper is a library class; we cannot add @Component to that dependency class in our source project. An @Bean factory can build its object and return it to Spring. @Component and @Bean work together.

Catalog is our business name for the collection of products. CatalogSettings holds INR and a minimum stock of 20. CatalogConfig specifies how to construct those settings. The service applies the stock rule. This separates responsibilities, but a bean specifically means a Spring-managed object. The settings class is optional in this small teaching example.

A shared mapper is possible without Spring

Plain Java sharing — illustrative statements using the Stage 12 constructor
JsonMapper mapper = JsonMapper.builder().build();
ProductFileReader firstReader = new ProductFileReader(mapper);
ProductFileReader secondReader = new ProductFileReader(mapper);

Both readers receive the same mapper reference. Calling the builder twice would create two mappers. With @Bean, the default singleton scope means one instance for that bean definition in that application context; it does not forbid constructing other objects yourself.

What happens during this example’s startup?

Spring discovers ProductFileReader, sees its required JsonMapper constructor argument, obtains the configured mapper bean, constructs the reader with that reference and makes the reader available to other beans.

@Component alone does not create every possible dependency. Required dependencies must be registered or otherwise supplied. Our non-lazy singleton beans are created during context startup; Spring also supports creating some beans later when needed.

1. Start with direct construction

Illustrative Java statements
TodoStore store = new TodoStore();
TodoService service = new TodoService(store);

new TodoService(store) constructs a service using the existing store reference. The service variable receives the reference to the resulting object.

2. Move construction into a method

Illustrative method declaration
public TodoService createTodoService(TodoStore store) {
    return new TodoService(store);
}

Read this as: give this method a store reference, and it will construct and return a service that uses it. The returned value is a TodoService reference. The caller decides what to do with that reference.

3. Call the factory yourself

Statements inside an instance method of the same helper class
TodoStore store = new TodoStore();
TodoService service = createTodoService(store);

This is ordinary Java. No Spring annotation is needed for a method to serve as a factory. These small excerpts explain the idea; use the complete demo in section 2.6 for execution.

4. Let Spring call a configured factory

Illustrative AppConfig.java excerpt
@Configuration(proxyBeanMethods = false)
public class AppConfig {
    @Bean
    public TodoStore todoStore() {
        return new TodoStore();
    }

    @Bean
    public TodoService todoService(TodoStore store) {
        return new TodoService(store);
    }
}

Register AppConfig with the context. Spring reads its @Bean methods as bean definitions and supplies their dependency parameters when invoking them. TodoStore and TodoService are the classes from the complete demo.

5. Read the service factory

PartMeaning
TodoService before the nameThe return type: what kind of object reference the method returns.
todoServiceThe method name; also the bean name with the default naming configuration.
TodoStore storeA required dependency that Spring supplies from the context.
new TodoService(store)Calls the constructor to construct the service.
returnPasses the service reference back to Spring.

6. Follow the call sequence

Dependency sequence for this example
Spring calls todoStore()
  -> receives and registers a TodoStore object
  -> supplies that store to todoService(store)
  -> the method constructs and returns a TodoService
  -> Spring manages the returned service

After startup, context.getBean(TodoService.class) retrieves the managed service. With the default singleton scope here, repeated lookups reuse the registered instance; they do not invoke the factory to make a fresh service each time.

7. Constructor versus factory method

Constructor excerpt
// Constructor declared inside TodoService
public TodoService(TodoStore store) {
    this.store = store;
}

The constructor has the class name and no return type; it initializes an instance during construction. The factory is an ordinary method with a return type and a chosen name. Here it calls that constructor and returns the object reference.

8. Why use a factory?

It gives you a place to choose an implementation, supply configuration, or prepare an object before returning it. This is useful for classes from libraries whose source you cannot annotate.

An ordinary factory can return a new or existing object. In this example its body uses new; Spring separately controls when it calls the factory and how it reuses the resulting bean.

The example uses proxyBeanMethods = false. Do not call its factory methods directly expecting singleton lookup behavior: request dependencies as method parameters and retrieve beans through the context.

Practice: find the three @Bean methods in the complete demo. For each, identify its return type, the dependencies Spring supplies, and the constructor invoked inside it.

Detailed walkthrough: How Spring creates and connects objects →

See it in a working stage

Related concepts

Read the official documentation ↗
Java 21 · Spring Boot 4.1.1 · Edupoly