Skip to content
Spring Boot sb design-patterns 3 min read

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 FactoryBean itself rather than its product, prefix the bean name with &: context.getBean("&paymentGatewayFactory").

@Bean method vs FactoryBean

@Bean methodFactoryBean<T>
Where it livesA method in @ConfigurationA dedicated class
Best forSimple, one-off constructionReusable, complex creation logic
BoilerplateMinimalAn interface to implement
Modern preferenceUsually thisMostly 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.

Last updated June 13, 2026
Was this helpful?