Что такое scope «prototype» в Spring и приведи пример его использования?

Ответ

В Spring scope (область видимости) "prototype" означает, что контейнер создает новый экземпляр бина при каждом запросе (через getBean() или инъекцию зависимости). Это противоположность scope "singleton" (по умолчанию), где контейнер возвращает один и тот же экземпляр.

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

  • Когда состояние бина не должно разделяться между разными частями приложения.
  • Для stateful-бинов (содержащих изменяемое состояние, специфичное для клиента).
  • Когда бин не является потокобезопасным и используется в многопоточной среде.

Пример объявления prototype-бина:

  1. С помощью аннотации @Scope:

    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    @Component
    @Scope("prototype") // Ключевая аннотация
    public class PrototypeService {
        private int id = (int) (Math.random() * 1000); // Уникальное состояние для каждого экземпляра
    
        public int getId() {
            return id;
        }
    }
  2. В XML-конфигурации:

    <bean id="prototypeService" class="com.example.PrototypeService" scope="prototype"/>

Пример использования и проверки:

@RestController
public class TestController {
    // При каждой инъекции будет создан НОВЫЙ экземпляр PrototypeService
    @Autowired
    private PrototypeService service1;

    @Autowired
    private PrototypeService service2;

    @GetMapping("/test")
    public String test() {
        // service1 и service2 — разные объекты с разными id
        return "Service1 ID: " + service1.getId() +
               ", Service2 ID: " + service2.getId();
        // Вывод будет примерно: "Service1 ID: 742, Service2 ID: 319"
    }
}

Важные замечания:

  • Spring не управляет полным жизненным циклом prototype-бинов после их создания и передачи клиенту. Метод уничтожения (@PreDestroy) не будет вызван контейнером.
  • Использование prototype для всех бинов может негативно сказаться на производительности из-за постоянного создания объектов.
  • Для внедрения prototype-бина в singleton-бин часто используется режим инъекции Lookup Method или Provider<>, чтобы избежать фиксации одного экземпляра.

Ответ 18+ 🔞

А, ну вот, прототипы в Спринге! Слушай, это ж классика, как "Муму" у Тургенева, только без утоплений, блядь.

Короче, представь: есть у тебя scope "singleton" — это как Герасим, один на весь двор, всех обслуживает, и хоть ты его в десять мест инжектни, он один и тот же мужик. А "prototype" — это уже другой коленкор, ёпта. Это когда каждый раз, как ты просишь бин, тебе выдают нового Герасима, свежесозданного, блядь. И у каждого свой уникальный номер, как у зэка, а не общая судьба на всех.

Когда это пиздец как нужно:

  • Когда твой бин — это хитрая жопа с состоянием, которое нельзя шарить между всеми подряд.
  • Для этих самых stateful-штук, где у каждого клиента своя отдельная песочница.
  • Если бин не потокобезопасный, а вокруг него мартышлюшки-потоки носятся, как угорелые. Чтобы они друг другу в тарелку не срали.

Как его объявить, этого франта:

  1. Через аннотацию @Scope — просто и элегантно:

    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    @Component
    @Scope("prototype") // Вот эта хуйня и делает магию!
    public class PrototypeService {
        private int id = (int) (Math.random() * 1000); // Каждому новому — свой рандомный ID, как паспорт!
    
        public int getId() {
            return id;
        }
    }
  2. В XML — для староверов и ценителей старой школы:

    <bean id="prototypeService" class="com.example.PrototypeService" scope="prototype"/>

А вот как это выглядит в деле, на примере контроллера:

@RestController
public class TestController {
    // Тут при каждой инъекции Spring, сука, рожает нового!
    @Autowired
    private PrototypeService service1;

    @Autowired
    private PrototypeService service2;

    @GetMapping("/test")
    public String test() {
        // service1 и service2 — это два разных чувака, у них ID разные!
        return "Service1 ID: " + service1.getId() +
               ", Service2 ID: " + service2.getId();
        // Выдаст что-то вроде: "Service1 ID: 742, Service2 ID: 319" — иди проверь!
    }
}

Но есть, блядь, важные нюансы, чтоб тебя не накрыло медным тазом:

  • Spring, создав prototype-бин и отдав его тебе, дальше забивает хуй на его жизненный цикл. Аннотация @PreDestroy? Не, не слышал. Контейнер её вызывать не будет, это твои проблемы, чувак.
  • Если начнёшь всё подряд делать prototype, производительность ебнешь — представь, если бы на каждый чих тебе нового Герасима из-под лестницы доставали. Заебёшься.
  • Особый цирк начинается, когда нужно воткнуть prototype-бин в singleton-бин. Если просто заинжектить, singleton схватит одного прототипа при своём рождении и будет с ним до гроба. Чтобы этого не было, используют Provider<> или Lookup Method — это такие костыли, чтобы каждый раз выпрашивать свеженький экземпляр. Ёперный театр, но работает!

Вот и вся философия. Либо один на всех, либо каждому — свой, блядь. Выбирай, но думай головой, а не жопой.