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
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
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
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
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
@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
| Part | Meaning |
|---|---|
| TodoService before the name | The return type: what kind of object reference the method returns. |
| todoService | The method name; also the bean name with the default naming configuration. |
| TodoStore store | A required dependency that Spring supplies from the context. |
| new TodoService(store) | Calls the constructor to construct the service. |
| return | Passes the service reference back to Spring. |
6. Follow the call sequence
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 serviceAfter 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 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 →