Приведи пример использования bean scope prototype
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Bean Scope Prototype в Spring: примеры использования
Scope prototype — это один из самых важных, но часто неправильно используемых scopes в Spring. Давайте разберемся, когда и почему его нужно использовать.
Что такое prototype scope?
По умолчанию Spring создает singleton beans — один экземпляр на всё приложение:
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
UserService service1 = context.getBean(UserService.class);
UserService service2 = context.getBean(UserService.class);
service1 == service2 // true, один и тот же объект!
Prototype scope создает новый экземпляр каждый раз:
@Component
@Scope(BeanScope.PROTOTYPE)
public class UserRequest {
private String userId;
private long createdAt;
}
UserRequest req1 = context.getBean(UserRequest.class);
UserRequest req2 = context.getBean(UserRequest.class);
req1 == req2 // false, разные объекты!
Проблема: кто несет ответственность за очистку?
Singleton — Spring управляет полным жизненным циклом.
Prototype — Spring создает, но НЕ уничтожает. Это критичное отличие!
Пример 1: Stateful объекты с временными данными
Проблема: используете singleton для объекта с состоянием
@Component
public class ReportGenerator {
private String currentUserId; // Опасно! Общее для всех
private List<String> data = new ArrayList<>();
public void generateReport(String userId) {
this.currentUserId = userId; // Race condition!
}
}
Решение: использовать prototype
@Component
@Scope(BeanScope.PROTOTYPE)
public class ReportGenerator {
private String userId; // Безопасно — свой для каждого
private List<String> data = new ArrayList<>();
public void generateReport(String userId) {
this.userId = userId;
}
}
Пример 2: HTTP Request обработчик
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext {
private String requestId;
private long startTime;
private String userId;
public RequestContext() {
this.requestId = UUID.randomUUID().toString();
this.startTime = System.currentTimeMillis();
}
}
@RestController
public class OrderController {
@Autowired
private RequestContext requestContext;
@PostMapping("/orders")
public void createOrder(@RequestBody OrderRequest request) {
System.out.println("Request ID: " + requestContext.getRequestId());
}
}
Пример 3: Batch processor с очисткой ресурсов
@Component
@Scope(BeanScope.PROTOTYPE)
public class BatchProcessor implements DisposableBean {
private List<String> items;
private File tempFile;
public BatchProcessor() {
this.items = new ArrayList<>();
try {
this.tempFile = File.createTempFile("batch_", ".tmp");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void process(List<String> data) {
this.items.addAll(data);
}
@Override
public void destroy() {
items.clear();
if (tempFile != null && tempFile.exists()) {
tempFile.delete();
}
}
}
public class BatchService {
@Autowired
private ObjectProvider<BatchProcessor> batchProcessorProvider;
public void processBatch(List<String> data) {
BatchProcessor processor = batchProcessorProvider.getObject();
try {
processor.process(data);
} finally {
processor.destroy();
}
}
}
Пример 4: Injection prototype в singleton (правильно)
Проблема: если inject prototype в singleton, может не создать новый
@Component
public class UserService {
@Autowired
private UserRequest request; // Создастся один раз!
}
Решение 1: ObjectProvider
@Component
public class UserService {
@Autowired
private ObjectProvider<UserRequest> requestProvider;
public void process() {
UserRequest request = requestProvider.getObject(); // Новый каждый раз
}
}
Решение 2: Lookup method injection
@Component
public class UserService {
@Lookup
protected UserRequest createRequest() {
return null;
}
public void process() {
UserRequest request = createRequest(); // Новый каждый раз
}
}
Пример 5: Генератор уникальных ID
@Component
@Scope(BeanScope.PROTOTYPE)
public class UniqueIdGenerator {
private final String prefix;
private final Random random = new Random();
public UniqueIdGenerator() {
this.prefix = UUID.randomUUID().toString().substring(0, 8);
}
public String generate() {
return prefix + "_" + random.nextLong();
}
}
@Service
public class OrderService {
@Autowired
private ObjectProvider<UniqueIdGenerator> generatorProvider;
public String createOrder(OrderRequest request) {
UniqueIdGenerator idGen = generatorProvider.getObject();
String orderId = idGen.generate();
return orderId;
}
}
Когда использовать prototype
Используйте prototype:
- Объект содержит mutable state (изменяемое состояние)
- Нужны независимые экземпляры для разных контекстов
- Объект содержит ресурсы, которые нужно очищать
- Thread-unsafe классы
НЕ используйте prototype:
- Stateless сервисы (UserService, ProductService)
- Дорогие объекты (БД подключения, HTTP клиенты)
- Когда нужна синхронизация состояния
Вывод
Prototype scope создает новый экземпляр bean'а каждый раз, что важно для stateful объектов, thread-safety и resource management. Используйте ObjectProvider или @Lookup для injection prototype beans в singleton компоненты.