← Назад к вопросам
Как настроить Spring для создания нового экземпляра бина @Component класса, где он используется через @Autowired
2.2 Middle🔥 231 комментариев
#REST API и микросервисы#Spring Boot и Spring Data#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Ответ
По умолчанию Spring создаёт один экземпляр (singleton) каждого @Component класса и переиспользует его везде. Если нужен новый экземпляр каждый раз — нужно изменить scope на PROTOTYPE.
1. Аннотация @Scope("prototype")
Это основной способ — каждый @Autowired получит новый экземпляр:
@Component
@Scope("prototype")
public class UserService {
private String sessionId = UUID.randomUUID().toString();
public void process() {
System.out.println("Processing with session: " + sessionId);
}
}
@Component
public class OrderProcessor {
@Autowired
private UserService userService1;
@Autowired
private UserService userService2;
public void process() {
userService1.process(); // sessionId_1
userService2.process(); // sessionId_2 (разные!)
}
}
2. Использование ScopedProxyMode
Если нужен prototype внутри singleton бина, используйте proxyMode:
@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext {
private String requestId;
public RequestContext() {
this.requestId = UUID.randomUUID().toString();
}
public String getRequestId() {
return requestId;
}
}
@Component
public class RequestHandler {
@Autowired
private RequestContext requestContext; // новый для каждого использования
public void handleRequest() {
System.out.println("Request ID: " + requestContext.getRequestId());
}
}
3. Использование ObjectFactory
Для более явного управления созданием новых экземпляров:
@Component
public class DataService {
private String dataId = UUID.randomUUID().toString();
public void loadData() {
System.out.println("Loading data: " + dataId);
}
}
@Component
public class DataProcessor {
@Autowired
private ObjectFactory<DataService> dataServiceFactory;
public void process() {
// Каждый вызов getObject() создаёт новый экземпляр
DataService service1 = dataServiceFactory.getObject();
DataService service2 = dataServiceFactory.getObject();
service1.loadData(); // dataId_1
service2.loadData(); // dataId_2
}
}
4. Использование Provider (Javax injection)
import javax.inject.Provider;
import javax.inject.Singleton;
@Component
public class ReportService {
private String reportId = UUID.randomUUID().toString();
public void generateReport() {
System.out.println("Report ID: " + reportId);
}
}
@Component
public class ReportGenerator {
@Autowired
private Provider<ReportService> reportServiceProvider;
public void generate() {
ReportService service1 = reportServiceProvider.get();
ReportService service2 = reportServiceProvider.get();
service1.generateReport(); // reportId_1
service2.generateReport(); // reportId_2
}
}
5. Использование метода-фабрики @Bean
Если компонент создаётся в конфиге:
@Configuration
public class AppConfig {
@Bean
@Scope("prototype")
public PaymentProcessor paymentProcessor() {
return new PaymentProcessor(UUID.randomUUID().toString());
}
}
public class PaymentProcessor {
private String processorId;
public PaymentProcessor(String processorId) {
this.processorId = processorId;
}
public void process() {
System.out.println("Processing with: " + processorId);
}
}
@Component
public class OrderService {
@Autowired
private ObjectFactory<PaymentProcessor> processorFactory;
public void processOrder() {
PaymentProcessor processor = processorFactory.getObject();
processor.process();
}
}
6. Сравнение Scopes
// SINGLETON (по умолчанию) — один на всё приложение
@Component // это SINGLETON
public class AppConfig {}
// PROTOTYPE — новый для каждого @Autowired
@Component
@Scope("prototype")
public class SessionData {}
// REQUEST — новый для каждого HTTP запроса (только web)
@Component
@Scope("request")
public class RequestData {}
// SESSION — новый для каждой HTTP сессии (только web)
@Component
@Scope("session")
public class UserSession {}
// APPLICATION — один на сервлет контекст (только web)
@Component
@Scope("application")
public class ApplicationData {}
7. Полный пример с prototype
@Component
@Scope("prototype")
public class Task {
private String taskId;
private LocalDateTime createdAt;
public Task() {
this.taskId = UUID.randomUUID().toString();
this.createdAt = LocalDateTime.now();
}
public void execute() {
System.out.println("Executing task: " + taskId + " at " + createdAt);
}
}
@Component
public class TaskManager {
@Autowired
private ObjectFactory<Task> taskFactory;
public void executeTasks() {
// Каждый раз новый экземпляр Task
Task task1 = taskFactory.getObject();
Task task2 = taskFactory.getObject();
task1.execute();
task2.execute();
// taskId и createdAt будут разными
}
}
8. Проблема: Prototype в Singleton
При инъекции prototype в singleton требуется proxyMode:
// НЕПРАВИЛЬНО — singleton получит одного prototype
@Component
public class SingletonService {
@Autowired
private PrototypeService proto; // всегда один и тот же экземпляр!
}
// ПРАВИЛЬНО — используйте ObjectFactory
@Component
public class SingletonService {
@Autowired
private ObjectFactory<PrototypeService> protoFactory;
public void usePrototype() {
PrototypeService proto1 = protoFactory.getObject();
PrototypeService proto2 = protoFactory.getObject();
// Это разные экземпляры!
}
}
// ИЛИ используйте @Scope с proxyMode
@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeService {
}
@Component
public class SingletonService {
@Autowired
private PrototypeService proto; // proxy будет создавать новые экземпляры
}
Когда использовать Prototype?
- Stateful объекты: каждый нуждается в своём состоянии
- Session-specific данные: разные для каждого запроса
- Временные объекты: создаются и выбрасываются
- Потокобезопасность: избежать синхронизации
Правило выбора
- Singleton — по умолчанию, для stateless сервисов
- Prototype — для stateful компонентов
- ObjectFactory/Provider — для контролируемого создания
Основное правило: используйте @Scope("prototype") для класса, и Spring будет создавать новый экземпляр каждый раз при инъекции.