Для чего нужен prototype bean

Обновлено: 02.07.2024

Область применения не-синглтон прототипа (prototype) развертывания компонента приводит к созданию нового экземпляра компонента каждый раз, когда делается запрос на этот конкретный компонент. Таким образом, компонент вводится в другой компонент, или вы запрашиваете его с помощью вызова метода getBean() для контейнера. Как правило, вы должны использовать область действия прототипа для всех bean-компонентов с состоянием и единственную (singleton) область для bean-компонентов без состояния.

В следующем примере бин определяется как прототип в XML:

В отличие от других областей, Spring не управляет полным жизненным циклом прототипа bean-компонента. Контейнер создает, настраивает и иным образом собирает объект-прототип и передает его клиенту без дальнейшей записи этого экземпляра-прототипа. Таким образом, хотя методы обратного вызова жизненного цикла инициализации вызываются для всех объектов независимо от области действия, в случае прототипов настроенные обратные вызовы жизненного цикла уничтожения не вызываются. Клиентский код должен очистить объекты в области прототипов и освободить дорогие ресурсы, которые содержатся в прототипах. Чтобы заставить контейнер Spring высвобождать ресурсы, содержащиеся в bean-объектах с прототипом, попробуйте использовать собственный постпроцессор bean-компонента, который содержит ссылку на bean-компоненты, которые необходимо очистить.

В некоторых отношениях роль контейнера Spring в отношении bean-объекта с прототипом является заменой new оператора Java. Все управление жизненным циклом после этой точки должно выполняться клиентом.

Singleton компоненты с зависимостями Prototype компонентов

Когда вы используете одноэлементные (singleton) компоненты с зависимостями от прототипов (prototype), имейте в виду, что зависимости разрешаются во время создания экземпляра. Таким образом, если вы вводите зависимый bean-объект prototype в bean-объект синглтона, создается новый bean-объект-прототип и затем внедряется зависимость в синглтон-компонент. Экземпляр прототипа является единственным экземпляром, который когда-либо поставляется в бин с singleton областью действия.

Однако предположим, что вы хотите, чтобы bean-объект с singleton областью многократно получал новый экземпляр bean-объекта prototype во время выполнения. Вы не можете внедрить bean-компонент prototype в ваш синглтон-компонент, поскольку это внедрение происходит только один раз, когда контейнер Spring создает экземпляр синглтон-компонента, а также разрешает и внедряет его зависимости.

Урок 2: Введение в Spring IoC контейнер

Этот урок освещает работу с Spring Framework IoC контейнером и основан на оригинальной документации §5. The IoC container.

Области видимости(scopes) бинов

Когда вы создаете определение бинов, вы вы создаете рецепт для создания экземпляров класса, который определяет бин. Важно понять, что определение бинов является рецептом, потому что он означает, какого класса вы можете создать множество экземпляров по этому рецепту.

Вы можете контролировать не только какие зависимости и значения конфигурации вы можете подключить в объекте, который создан из определения бина, но также область видимости из того же определения бина. Это мощный и гибкий подход, при котором вы можете выбрать область видимости создаваемых объектов. Изначально, Spring Framework поддерживает несколько вариантов, некоторые доступны, только если вы используете web-aware ApplicationContext . Также вы можете создать свою собственную облать видимости. Ниже приведен список областей видимостей, описанных в документации на момент написания урока:

С более подробной информацией о настройке приложения для применения областей видимости request , session , global session и application вы можете ознакомиться в документации. Пример реализации собственной области видимости будет рассмотрено в отдельном уроке.

Для того, чтобы указать область видимости бина, отличный от singleton , необходимо добавить аннотацию @Scope("область_видимости") методу объявления бина или классу с аннотацией @Component :

Использование @Configuration аннотации

Как упоминалось выше, классы с аннотацией @Configuration указывают на то, что они являются источниками определения бинов, public-методов с аннотацией @Bean .

Кода бин имеет зависимость от другого бина, то зависимость выражается просто как вызов метода:

Однако работает такое взаимодействие только в @Configuration -классах, в @Component -классах такое не работает.

Представим теперь ситуацию, когда у вас есть бин с областью видимости singleton , который имеет зависимость от бина с областью видимости prototype .

Большая часть приложений строится по модульной архитектуре, разделенная по слоям, например DAO, сервисы, контроллеры и др. Создавая конфигурацию, можно также её разбивать на составные части, что также улучшит читабельность и панимание архитектуры вашего приложения. Для этого в конфигурацию необходимо добавить аннотацию @Import , в параметрах которой указываются другие классы с аннотацией @Configuration , например:

Таким образом, при инициализации контекста вам не нужно дополнительно указывать загрузку из конфигурации AnotherConfiguration , все останется так, как и было:

В большинстве случаев, имеются такие случаи, когда бин в одной конфигурации имеет зависимость от бина в другой конфигурации. Поскольку конфигурация является источником определения бинов, то разрешить такую зависимость не является проблемой, достаточно объявить поле класса конфигурации с аннотацией @Autowired (более подробно оисано в отдельной главе):

При этом LessonsConfiguration остается без изменений:

Классы с аннотацией @Configuration не стремятся на 100% заменить конфигурации на XML, при этом, если вам удобно или имеется какая-то необходимость в использовании XML конфигурации, то к вашей Java-конфигурации необходимо добавить аннотацию @ImportResource , в параметрах которой необходимо указать нужное вам количество XML-конфигураций. Выглядит это следующим способом:

Описание работы IoC контейнера

Ниже представлена диаграмма, отражающая, как работает Spring. Ваши классы приложения совмещаются с метаданными конфигурации, в результате чего будет создан и инициализирован ApplicationContext , а на выходе вы получите полностью настроенное и готовое к выполнению приложение.


ApplicationContext представляет собой Spring IoC контейнер и необходим для инициализации, настройки и сборки бинов для построения приложения.


В метаданных конфигурации разработчик описывает как инициализировать, настроить IoC контейнер и собрать объекты в вашем приложении. В данном и других уроках этого цикла везде, где возможно, будет использоваться подход на основе аннотаций и Java-конфигурации. Если вы сторонник XML-конфигурации, либо хотите посмотреть как делать тоже самое через XML, обратитесь к оригинальной документации по Spring Framework или соответствующего модуля/проекта.

Что вам потребуется

В чем разница между singleton и прототипом bean?

в основном Боб имеет области, которые определяют их существование в приложении

Singleton: означает определение одного компонента для одного экземпляра объекта на контейнер Spring IOC.

Prototype: означает одно определение bean для любого количества экземпляров объекта.

Так что такое "экземпляр объекта" .

прототип пределы = новый объект создается каждый раз, когда он вводится / просматривается. Он будет использовать new SomeClass() каждый раз.

Singleton scope = (по умолчанию) один и тот же объект возвращается каждый раз, когда он впрыскивается/посмотрел вверх. Здесь он создаст экземпляр одного экземпляра SomeClass и затем вернуть его каждый раз.

давайте просто посмотрим это через код.

Ниже приведен Tenniscoach Bean с по умолчанию singleton объем

Далее идет Боб Теннискоч с прототип объем

Ниже приведен основной класс:

на синглтон область вывода:

на прототип область вывода:

надеюсь, что это ответить ваш вопрос. : D

добавление к вышесказанному..не путайте с Java singleton. согласно спецификации JAVA singleton означает, что только один экземпляр этого компонента будет создан на JVM. но весной singleton означает, что один экземпляр для этого конкретного компонента будет создан для контекста приложения. поэтому, если ваше приложение имеет более одного контекста, вы все равно можете иметь более одного экземпляра для этого компонента.

Singleton Область: С помощью одноэлементной области создается один и только один экземпляр компонента с предоставленным определением компонента и для последующих запросов для того же компонента Spring container возвращает тот же экземпляр .

из весенней документации:

.. когда вы определяете определение bean и оно ограничено как одноэлементное, контейнер Spring IoC создает ровно один экземпляр объекта определяется этим bean определение. Этот единственный экземпляр хранится в кэш таких одноэлементных бобов и всех последующих запросов и ссылки по имени Боб возвращает кэшированный объект.

пример: Допустим, мы определили bean accountDao как показано ниже:

и еще два боба, которые используют это accountDao бин

Весна изначально создаст accountDao Bean и кэшировать его. А потом для someBean а также anotherBean , он обеспечит то же самое пример accountDao .

Примечание: если область не задана с определением bean, Singleton является областью по умолчанию.

Прототип Объем: для области прототипа для каждого запроса для компонента будет создан и возвращен новый экземпляр компонента. Это похоже на вызов новая оператор в Java для класса.

пример: Допустим, мы определили bean accountDao as ниже:

и еще два боба, которые используют это accountDao бин

для someBean и anotherBean Spring вернет два отдельных экземпляра объекта accountDao.

одно важное отличие заключается в том, что для области прототипа Spring не управляет полным жизненным циклом компонента, очистка должна выполняться клиентским кодом.

из весенней документации:

весна не управляет полный жизненный цикл прототипа компонента: контейнер создает экземпляр, настраивает и иным образом собирает a прототип объекта и передает его клиенту без дополнительной записи этого экземпляра прототипа. Таким образом, хотя жизненный цикл инициализации методы обратного вызова вызываются для всех объектов независимо от области, в случай прототипов, настроенные обратные вызовы жизненного цикла уничтожения не являются называемый. Клиентский код должен очистить объекты области прототипа и высвободить дорогостоящие ресурсы, которые прототип bean(s) удерживается.

Singleton: если scope имеет значение singleton, контейнер Spring IoC создает ровно один экземпляр объекта, определенного этим определением компонента. Этот единственный экземпляр хранится в кэше таких одноэлементных компонентов, и все последующие запросы и ссылки на этот именованный компонент возвращают кэшированный объект.

Prototype: если scope имеет значение prototype, контейнер Spring IoC создает новый экземпляр bean объекта каждый раз, когда выполняется запрос для этого конкретного bean. Как правило, используйте область прототипа для всех полных состояний бобов и одноэлементную область для бобов без состояния.

Они оба являются творческими шаблонами дизайна.

Singleton создаст новый экземпляр в первом вызове и вернет его в последующих вызовах.

Prototype будет возвращать новый экземпляр каждый раз.

Я хочу добавить некоторую дополнительную информацию, которая может помочь нам узнать значение "экземпляра объекта" в упомянутых предложениях. Этот абзац из Spring Doc пытается определить "экземпляр объекта":

когда вы создаете определение bean, вы создаете рецепт для создания фактический экземпляры класса, определенного этим определением bean. Идея о том, что определение боба является рецептом, важна, потому что это означает, что, как и в случае с классом, вы может создать много экземпляров объекта из одного рецепта.

поэтому, как упоминалось в предыдущем разделе, каждое определение bean можно рассматривать как класс(с точки зрения объектно-ориентированного). Согласно данным, которые вы определили в нем (например, scope. ) этот класс (или определение компонента) может иметь только один экземпляр объекта (одноэлементная область только одним общим экземпляром) или любое количество экземпляров объекта(например, область прототипа путем создания нового компонента экземпляр каждый раз, когда выполняется запрос для этого конкретного компонента).

Если scope имеет значение singleton, контейнер Spring IoC создает ровно один экземпляр объекта, определенного этим определением компонента. Этот единственный экземпляр хранится в кэше таких одноэлементных компонентов, и все последующие запросы и ссылки на этот именованный компонент возвращают кэшированный объект.

область по умолчанию всегда одноэлементная, однако, когда вам нужен один и только один экземпляр bean, вы можете установить свойство scope в singleton

разницу между Singleton и Prototype-это область прототипа для всех бобов с полным состоянием и область Singleton для бобов без состояния.

Singleton является тем же экземпляром в приложении

Prototype-это новый экземпляр для каждого нового запроса getBean

прототип объем: новый объект создается каждый раз, когда он вводится.
Singleton область: один и тот же объект возвращается каждый раз, когда он вводится.

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

у нас есть другой класс называется TrackCoach, который реализует Coach.

теперь есть интерфейс FortuneService.

он реализован нашим классом HappyFortuneService.

давайте свяжем два класса и введем объект bean одного класса в другой с помощью Xml. Давайте выполним инъекцию зависимостей. Обратите внимание, что мы можем сделать это с помощью аннотаций Java.

обратите внимание, что scope = singleton .

теперь давайте определите наш BeanScopeDemoApp, который имеет наш основной метод.

после запуска вышеуказанного кода Вы увидите следующие результаты:

он указывает на один и тот же объект и занимает одно и то же место памяти после вызова его дважды. Теперь давайте изменим scope = prototype в нашем Xml-файле сохраните его и снова запустите BeanScopeDemoApp.
Вы увидите следующие результаты:

он указывает на другой объект и занимает различные места памяти после вызова дважды. Это была бы графическая иллюстрация того, что я только что сказал.

  1. одноэлементная область по умолчанию.
  2. одноэлементные бобы создаются во время инициализации контекста приложения, и один и тот же Боб возвращается всегда.
  3. Prototype bean создается, когда он когда-либо вызывается. Каждый раз, когда он называется, мы получаем новый объект.

Примечание: компонент с любой областью будет создан, если он упоминается другими компонентами и вызывается с использованием контекста приложения.

пример кода для проверки этого.

этот будет печатать соответствующий текст при вызове конструктора.

для приведенного ниже кода

класс прототипа createdSpring.Prototypeclass@29774679 прототип класс createdSpring.Класс прототипа PrototypeClass@3ffc5af1 createdSpring.PrototypeClass@5e5792a0 класс прототипа createdSpring.PrototypeClass@26653222 класс прототипа createdSpring.PrototypeClass@3532ec19 класс прототипа createdSpring.Prototypeclass@68c4039c прототип класс createdSpring.PrototypeClass@ae45eb6 класс прототипа createdSpring.PrototypeClass@59f99ea класс прототипа createdSpring.PrototypeClass@27efef64 класс прототипа createdSpring.Класс прототипа PrototypeClass@6f7fd0e6 createdSpring.PrototypeClass@47c62251

<bean scope postinfo clearfix">

Spring гарантирует ровно один общий экземпляр bean для данного id на контейнер IoC, в отличие от Java Singletons, где Синглетон жестко кодирует область объекта таким образом, что один и только один экземпляр определенного класса будет когда-либо создан на ClassLoader.

Введение

Inversion of Control (IoC), также известное как Dependency Injection (DI), является процессом, согласно которому объекты определяют свои зависимости, т.е. объекты, с которыми они работают, через аргументы конструктора/фабричного метода или свойства, которые были установлены или возвращены фабричным методом. Затем контейнер inject(далее "внедряет") эти зависимости при создании бина. Этот процесс принципиально противоположен, поэтому и назван Inversion of Control, т.к. бин сам контролирует реализацию и расположение своих зависимостей, используя прямое создание классов или такой механизм, как шаблон Service Locator.

Основными пакетами Spring Framework IoC контейнера являются org.springframework.beans и org.springframework.context . Интерфейс BeanFactory предоставляет механизм конфигурации по управлению любым типом объектов. ApplicationContext - наследует нитерфейс BeanFactory и добавляет более специфичную функциональность. Ниже в таблице представлены различия между ними:

Автоматическая регистрация BeanFactoryPostProcessor

В большинстве случаев предпочтительно использовать ApplicationContext , поэтому в дальнейшем будет использоваться только он и его реализации. Поскольку он включает в себя всю функциональность BeanFactory , его можно и нужно использовать, за исключением случаев, когда приложение запускается на устройствах с ограниченными ресурсами, в которых объем потребляемой памяти может быть критичным, даже в пределах нескольких килобайт, либо когда вы разрабатываете приложение, в котором необходима поддержка совместимости со сторонними библиотеками, использующими JDK 1.4 или не поддерживают JSR-250. Spring Framework активно использует BeanPostProcessor для проксирования и др., поэтому, если вам необходима поддержка такой функциональности, как AOP и транзакций, то при использовании BeanFactory необходимо добавить вручную регистрацию BeanPostProcessor и BeanFactoryPostProcessor , как показано ниже:

Аннотации @Autowired , @Inject , @Resource и @Value обрабатываются Spring реализацией BeanPostProcessor , поэтому вы не можете их применять в своих собственных BeanPostProcessor и BeanFactoryPostProcessor , а только лишь явной инициализацией через XML или @Bean метод.

Настройка проекта

Прежде чем вы начнете изучать этот урок, вам необходимо внести некоторые изменения в проект. Для начала создайте структуру папок src/main/resources и переместите в него файл настроек логгирования log4j.properties , тем самым поместив его в classpath проекта. Теперь немного измените файл сборки pom.xml , добавив и изменив в нем следующее:

И наконец, создайте структуру папок src/main/java/lessons/starter/ . В данном пакете вы будете создавать классы с методами public static void main(String[] args) , которые вы будете запускать для того, чтобы можно было видеть результаты действий в процессе изучения данного материала.

Что вы создадите

Вы создадите некоторое количество классов, в которых будет рассмотрена функциональность Spring Framework IoC контейнера.

Настройка IoC контейнера

Основными признаками и частями Java-конфигурации IoC контейнера являются классы с аннотацией @Configuration и методы с аннотацией @Bean . Аннотация @Bean используется для указания того, что метод создает, настраивает и инициализирует новый объект, управляемый Spring IoC контейнером. Такие методы можно использовать как в классах с аннотацией @Configuration , так и в классах с аннотацией @Component (или её наследниках). Класс с аннотацией @Configuration говорит о том, что он является источником определения бинов. Самая простейшая из возможных конфигураций выглядит следующим образом:

Полный @Configuration vs легкий @Bean режимы Когда методы с аннотацией @Bean определены в классах, не имеющих аннотацию @Configuration , то относятся к обработке в легком режиме, то же относится и к классам с аннотацией @Component . Иначе, такие методы относятся к полному режиму обработки. В отличие от полного, в легком режиме @Bean методы не могут просто так объявлять внутренние зависимости. Поэтому, в основном предпочтительно работать в полном режиме, во избежание трудноуловимых ошибок.

Для того, чтобы приступить к настройке и изучению Spring IoC контейнера, вы должны инициализировать ApplicationContext , который поможет также с разрешением зависимостей. Для обычной Java-конфигурации применяется AnnotationConfigApplicationContext , в качестве аргумента к которому передается класс, либо список классов с аннотацией @Configuration , либо с любой другой аннотацией JSR-330, в том числе и @Component :

Как вариант, можно инициализировать контекст(ы) таким образом:

Подготовка к Spring Professional Certification. Контейнер, IoC, бины

Сегодня я решил представить вам перевод цикла статей для подготовки к Spring Professional Certification.

Это перевод только первой статьи, если он зайдет аудитории, я продолжу выпуск переводов.

Зачем я это делаю, ведь уже есть куча профильных материалов?




Сразу напишу список источников, из которых автор брал материалы
  • Spring 5 Design Patterns
  • Spring in Action 4th edition
  • Spring Security — Third Edition
  • Core Spring 5 Certification in Detail by Ivan Krizsan
  • Spring Documentation and Spring API javadocs
Что такое внедрение зависимостей(DI) и в чем его преимущества?

Внедрение зависимостей — это специальный паттерн, который уменьшает связь между Spring компонентами. Таким образом, при применении DI, ваш код становится чище, проще, его становится легче понять и тестировать.
Согласно паттерну DI, создание объектов для зависимостей переходит на фабрику или отдается третьей стороне. Это означает, что мы можем сосредоточиться на использовании этих объектов вместо их создания.

  • Уменьшенная связь между частями приложения
  • Улучшенное тестирование
  • Улучшенная архитектура приложения
  • Уменьшает шаблонный код
  • Стандартизирует разработку приложения
Почему для создания Spring beans рекомендуются интерфейсы?
  • Улучшенное тестирование. В тестах бин может быть заменен специальным объектом(mock или stub), который реализует интерфейс бина.
  • Позволяет использовать механизм динамических прокси из JDK(например, при создании репозитория через Spring Data)
  • Позволяет скрывать реализацию

В Spring Framework интерфейс org.springframework.factory.BeanFactory предоставляет фабрику для бинов, которая в то же время является IoC контейнером приложения. Управление бинами основано на конфигурации(java или xml).

Интерфейс org.springframework.context.ApplicationContext — это обертка над bean factory, предоставляющая некоторые дополнительные возможности, например AOP, транзакции, безопасность, i18n, и т.п.

Что такое контейнер и какой у него жизненный цикл?

Основа Spring Framework — контейнер, и наши объекты "живут" в этом контейнере.
Контейнер обычно создает множество объектов на основе их конфигураций и управляет их жизненным циклом от создания объекта до уничтожения.

Контейнер — это объект, реализующий интерфейс ApplicationContext.

  1. Контейнер создается при запуске приложения
  2. Контейнер считывает конфигурационные данные
  3. Из конфигурационных данных создается описание бинов
  4. BeanFactoryPostProcessors обрабатывают описание бина
  5. Контейнер создает бины используя их описание
  6. Бины инициализируются — значения свойств и зависимости внедряются в бин
  7. BeanPostProcessor запускают методы обратного вызова(callback methods)
  8. Приложение запущено и работает
  9. Инициализируется закрытие приложения
  10. Контейнер закрывается
  11. Вызываются callback methods

Spring обеспечивает несколько разновидностей контекста.

Есть несколько основных реализаций интерфейса ApplicationContext:

  • FileSystemXmlApplicationContext
  • ClassPathXmlApplicationContext
  • AnnotationConfigApplicationContext
  • XmlWebApplicationContext
  • AnnotationConfigWebApplicationContext

Примеры создания контекста:

Можете ли вы описать жизненный цикл бина в контейнере? Как получить ApplicationContext в интеграционном тесте?

Если вы используете JUnit 5, то вам нужно указать 2 аннотации:

  • @ExtendWith(TestClass.class) — используется для указания тестового класса
  • @ContextConfoguration(classes = JavaConfig.class) — загружает java/xml конфигурацию для создания контекста в тесте

Можно использовать аннотацию @SpringJUnitConfig , которая сочетает обе эти аннотации.
Для теста веб-слоя можно использовать аннотацию @SpringJUnitWebConfig .

Если это не веб-приложение, то есть 2 способа:

В Spring Boot приложении:

  • Spring Boot самостоятельно зарегистрирует shutdown-hook за вас.
Что такое Java-конфигурация? Как она применяется?

Чтобы создать класс с конфигурацией на основе Java-кода, нужно аннотировать его с помощью
@Configuration .
Этот класс будет содержать фабричные методы для создания бинов в контейнере.
Эти методы должны быть аннотированы аннотацией @Bean .

Этот класс поместит в контейнер экземпляр класса DataSource. Позднее его можно будет использовать при доступе к базе данных.

Component scanning(сканирование компонентов) — Spring автоматически обнаруживает бины, которые будут находиться в контейнере. Это бины с аннотациями-стереотипами.

Однако сканирование компонентов не включено по умолчанию.
Чтобы включить сканирование, аннотируйте @Configuration-класс аннотацией @ComponentScanning . Spring будет автоматически сканировать тот пакет, в котором находится этот класс и все его подпакеты.
Можно указать и другие пакеты для сканирования, и даже классы:

Autowiring(внедрение) — Spring автоматически внедрит зависимости во время сканирования или помещения бина в контейнер.
Для внедрения зависимостей используется аннотация @Autowire .

Стереотипы — это аннотации, обозначающие специальную функциональность.
Все стереотипы включают в себя аннотацию @Component .

Component Корневая аннотация, которая помечает класс как кандидат для автовнедрения
Controller Указывает, что класс является контроллером для отправления данных на фронт.
@RestController Указывает, что класс является контроллером для REST.
Содержит аннотации Controller и @ResponseBody
Service Указывает, что класс является сервисом для выполнения бизнес-логики
Repository Указывает, что класс является репозиторием для работы с бд
@Configuration Указывает, что класс содержит Java-конфигурацию(@Bean-методы)
Какие существуют области видимости у бинов? Какая у них видимость по умолчанию?

Область видимости — scope, скоуп. Существует 2 области видимости по умолчанию.

Singleton
Область видимости по умолчанию. В контейнере находится всего 1 экземпляр бина
Prototype
В контейнере может находится любое количество экземпляров бина

И 4 области видимости в веб-приложении.

Область видимости указывается с помощью аннотации @Scope на @Bean методах.

Как связаны различные скоупы и многопоточность?

Prototype Scope не потокбезопасный, т.к. он не гарантирует что один и тот же экземпляр будет вызываться только в 1 потоке.

Singleton Scope же наоборот потокобезопасный.

Как создаются бины: сразу или лениво? Как изменить это поведение?

Singleton-бины обычно создаются сразу при сканировании.
Prototype-бины обычно создаются только после запроса.

Чтобы указать способ инициализации, можно использовать аннотацию @Lazy .
Она ставится на @Bean-методы, на @Configuration-классы, или на @Component-классы.
В зависимости от параметра(true или false), который принимает аннотация, инициализация будет или ленивая, или произойдет сразу. По умолчанию(т.е. без указания параметра) используется true.

Что будет если бин с одним скоупом внедрить в бин с другим скоупом?

Singleton bean можно внедрять в любой другой бин.

В сам singleton можно внедрить только prototype или singleton .
Если внедрять prototype, то для каждого singleton будет создан уникальный prototype.

Prototype может быть зависимостью для любого бина.
Внедрять можно только singleton или prototype.

Что такое BeanFactoryPostProcessor и когда он используется?
  • BeanFactoryPostProcessor работает над описаниями бинов или конфигурационными метаданными перед тем, как бин будет создан.
  • Spring поставляет несколько полезных реализаций BeanFactoryPostProcessor , например, читающий property-файлы и получающий из них свойства бинов.
  • Вы можете написать собственную реализацию BFPP.

Для того чтобы использовать кастомный BFPP. Вы можете переопределить механизм получения данных из метафайлов.

  • destroyMethod — указывает на метод обратного вызова. Метод находится в бине.
  • initMethod — указывает на метод обратного вызова. Метод находится в бине.
  • name — имя бина. По умолчанию именем бина является имя метода.
  • value — алиас для name()
Что такое BeanPostProcessor и чем он отличается от BeanFactoryPostProcessor?

Spring использует несколько BeanPostProcessor’ов.
Например, CommonAnnotationPostProcessor или AutowiredAnnotationBeanPostProcessor .
BPP работает с экземплярами бинов, т.е. контейнер создает бин, а затем начинает работать BPP.



Что такое callback methods и как их использовать?

Есть 3 варианта для создания таких методов:

Как можно использовать аннотацию @Autowire и в чем отличие между способами?

Ниже перечислены типы DI, которые могут быть использованы в вашем приложении:

DI через конструктор считается самым лучшим способом, т.к. для него не надо использовать рефлексию, а также он не имеет недостатков DI через сеттер.
DI через поле не рекомендуется использовать, т.к. для этого применяется рефлексия, снижающая производительность.
DI через конструктор может приводить к циклическим зависимостям. Чтобы этого избежать, можно использовать ленивую инициализацию бинов или DI через сеттер.

  1. Контейнер определяет тип объекта для внедрения
  2. Контейнер ищет бины в контексте(он же контейнер), которые соответствуют нужному типу
  3. Если есть несколько кандидатов, и один из них помечен как @Primary , то внедряется он
  4. Если используется аннотации @Autowire + Qualifier , то контейнер будет использовать информацию из @Qualifier , чтобы понять, какой компонент внедрять
  5. В противном случае контейнер попытается внедрить компонент, основываясь на его имени или ID
  6. Если ни один из способов не сработал, то будет выброшено исключение

Контейнер обрабатывает DI с помощью AutowiredAnnotationBeanPostProcessor. В связи с этим, аннотация не может быть использована ни в одном BeanFactoryPP или BeanPP.

Если внедряемый объект массив, коллекция, или map с дженериком, то Spring внедрит все бины подходящие по типу в этот массив(или другую структуру данных). В случае с map ключом будет имя бина.

Вы можете использовать разные типы внедрения:

Как использование @Qualifier дополняет @Autowired?

Spring предоставляет аннотацию Qualifier, чтобы преодолеть проблему неоднозначности при DI.

Если в контейнере есть несколько бинов одного типа(SomeClass), то контейнер внедрит именно тот бин, над @Bean-методом которого стоит соответствующий квалификатор. Также можно не ставить квалификатор на метод, а использовать имя бина в качестве параметра квалификатора.
Имя бина можно можно указать через параметр аннотации Bean, а по умолчанию это имя фабричного метода.

Что такое прокси-объекты и какие типы прокси-объектов может создавать Spring?

Прокси это специальный объект, который имеет такие же публичные методы как и бин, но у которого есть дополнительная функциональность.
Два вида прокси:

  • JDK-proxy — динамическое прокси. API встроены в JDK. Для него необходим интерфейс
  • CGLib proxy — не встроен в JDK. Используется когда интерфейс объекта недоступен
  • Позволяют добавлять доп. логику — управление транзакциями, безопасность, логирование
  • Отделяет некоторый код(логирование и т.п.) от основной логики

Если в контейнере нет экземпляра бина, то вызывается @Bean-метод. Если экземпляр бина есть, то возвращается уже созданный бин.

Что такое профили? Какие у них причины использования?

При использовании Java-конфигурации вы можете использовать аннотацию @Profile .
Она позволяет использовать разные настройки для Spring в зависимости от указанного профиля.
Ее можно ставить на @Configuration и Component классы, а также на Bean методы.

Как внедрить простые значения в свойства в Spring?

Для этого можно использовать аннотацию @Value .
Такие значения можно получать из property файлов, из бинов, и т.п.

В эту переменную будет внедрена строка, например из property или из view.

Процесс разрешения зависимостей

IoC контейнер выполняет разрешение зависимостей бинов в следующем порядке:

  • Создается и инициализируется ApplicationContext с метаданными конфигурации, которые описывают все бины. Эти метаданные могут быть описаны через XML, Java-код или аннотации
  • Для каждого бина и его зависимостей вычисляются свойства, аргументы конструктора или аргументы статического фабричного метода, либо обычного(без аргументов) конструктора. Эти зависимости предоставляются бину, когда он(бин) уже создан. Сами зависимости инициализируются рекурсивно, в зависимости от вложенности в себе других бинов. Например, при инициализации бина А, котый имеет зависимость В, а В зависит от С, сначала инициализируется бин С, потом В, а уже потом А
  • Каждому свойству или аргументу конструктора устанавливается значение или ссылка на другой бин в контейнере
  • Для каждого свойства или аргумента конструктора подставляемое значение конвертируется в тот формат, который указан для свойства или аргумента. По умолчанию Spring может конвертировать значения из строкового формата во все встроенные типы, такие как int , long , String , boolean и др.

Spring каждый раз при создании контейнера проверяет конфигурацию каждого бина. И только бины с областью видимости(scope) singleton создаются сразу вместе со своими зависимостями, в отличие от остальных, которые создаются по запросу и в соответствии со своей областью видимости. В случае цикличной зависимости(когда класс А требует экземпляр В, а классу В требуется экземпляр А) Spring IoC контейнер обнаруживает её и выбрасывает исключение BeanCurrentlyInCreationException .

Spring контейнер может разрешать зависимости между бинами через autowiring(далее, автоматическое связывание). Данный механизм основан на просмотре содержимого в ApplicationContext и имеет следующие преимущества:

  • Автоматическое связывание позволяет значительно сократить количество инструкций для указания свойств или аргументов конструктора
  • Автоматическое связывание позволяет обновлять конфигурацию, несмотря на развитие ваших объектов. К примеру, вам необходимо добавить зависимость в классе и эта зависимость может быть разрешена без необходимости модификации конфигурации. Поэтому автоматическое связывание может быть особенно полезным при разработке, не исключая возможность переключения на явное описание, когда кодовая база будет стабильна

Для того, чтобы воспользоваться механизмом автоматического связывания, Spring Framework предоставляет аннотацию @Autowired . Примеры применения приведены ниже:

Т.к. кандидатов для автоматического связывания может быть несколько, то для установки конкретного экземпляра необходимо использовать аннотацию @Qualifier , как показано ниже. Данная аннотация может быть применена как к отдельному полю класса, так и к отдельному аргументу метода или конструктора:

Соответственно, у одной из реализации GreetingService должна быть установлена соответствующая аннотация @Qualifier :

Spring также поддерживает использование JSR-250 @Resource аннотации автоматического связывания для полей класса или параметров setter-методов:

Использование @Bean аннотации

Как упоминалось выше, для того, чтобы объявить Bean-объект(далее просто бин), достаточно указать аннотацию @Bean тому методу, который возвращает тип бина как в классах с аннотацией @Configuration , так и в классах с аннотацией @Component (или её наследниках). Например, определим интерфейс какого-нибудь сервиса и его реализацию:

Теперь, для того, чтобы объект с типом GreetingService был доступен для использования, необходимо описать его в конфигурации следующим образом:

А для того, чтобы использовать его, достаточно выполнить следующее:

Метод getBean() может принимать в качестве аргумента как класс(как показано выше), так и названия бина(подробнее будет рассмотрено ниже), либо другие варианты, с которыми вы можете ознакомится в документации. Однако такой подход не рекомендуется использовать в production-конфигурациях, т.к. для подобных целей существует механизм Dependency Injection (DI), собственно говоря, для чего и предназначен Spring IoC контейнер. Использование DI будет рассмотрено ниже в отдельной главе.

Именовать бины принято в соответствии со стандартным соглашением по именованию полей Java-классов. Т.е. имена бинов должны начинаться со строчной буквы и быть в "Верблюжьей" нотации.

По умолчанию, так, как будет назван метод определения бина, по такому имени и нужно получать бин через метод getBean() или автоматическое связывание. Однако вы можете переопределить это имя или указать несколько псевдонимов, через параметр name аннотации @Bean . Выглядеть это будет примерно так:

Иногда полезно предоставить более подробное описание бина, например, в целях мониторинга. Для этого существует аннотация @Description :

Жизненный цикл бина

Для управления контейнером жизненным циклом бина, вы можете реализовать метод afterPropertiesSet() интерфейса InitializingBean и метод destroy() интерфейса DisposableBean . Метод afterPropertiesSet() позволяет выполнять какие-либо действий после инициализации всех свойств бина контейнером, метод destroy() выполняется при уничтожении бина контейнером. Однако их не рекомендуется использовать, поскольку они дублируют код Spring. Как вариант, предпочтительно использовать методы с JSR-250 аннотациями @PostConstruct и @PreDestroy . Также существует вариант определить аналогичные методы как параметры аннотации @Bean , например так: @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod") .В качестве примера применения данных методов, интерфейсов и аннотаций вы можете ознакомиться в классе GreetingServiceImpl .

При совместном использовании методов, интерфейсов и аннотаций, описанных выше, учитывайте их порядок вызовов. Для методов инициализации порядок будет следующий:

  • Методы с аннотациями @PostConstruct в порядке их определения в классе
  • Метод afterPropertiesSet()
  • Метод, указанный в параметре initMethod аннотации @Bean

Для методов разрушения порядок будет следующий:

  • Методы с аннотациями @PreDestroy в порядке их определения в классе
  • Метод destroy()
  • Метод, указанный в параметре destroyMethod аннотации @Bean

Если вам необходимо реализовать свою собственную модель жизненного цикла бина, то в таком случае бин должен реализовывать один из интерфейсов, приведенных ниже:

SmartLifecycle интересен тем, что наследует интерфейс Phased , в котором есть метод int getPhase(); . Суть в том, что порядок создания бинов, реализующих этот интерфейс, зависит от возвращаемого методом значения и чем оно меньше, тем раньше всех будет создан бин и тем позже он будет разрушен.

Если вы на данном этапе запустите Starter.java , то в логах увидите, что методы разрушения не вызываются, однако программа завершает свою работу корректно. Дело в том, что для обычных приложений для этих целей стоит инициализировать контекст с типом AbstractApplicationContext , который также реализует ApplicationContext и имеет метод registerShutdownHook() . В итоге, у вас должно быть премерно следующее:

После этого у вас появятся результаты работы методов при разрушении бина. Однако стоит заметить ещё раз, что это относится к обычным приложения, не относящимся к web-приложения(поскольку для них применяется отдельный тип контекста и подобный метод в них уже есть).

В некоторых случаях необходимо производить манипуляции с ApplicationContext 'ом, например, в самом бине. Для этого существуют интерфейсы *Aware , полный список которых приведен в таблице 5.4 документации. Поэтому когда ApplicationContext создает экземпляр бина, он учитывает соответствующий интерфейс и передает ссылку на соответствующий ресурс.

Как было описано выше, Spring IoC контейнеру требуются метаданные для конфигурации. Одну из таких аннотаций мы уже рассмотрели, это @Bean , рассмотрим теперь и другие.

Другой основной аннотацией является @Component , а также её наследники @Repository , @Service и @Controller . Все они являются общими шаблонами для любых компонентов, управляемыми контейнеером. @Repository , @Service и @Controller рекомендуется использовать в тех случаях, когда вы можете отнести аннотируемый класс к определенному слою, например DAO, либо когда вам необходима поддержка функциональности, которую предоставляет аннотация. Также эти аннотации могут иметь дополнительный смысл в будущих версиях Spring Framework. В остальных же случаях достаточно использовать аннотацию @Component .

Для того, чтобы ваша конфигурация могла знать о таких компонентах и вы могли бы их использовать, существует специальная аннотация для класса вашей конфигурации @ComponentScan .

По умолчанию, такая конфигурация сканирует на наличие классов с аннотацией @Component и его потомков в том пакете, в котором сама находится, а также в подпакетах. Однако, если вы хотите, чтобы сканирование было по определенным каталогам, то это можно настроить, просто добавив в аннотацию @ComponentScan параметр basePackages с указанием одного или нескольких пакетов. Выглядеть это будет примерно таким образом: @ComponentScan(basePackages = "lessons.services") , а классу GreetingServiceImpl при этом необходимо добавить аннотацию @Component .

Стоит упомянуть ещё одну мета-аннотацию @Required . Данная аннотация применяется к setter-методу бина и указывает на то, чтобы соответствующее свойство метода было установлено на момент конфигурирования значением из определения бина или автоматического связывания. Если же значение не будет установлено, будет выброшено исключение. Использование аннотации позволит избежать NullPointerException в процессе использования свойства бина. Пример использования:

Читайте также: