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

В чем разница между Singleton и Prototype scope?

2.0 Middle🔥 121 комментариев
#Spring Framework

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

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

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

Разница между Singleton и Prototype scope в Spring

Это два противоположных подхода к управлению жизненным циклом бинов в Spring контейнере. Давайте разберёмся в деталях.

Singleton Scope (по умолчанию)

Singleton создаётся один раз на всё приложение и переиспользуется.

@Configuration
public class BeanConfig {
    @Bean
    @Scope("singleton") // или просто @Bean без указания
    public DatabaseConnection singletonConnection() {
        return new DatabaseConnection();
    }
}

@Service
public class UserService {
    @Autowired
    private DatabaseConnection connection; // одна и та же инстанция для всех
}

Характеристики:

  • Создаётся один раз при запуске приложения
  • Хранится в Spring контейнере
  • Переиспользуется всеми бинами, которые его запрашивают
  • Thread-safe операции — ответственность разработчика
  • Экономит память
// Демонстрация
@SpringBootApplication
public class SingletonDemo {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SingletonDemo.class);
        
        MyService service1 = context.getBean(MyService.class);
        MyService service2 = context.getBean(MyService.class);
        
        System.out.println(service1 == service2); // true — один и тот же объект
    }
}

Когда использовать:

  • Stateless сервисы (BusinessLogicService, UserRepository)
  • Дорогостоящие объекты (DataSource, EntityManagerFactory)
  • Конфигурационные бины
  • Shared ресурсы (thread pool, cache)

Prototype Scope

Prototype создаёт новый экземпляр каждый раз, когда бин запрашивается.

@Configuration
public class BeanConfig {
    @Bean
    @Scope("prototype")
    public RequestContext prototypeContext() {
        return new RequestContext();
    }
}

@Service
public class OrderService {
    @Autowired
    private RequestContext context; // ВНИМАНИЕ: получит новый объект
}

Характеристики:

  • Создаётся новая инстанция при каждом запросе
  • Spring не управляет жизненным циклом после создания
  • Каждый клиент получает свою копию
  • Потребляет больше памяти
  • Идеален для stateful объектов
@SpringBootApplication
public class PrototypeDemo {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(PrototypeDemo.class);
        
        MyService service1 = context.getBean(MyService.class);
        MyService service2 = context.getBean(MyService.class);
        
        System.out.println(service1 == service2); // false — разные объекты
    }
}

Когда использовать:

  • Stateful объекты (HttpRequest, Form model)
  • Объекты с состоянием, специфичным для запроса
  • Временные объекты для обработки
  • Объекты, которые должны быть изолированы друг от друга

Практическое сравнение

@Component
@Scope("singleton")
public class DatabaseConnection {
    private String connectionString = "localhost:5432";
    
    public String getConnection() {
        return connectionString;
    }
    
    public void setConnection(String cs) {
        this.connectionString = cs; // ОПАСНО: изменится для всех!
    }
}

@Component
@Scope("prototype")
public class RequestData {
    private String userId;
    private Map<String, Object> sessionData = new HashMap<>();
    
    public void setUserId(String userId) {
        this.userId = userId; // Безопасно: каждый запрос имеет свои данные
    }
}

Таблица сравнения

ХарактеристикаSingletonPrototype
Количество инстанций1Новая при каждом запросе
Сохранение в контейнереДаНет (после создания)
ПамятьНизкоВысоко
Thread-safetyНужна для shared stateНе нужна (изолирован)
ИнициализацияПри старте приложенияПри каждом запросе
@PreDestroy вызовДаНет
Best practiceStatelessStateful

Реальные примеры

Singleton (правильно):

@Service
public class UserRepository { // Stateless — идеален для singleton
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findById(Long id) {
        return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?", 
            new Object[]{id}, User.class);
    }
}

Prototype (когда нужен):

@Component
@Scope("prototype")
public class ReportGenerator { // Stateful — новый для каждого отчета
    private StringBuilder content = new StringBuilder();
    private List<String> sections = new ArrayList<>();
    
    public void addSection(String section) {
        sections.add(section);
    }
    
    public String generate() {
        sections.forEach(s -> content.append(s).append("\n"));
        return content.toString();
    }
}

Важные замечания

  1. Nested Singleton проблема — singleton содержит prototype
@Service // singleton
public class MyService {
    @Autowired
    private PrototypeBean proto; // Получит ОДНУ инстанцию при создании MyService!
}
// Решение: использовать ObjectProvider

@Service
public class MyService {
    @Autowired
    private ObjectProvider<PrototypeBean> protoProvider;
    
    public void process() {
        PrototypeBean proto = protoProvider.getObject(); // Новая инстанция каждый раз
    }
}
  1. Request scope — отдельный scope для web приложений, специфичный для каждого HTTP запроса

  2. Session scope — сохраняется на время сессии пользователя

Вывод

  • Singleton — основной выбор, экономит ресурсы, требует осторожности с shared state
  • Prototype — для stateful, request-specific объектов
  • Большинство бинов должны быть singleton'ами
  • Prototype использовать осознанно, когда действительно нужна новая инстанция