← Назад к вопросам
В чем разница между 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; // Безопасно: каждый запрос имеет свои данные
}
}
Таблица сравнения
| Характеристика | Singleton | Prototype |
|---|---|---|
| Количество инстанций | 1 | Новая при каждом запросе |
| Сохранение в контейнере | Да | Нет (после создания) |
| Память | Низко | Высоко |
| Thread-safety | Нужна для shared state | Не нужна (изолирован) |
| Инициализация | При старте приложения | При каждом запросе |
| @PreDestroy вызов | Да | Нет |
| Best practice | Stateless | Stateful |
Реальные примеры
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();
}
}
Важные замечания
- 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(); // Новая инстанция каждый раз
}
}
-
Request scope — отдельный scope для web приложений, специфичный для каждого HTTP запроса
-
Session scope — сохраняется на время сессии пользователя
Вывод
- Singleton — основной выбор, экономит ресурсы, требует осторожности с shared state
- Prototype — для stateful, request-specific объектов
- Большинство бинов должны быть singleton'ами
- Prototype использовать осознанно, когда действительно нужна новая инстанция