Создаётся ли один экземпляр бина @Component, если он внедряется через @Autowired в несколько других бинов?

Ответ

Да, по умолчанию создаётся один экземпляр (singleton). Все поля, аннотированные @Autowired и ссылающиеся на бин с @Component, получат ссылку на один и тот же объект в контексте Spring.

Пример:

@Component // Spring создаст ОДИН экземпляр этого класса
public class CommonService {
    // ...
}

@Service
public class OrderService {
    @Autowired // Ссылка на единственный экземпляр CommonService
    private CommonService commonService;
}

@Controller
public class UserController {
    @Autowired // Ссылка на тот же самый экземпляр CommonService
    private CommonService commonService;
}

Области видимости (Scope) бинов: Поведение по умолчанию можно изменить с помощью аннотации @Scope:

  • @Scope("singleton") (по умолчанию): Один бин на IoC-контейнер.
  • @Scope("prototype"): Новый экземпляр при каждом запросе (через getBean() или инъекцию).
  • @Scope("request"), @Scope("session"): Бин привязан к жизненному циклу HTTP-запроса или сессии (в веб-приложениях).

Почему singleton по умолчанию? Это обеспечивает эффективное использование памяти, отсутствие накладных расходов на создание объектов и безопасность в многопоточном окружении, если бин является stateless.

Ответ 18+ 🔞

А, слушай, вот это вообще классика, про которую все постоянно пиздят, но не все до конца въезжают. Ну, типа, представь: ты написал класс, пометил его @Component и думаешь — о, класс, теперь у меня их будет столько, сколько надо. А Spring такой: «Не, мудила, погоди, я тебе щас объясню, как оно работает на самом деле».

По умолчанию, блядь, Spring создаёт один-единственный экземпляр твоего бина на весь свой контейнер. Это называется singleton, ёпта. И все, кто на него ссылаются через @Autowired, получают в морду одну и ту же ссылку на этот самый объект. Не копию, не новый, а именно ту самую, оригинальную, блядь, сущность.

Вот смотри, как это выглядит в коде, чтоб не быть голословным:

@Component // Вот тут Spring видит эту аннотацию и говорит: "О, так, щас я из этого сделаю ОДНУ штуку на весь свой огород".
public class CommonService {
    // ... тут какая-то логика, неважно
}

@Service
public class OrderService {
    @Autowired // И вот сюда он подсовывает ссылку на ту самую, единственную CommonService
    private CommonService commonService;
}

@Controller
public class UserController {
    @Autowired // И сюда ТОЖЕ подсовывает ссылку на ту же самую, блядь, CommonService. Один объект на всех!
    private CommonService commonService;
}

Получается, что OrderService и UserController работают с одним и тем же объектом CommonService. Если в одном месте его состояние поменяли — во всех остальных местах оно тоже поменялось, потому что это один и тот же экземпляр в памяти. Вот такая, блядь, магия, а не dependency injection.

Но! — всегда есть это ёбаное «но» — если тебе такая всеобщая коммуналка не нравится, можно изменить поведение. Для этого есть аннотация @Scope, которая как раз и определяет, сколько этих экземпляров будет плодиться:

  • @Scope("singleton") — это как раз то самое, что стоит по умолчанию. Одна штука на весь контейнер. Экономия памяти, все дела.
  • @Scope("prototype") — а вот это уже интереснее. Тут Spring каждый раз, когда ты просишь этот бин (через getBean() или инъекцию), создаёт новый, свеженький экземпляр. Как одноразовые стаканчики — попользовался и выкинул.
  • @Scope("request"), @Scope("session") — это уже для веб-приложений. Здесь бин живёт ровно столько, сколько живёт HTTP-запрос или пользовательская сессия. Умрёт запрос — умрёт и бин. Закроется сессия — и ему пиздец.

А почему, собственно, singleton — дефолт? Да потому что это, блядь, эффективно! Не надо каждый раз тратить ресурсы на создание нового объекта, особенно если он stateless (то есть не хранит никакого состояния, связанного с конкретным запросом или пользователем). Один раз создал — и все им пользуются. И в многопоточке с ним проблем нет, если он, опять же, не тащит в себе какого-то изменяемого состояния на всех. Просто, быстро, надёжно. Ну, почти всегда.