← Назад к вопросам

Как работает паттерн Factory Method в Spring?

1.8 Middle🔥 191 комментариев
#SOLID и паттерны проектирования#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерн Factory Method в Spring

Factory Method — это один из наиболее распространённых паттернов в Spring. Он позволяет создавать объекты без указания конкретных классов, что особенно полезно при работе с интерфейсами и их реализациями. Рассмотрим, как Spring использует этот паттерн и как его применять на практике.

Концепция Factory Method

Factory Method — это метод, который создает объект нужного типа вместо прямого использования оператора new. Это позволяет:

  • Отделить логику создания объектов от их использования
  • Легко менять реализацию без изменения клиентского кода
  • Поддерживать принцип Dependency Inversion из SOLID

Как Spring использует Factory Method

1. Самый простой случай: @Bean методы в @Configuration

@Configuration
public class AppConfig {
    
    // Это и есть Factory Method
    @Bean
    public UserRepository userRepository() {
        return new UserRepositoryImpl();
    }
    
    @Bean
    public UserService userService(UserRepository repository) {
        // Spring сам подставит repository благодаря DI
        return new UserService(repository);
    }
}

В этом примере:

  • userRepository() — factory method, который создает UserRepository
  • userService() — тоже factory method, который создает UserService и внедряет зависимость

2. Стратегия: разные реализации для разных условий

Часто нужно создавать разные реализации интерфейса в зависимости от конфигурации:

interface DataSource {
    String connect();
}

class MySQLDataSource implements DataSource {
    @Override
    public String connect() {
        return "Connected to MySQL";
    }
}

class PostgreSQLDataSource implements DataSource {
    @Override
    public String connect() {
        return "Connected to PostgreSQL";
    }
}

@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource dataSource(
        @Value("${database.type}") String dbType) {
        
        if ("mysql".equals(dbType)) {
            return new MySQLDataSource();
        } else if ("postgresql".equals(dbType)) {
            return new PostgreSQLDataSource();
        }
        throw new IllegalArgumentException("Unknown database type");
    }
}

Теперь, в зависимости от переменной окружения database.type, Spring создаст нужную реализацию.

3. Использование @ConditionalOnProperty

Spring предоставляет удобные аннотации для условного создания Bean:

@Configuration
public class PaymentConfig {
    
    @Bean
    @ConditionalOnProperty(name = "payment.provider", havingValue = "stripe")
    public PaymentService stripePaymentService() {
        return new StripePaymentService();
    }
    
    @Bean
    @ConditionalOnProperty(name = "payment.provider", havingValue = "paypal")
    public PaymentService paypalPaymentService() {
        return new PayPalPaymentService();
    }
}

Если в конфигурации payment.provider=stripe, будет создан Stripe сервис. Если paypal — то PayPal сервис.

Практический пример: Система уведомлений

Допустим, нам нужна система отправки уведомлений разными каналами:

// Интерфейс
public interface NotificationService {
    void send(String message);
}

// Реализация для Email
@Component
public class EmailNotificationService implements NotificationService {
    @Override
    public void send(String message) {
        System.out.println("Sending email: " + message);
    }
}

// Реализация для SMS
@Component
public class SMSNotificationService implements NotificationService {
    @Override
    public void send(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

// Factory для выбора правильного сервиса
@Service
public class NotificationFactory {
    private final Map<String, NotificationService> services;
    
    public NotificationFactory(
        EmailNotificationService emailService,
        SMSNotificationService smsService) {
        this.services = Map.of(
            "email", emailService,
            "sms", smsService
        );
    }
    
    public NotificationService getService(String type) {
        NotificationService service = services.get(type);
        if (service == null) {
            throw new IllegalArgumentException("Unknown notification type: " + type);
        }
        return service;
    }
}

// Использование
@Service
public class UserService {
    private final NotificationFactory notificationFactory;
    
    @Autowired
    public UserService(NotificationFactory factory) {
        this.notificationFactory = factory;
    }
    
    public void registerUser(User user) {
        // ...
        NotificationService service = notificationFactory.getService("email");
        service.send("Welcome, " + user.getName());
    }
}

Использование FactoryBean интерфейса

Spring предоставляет специальный интерфейс FactoryBean, который используется для создания сложных объектов:

// Интерфейс
public interface ComplexObject {
    void doSomething();
}

// Реализация
public class ComplexObjectImpl implements ComplexObject {
    public void doSomething() {
        System.out.println("Doing something complex");
    }
}

// Factory Bean — создатель объектов
public class ComplexObjectFactoryBean implements FactoryBean<ComplexObject> {
    
    @Override
    public ComplexObject getObject() throws Exception {
        // Сложная логика создания
        ComplexObjectImpl obj = new ComplexObjectImpl();
        System.out.println("Creating ComplexObject with initialization...");
        return obj;
    }
    
    @Override
    public Class<?> getObjectType() {
        return ComplexObject.class;
    }
    
    @Override
    public boolean isSingleton() {
        return true; // Bean будет singleton
    }
}

// Регистрация в конфигурации
@Configuration
public class Config {
    
    @Bean
    public FactoryBean<ComplexObject> complexObjectFactory() {
        return new ComplexObjectFactoryBean();
    }
}

// Использование — Spring автоматически вызовет getObject()
@Service
public class MyService {
    private final ComplexObject complexObject;
    
    @Autowired
    public MyService(ComplexObject complexObject) {
        this.complexObject = complexObject; // Это уже созданный объект
    }
}

ObjectFactory и ObjectProvider

Если нужно создавать Bean на лету, используют ObjectFactory или ObjectProvider:

@Service
public class LazyBeanCreator {
    private final ObjectFactory<ExpensiveService> serviceFactory;
    
    @Autowired
    public LazyBeanCreator(ObjectFactory<ExpensiveService> factory) {
        this.serviceFactory = factory;
    }
    
    public void useService() {
        // Сервис создается только здесь, при вызове getObject()
        ExpensiveService service = serviceFactory.getObject();
        service.doWork();
    }
}

// ObjectProvider более гибкий
@Service
public class FlexibleBeanCreator {
    private final ObjectProvider<OptionalService> serviceProvider;
    
    @Autowired
    public FlexibleBeanCreator(ObjectProvider<OptionalService> provider) {
        this.serviceProvider = provider;
    }
    
    public void useService() {
        // Если Bean есть — используем его, нет — игнорируем
        serviceProvider.ifAvailable(service -> service.doWork());
    }
}

Сравнение подходов

ПодходКогда использоватьПлюсыМинусы
@Bean методыПростое создание объектовПросто, явноНужна конфигурация
@ComponentДля обычных компонентовАвтоматическое сканированиеМенее гибко
FactoryBeanСложное создание объектовПолный контроль над логикойСложнее в использовании
ObjectFactoryЛенивое созданиеСоздание по требованиюМожет быть избыточным

Преимущества Factory Method в Spring

  1. Гибкость — легко менять реализацию без изменения кода
  2. Тестируемость — проще создавать mock объекты
  3. Разделение ответственности — логика создания отделена от логики использования
  4. Соответствие SOLID — особенно принципам Open/Closed и Dependency Inversion

Заключение

Factory Method в Spring — это не просто паттерн проектирования, это основной механизм создания и управления объектами. Понимание различных способов его применения — критично для разработки масштабируемых приложений. От простых @Bean методов до сложных FactoryBean реализаций, этот паттерн позволяет писать гибкий и поддерживаемый код.

Как работает паттерн Factory Method в Spring? | PrepBro