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

Приведи пример использования bean scope prototype

2.0 Middle🔥 111 комментариев
#Другое

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

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

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

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 компоненты.

Приведи пример использования bean scope prototype | PrepBro