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

Как часто создается Bean с Scope("prototype") в Spring

2.3 Middle🔥 261 комментариев
#Spring Framework

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

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

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

Scope("prototype") в Spring: Частота создания Bean

Bean с scope "prototype" создается каждый раз, когда на него запрашивается ссылка через dependency injection или явный вызов getBean(). Это кардинально отличается от scope "singleton", где создается только один экземпляр на всё приложение.

Определение Prototype Scope

// PROTOTYPE: каждый вызов создает НОВЫЙ bean
@Component
@Scope("prototype")
public class PrototypeService {
    public void doSomething() {
        System.out.println("Working...");
    }
}

// SINGLETON: один экземпляр на всё приложение (по умолчанию)
@Component
@Scope("singleton")
public class SingletonService {
    public void doSomething() {
        System.out.println("Working...");
    }
}

Частота создания: примеры

Пример 1: Dependency Injection

@Component
public class MyController {
    // Каждый раз, когда MyController создается, создается НОВЫЙ PrototypeService
    @Autowired
    private PrototypeService prototypeService;
    
    // Этот service создается ровно ОДИН раз на приложение
    @Autowired
    private SingletonService singletonService;
}

// Результаты
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");

MyController controller1 = context.getBean(MyController.class);
// Создан 1 PrototypeService (для controller1)
// Создан 1 SingletonService (один на всё приложение)

MyController controller2 = context.getBean(MyController.class);
// Создан 1 PrototypeService (для controller2) — НОВЫЙ, отличается от первого
// SingletonService переиспользуется (тот же, что и для controller1)

controller1.prototypeService == controller2.prototypeService;  // false
controller1.singletonService == controller2.singletonService;  // true

Пример 2: Прямой вызов getBean()

@Configuration
public class AppConfig {
    @Bean
    @Scope("prototype")
    public DataProcessor dataProcessor() {
        return new DataProcessor();
    }
}

public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        
        // КАЖДЫЙ getBean создает НОВЫЙ объект
        DataProcessor processor1 = context.getBean(DataProcessor.class);
        DataProcessor processor2 = context.getBean(DataProcessor.class);
        DataProcessor processor3 = context.getBean(DataProcessor.class);
        
        // Все три — разные объекты в памяти!
        processor1 == processor2;  // false
        processor2 == processor3;  // false
        processor1 == processor3;  // false
        
        System.out.println(processor1.hashCode());  // например 12345
        System.out.println(processor2.hashCode());  // например 67890
        System.out.println(processor3.hashCode());  // например 24680
    }
}

Пример 3: В циклах

@Component
public class RequestHandler {
    @Autowired
    private PrototypeService prototypeService;  // Создается при инъекции
    
    public void processRequests() {
        // prototypeService — один и тот же объект, внедренный один раз
        for (int i = 0; i < 100; i++) {
            prototypeService.process();  // Один и тот же объект используется
        }
    }
}

// Для получения НОВОГО объекта в цикле:
public class RequestHandler {
    @Autowired
    private ApplicationContext context;
    
    public void processRequests() {
        for (int i = 0; i < 100; i++) {
            // Каждый раз новый объект!
            PrototypeService service = context.getBean(PrototypeService.class);
            service.process();
        }
    }
}

Сравнение Singleton vs Prototype

@Component
@Scope("prototype")
public class PrototypeExample {
    private UUID id = UUID.randomUUID();
    
    public UUID getId() {
        return id;
    }
}

@Component  // По умолчанию singleton
public class SingletonExample {
    private UUID id = UUID.randomUUID();
    
    public UUID getId() {
        return id;
    }
}

// Тестирование
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// SINGLETON
SingletonExample s1 = context.getBean(SingletonExample.class);
SingletonExample s2 = context.getBean(SingletonExample.class);
SingletonExample s3 = context.getBean(SingletonExample.class);

System.out.println(s1.getId());  // UUID-1
System.out.println(s2.getId());  // UUID-1 (один и тот же объект)
System.out.println(s3.getId());  // UUID-1 (один и тот же объект)

// PROTOTYPE
PrototypeExample p1 = context.getBean(PrototypeExample.class);
PrototypeExample p2 = context.getBean(PrototypeExample.class);
PrototypeExample p3 = context.getBean(PrototypeExample.class);

System.out.println(p1.getId());  // UUID-A (новый объект)
System.out.println(p2.getId());  // UUID-B (новый объект)
System.out.println(p3.getId());  // UUID-C (новый объект)

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

Сценарий 1: Stateful объекты с уникальным состоянием

@Component
@Scope("prototype")
public class RequestContext {
    private String userId;      // Уникален для каждого запроса
    private Map<String, Object> data = new HashMap<>();
    
    public void setUserId(String userId) {
        this.userId = userId;
    }
    
    public String getUserId() {
        return userId;
    }
}

// Использование
@RestController
public class UserController {
    @Autowired
    private ApplicationContext context;
    
    @PostMapping("/users/{id}")
    public void getUser(@PathVariable String id) {
        // Каждый запрос получает свой RequestContext
        RequestContext ctx = context.getBean(RequestContext.class);
        ctx.setUserId(id);
        // ...
    }
}

Сценарий 2: Дорогостоящие ресурсы

@Component
@Scope("prototype")
public class ReportGenerator {
    public ReportGenerator() {
        System.out.println("Инициализация ReportGenerator");
    }
    
    public void generate() {
        System.out.println("Генерируем отчет");
    }
}

// Каждый раз создается новый генератор
ReportGenerator generator1 = context.getBean(ReportGenerator.class);
ReportGenerator generator2 = context.getBean(ReportGenerator.class);
// Выведет дважды: "Инициализация ReportGenerator"

Сценарий 3: Thread-safe объекты в многопоточности

@Component
@Scope("prototype")
public class TaskProcessor {
    private List<Task> tasks = new ArrayList<>();  // Уникален для каждого потока
    
    public void addTask(Task task) {
        tasks.add(task);
    }
    
    public void processTasks() {
        for (Task task : tasks) {
            // Процесс
        }
    }
}

// Каждый поток получает свой TaskProcessor
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
    executor.submit(() -> {
        TaskProcessor processor = context.getBean(TaskProcessor.class);
        processor.processTasks();
    });
}

Lifecycle Callbacks в Prototype

@Component
@Scope("prototype")
public class PrototypeBean {
    @PostConstruct
    public void init() {
        System.out.println("Bean инициализирован");
    }
    
    @PreDestroy
    public void destroy() {
        System.out.println("Bean уничтожен");
    }
}

// ВАЖНО: @PreDestroy НЕ вызывается автоматически для prototype beans!
// Spring создает bean, но НЕ отслеживает его для уничтожения
// Вы сами должны управлять жизненным циклом

PrototypeBean bean1 = context.getBean(PrototypeBean.class);
// Выведет: "Bean инициализирован"

PrototypeBean bean2 = context.getBean(PrototypeBean.class);
// Выведет: "Bean инициализирован" (новый bean)

// @PreDestroy будет вызван ТОЛЬКО если вы явно закрываете контекст
((ConfigurableApplicationContext) context).close();
// Может вывести @PreDestroy, но это не гарантировано

Производительность: Singleton vs Prototype

// SINGLETON — быстро (переиспользование)
@Component
public class SingletonService {}  // Создается один раз при startup

// PROTOTYPE — медленнее (создание при каждом использовании)
@Component
@Scope("prototype")
public class PrototypeService {}  // Создается каждый раз

// Бенчмарк
long start = System.nanoTime();
for (int i = 0; i < 1_000_000; i++) {
    context.getBean(SingletonService.class);  // ~100ms
    context.getBean(PrototypeService.class);  // ~3000ms (медленнее в 30 раз!)
}

Правила использования

// ПРАВИЛО 1: Singleton (по умолчанию) для stateless сервисов
@Component
public class UserService {  // Один экземпляр на приложение
    public User getUser(String id) {  // Без состояния
        // ...
    }
}

// ПРАВИЛО 2: Prototype для stateful объектов
@Component
@Scope("prototype")
public class RequestContext {  // Новый для каждого запроса
    private String userId;  // Состояние
    private Map<String, Object> data;
}

// ПРАВИЛО 3: Осторожнее с производительностью
// Prototype создается каждый раз — тяжелые операции замедляют приложение

Итог

Bean с scope "prototype" создается КАЖДЫЙ раз, когда запрашивается новый экземпляр через dependency injection или getBean(). Это противоположно scope "singleton", где создается единственный экземпляр на всё приложение. Prototype полезен для stateful объектов с уникальным состоянием на запрос/поток, но требует осторожности с производительностью, так как создание новых объектов дороже, чем переиспользование существующего.

Как часто создается Bean с Scope("prototype") в Spring | PrepBro