Factory & Abstract Factory
The factory method and abstract factory patterns separate the creation of objects from the use of them, so callers ask a factory for an instance instead of calling new. Spring is built around this idea: the entire container is one enormous abstract factory, and Spring gives you several ways to plug your own creation logic into it.
The container is the factory
Every Spring application is bootstrapped by a BeanFactory — and ApplicationContext extends it. This is the abstract factory at framework scale: a single object that, given a name or type, produces fully-configured instances of any registered bean.
ApplicationContext context = SpringApplication.run(App.class, args);
PricingService pricing = context.getBean(PricingService.class); // ask the factory
You rarely call getBean yourself (you let DI inject instead), but it is what powers injection under the hood. The container hides how a bean is built — eagerly or lazily, as a singleton or prototype, with or without proxies.
@Bean factory methods
The most common way to contribute your own creation logic is a @Bean method inside a @Configuration class. Each method is a factory method: its return value becomes a managed bean.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ClientConfig {
@Bean
public RestClient paymentRestClient(RestClient.Builder builder) {
return builder
.baseUrl("https://api.payments.example.com")
.defaultHeader("X-App", "devcraftly")
.build();
}
}
This is invaluable when the type is from a third-party library you cannot annotate, or when construction needs decisions you cannot express on the class itself. See configuration beans for the full treatment.
FactoryBean<T> — a bean that builds a bean
When creation is complex enough to deserve its own class, implement FactoryBean<T>. The container detects the interface and exposes the getObject() result — not the FactoryBean itself — as the bean.
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component
public class PaymentGatewayFactory implements FactoryBean<PaymentGateway> {
@Override
public PaymentGateway getObject() {
// complex, conditional, environment-driven construction
return new ResilientPaymentGateway(new StripeGateway());
}
@Override
public Class<?> getObjectType() {
return PaymentGateway.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
Injecting PaymentGateway now yields the object the factory built. Spring uses FactoryBean heavily itself — for example SqlSessionFactoryBean in MyBatis integrations and many *FactoryBean types in Spring Data.
Tip: To inject the
FactoryBeanitself rather than its product, prefix the bean name with&:context.getBean("&paymentGatewayFactory").
@Bean method vs FactoryBean
@Bean method | FactoryBean<T> | |
|---|---|---|
| Where it lives | A method in @Configuration | A dedicated class |
| Best for | Simple, one-off construction | Reusable, complex creation logic |
| Boilerplate | Minimal | An interface to implement |
| Modern preference | Usually this | Mostly framework-internal now |
For application code the @Bean method is almost always the better choice; FactoryBean survives mainly because older integrations and the framework internals rely on it.
ObjectProvider — deferred and conditional creation
ObjectProvider<T> is a factory you can inject when you need an instance lazily, optionally, or on demand rather than at construction time:
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;
@Service
public class ReportService {
private final ObjectProvider<ReportRenderer> rendererProvider;
public ReportService(ObjectProvider<ReportRenderer> rendererProvider) {
this.rendererProvider = rendererProvider;
}
public byte[] render() {
// resolved only when actually needed; null-safe via getIfAvailable
ReportRenderer renderer = rendererProvider.getIfAvailable(PdfRenderer::new);
return renderer.render();
}
}
getIfAvailable, getIfUnique, and stream() let you handle “zero, one, or many candidates” gracefully — handy when an optional dependency may or may not be present.
Note: For prototype-scoped beans, an injected
ObjectProvider(or a lookup method) is the correct way to get a fresh instance each call, since the singleton holding the reference is created only once.