Что такое компонент bean во фреймворке spring

Обновлено: 04.07.2024

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

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

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

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

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

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

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

Введение

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 метод.

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

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


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


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

Настройка 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 :

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

Использование @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 в процессе использования свойства бина. Пример использования:

Области видимости(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 контейнер выполняет разрешение зависимостей бинов в следующем порядке:

  • Создается и инициализируется 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-методов:

Основными элементами в контексте приложения, использующего Spring Framework, являются компоненты или бины (beans), используемые для внедрения зависимостей (Dependency Injection, DI). Есть два основных способа создания таких компонентов.

Создание компонента при помощи аннотаций

Пример простого компонента:

import org . springframework . stereotype . Component ;

В данном случае будет создан компонент типа SimpleBean с именем и идентификатором simpleBean. Контекст Spring автоматически найдёт этот класс и создаст объект этого типа, который будет использоваться в дальнейшем для внедрения зависимостей.

Создание компонента при помощи @Bean-метода

import org . acruxsource . sandbox . filters . SimpleBean ; import org . springframework . context . annotation . Bean ; import org . springframework . context . annotation . Configuration ;

В данном случае будут созданы 2 компонента типа SimpleBean и с именами и идентификаторами simpleBean и anotherSimpleBean. Данный способ удобен тем, что можно создавать компоненты сторонних классов. Так же отмечу тот момент, что создание компонентов при помощи @Bean-методов возможно только в классах, помеченных аннотацией @Confuguration, как это показано в примере выше. Так же этот код соответствует следующей XML-конфигурации:

<bean class = "name.alexkosarev.blog.beans.SimpleBean" name = "simpleBean" /> <bean class = "name.alexkosarev.blog.beans.SimpleBean" name = "anotherSimpleBean" />

При создании компонентов таким способом стоит помнить, что если в классе, объект которого вы создаёте, есть зависимости внедряемые при помощи аннотации @Autowired, они будут автоматически внедрены.

Внедрение через свойство класса

Самый распространённый, но не самый удобный для тестирования, способ внедрения, о чём я как-нибудь напишу в заметке о тестировании.

В данном случае контекст Spring находит класс, помеченный аннотацией @Controller, создаёт объект этого класса, находит свойство помеченное аннотацией @Autowired и присваивает указанному свойству компонент нужного класса, находящийся в контексте Spring.

Тут может произойти две ошибки: в контексте Spring не окажется компонента класса SimpleBean, либо в контексте окажется более одного компонента класса SimpleBean. Об этом я расскажу чуть ниже.

Внедрение через конструктор

Внедрение через конструктор значительно удобнее, так как при тестировании даёт возможность вручную создать объект класса и передать ему мок вместо реального компонента.

public SimpleController ( SimpleBean simpleBean , AnotherSimpleBean anotherSimpleBean ) <

Данный код соответствует XML-конфигурации:

<bean class = "name.alexkosarev.blog.beans.controllers.SimpleController" c : simpleBean-ref = "simpleBean" c : anotherSimpleBean-ref = "anotherSimpleBean" /> <bean class = "name.alexkosarev.blog.beans.controllers.SimpleController" c : simpleBean = "$" c : anotherSimpleBean = "$" />

Так же внедряемое свойство можно сделать final, как это показано в примере. Стоит помнить, что аннотация @Autowired ставится на метод, а не на аргумент. Фактически это даёт возможность передавать несколько внедряемых компонентов в конструктор, что и показано в примере.

Внедрение через сеттер

Внедрение через сеттер аналогично внедрению через конструктор с той разницей, что свойство класса нельзя сделать final.

Данный код соответствует следующей XML-кофигурации:

<bean class = "name.alexkosarev.blog.beans.controllers.SimpleController" p : simpleBean-ref = "simpleBean" /> <bean class = "name.alexkosarev.blog.beans.controllers.SimpleController" p : simpleBean = "$" />

Процесс внедрения компонентов

Процесс подбора кандидата на внедрение следующий:

    Контекст пытается найти компонент указанного типа и с указанным именем или идентификатором (по умолчанию в качестве имени и идентификатора используется имя внедряемого компонента). Например:

Поиск компонентов

По умолчанию контекст Spring ищет компоненты, находящиеся в пакетах, нижестоящих относительно класса с аннотацией @ComponentScan. Иными словами, если у нас в пакете name.alexkosarev.blog есть класс, помеченный @ComponentScan, то поиск компонентов будет вестись в пакетах name.alexkosarev.blog, name.alexkosarev.blog.beans, name.alexkosarev.blog.controllers и т.д., но компоненты, лежащие в пакете name.alexkosarev не будут найдены. Что бы включить в контекст компоненты из вышестоящих пакетов, нужно в аннотацию @ComponentScan передать аргумент basePackages с пакетом, относительно которого нужно искать компоненты:

Контекст не может найти кандидата на внедрение

Пожалуй, самая распространённая ошибка. Заключается она в том, что внедряемый компонент не был создан, либо был создан вручную, а не через контекст Spring. При возникновении такой ошибки нужно убедиться, что метод, создающий внедряемый компонент помечен аннотацией @Bean, или у класса компонента есть соответствующая аннотация, и сам класс находится в достижимом для поиска компонентов пакете.

Контекст не смог внедрить зависимость из-за того, что кандидатов более одного

Ещё одна очень распространённая ошибка. Взгляните на пример создания компонентов при помощи @Bean-методов. В этом случае у нас будет два компонента типа SimpleBean с идентификаторами simpleBean и anotherSimpleBean, в следствие чего следующий код приведёт именно к этой ошибке:

Сегодня я решил представить вам перевод цикла статей для подготовки к 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.

image


Этот небольшой список вопросов даст вам понимание самых важных концепций Spring, а так же поможет подготовится к собеседованию

Если вы понимаете как работает Component Scan, то вы понимаете Spring

Первый шаг для описания Spring Beans это добавление аннотации — @Component , или @Service , или @Repository .

Однако, Spring ничего не знает об этих бинах, если он не знает где искать их. То, что скажет Spring где искать эти бины и называется Component Scan. В @ComponentScan вы указываете пакеты, которые должны сканироваться.

Spring будет искать бины не только в пакетах для сканирования, но и в их подпакетах.

2 Как вы добавите Component Scan в Spring Boot?

@SpringBootApplication определяет автоматическое сканирование пакета, где находится класс Application

Всё будет в порядке, ваш код целиком находится в указанном пакете или его подпакетах.

Однако, если необходимый вам компонент находится в другом пакете, вы должны использовать дополнительно аннотацию @ComponentScan, где перечислите все дополнительные пакеты для сканирования

3 В чём отличие между @Component и @ComponentScan?

@Component и @ComponentScan предназначены для разных целей

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

В классах конфигурации Spring, @Bean используется для определения компонентов с кастомной логикой.

@Bean используется в конфигурационных классах Spring. Он используется для непосредственного создания бина.

@Component используется со всеми классами, которыми должен управлять Spring. Когда Spring видит класс с @Component , Spring определяет этот класс как кандидата для создания bean.

6 В чём разница между @Component, @Service и @Repository аннотациями?

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

@Component — универсальный компонент
@Repository — компонент, который предназначен для хранения, извлечения и поиска. Как правило, используется для работы с базами данных.
@Service — фасад для некоторой бизнес логики

Пользовательские аннотации, производные от @Component , могут добавлять специальную логику в бинах.

Например, бины, получившиеся при помощи @Repository , дополнительно имеют обработку для JDBC Exception

7 Можем ли мы использовать @Component вместо @Service для бизнес логики?

Если @Component является универсальным стереотипом для любого Spring компонента, то @Service в настоящее время является его псевдонимом. Однако, в официальной документации Spring рекомендуется использовать именно @Service для бизнес логики. Вполне возможно, что в будущих версиях фреймворка, для данного стереотипа добавится дополнительная семантика, и его бины станут обладать дополнительной логикой.

8 В чем различие между web.xml и the Spring Context - servlet.xml?

web.xml — Метаданные и конфигурация любого веб-приложения, совместимого с Java EE. Java EE стандарт для веб-приложений.
servlet.xml — файл конфигурации, специфичный для Spring Framework.

9 Что предпочитаете использовать для конфигурации Spring - xml или аннотирование?

Предпочитаю аннотации, если кодовая база хорошо описывается такими элементами, как @Service, @Component, @Autowired

Однако когда дело доходит до конфигурации, у меня нет каких-либо предпочтений. Я бы оставил этот вопрос команде.

10 Можем ли мы применить @Autowired с не сеттерами и не конструкторами методами?

@Autowired может использоваться вместе с конструкторами, сеттерами или любым другими методами. Когда Spring находит @Autowired на методе, Spring автоматически вызовет этот метод, после создания экземпляра бина. В качестве аргументов, будут подобраны подходящие объекты из контекста Spring.

11 В чем разница между Сквозной Функциональностью (Cross Cutting Concerns) и АОП (аспектно оринтированное программирование)?

Сквозная Функциональность — функциональность, которая может потребоваться вам на нескольких различных уровнях — логирование, управление производительностью, безопасность и т.д.
АОП — один из подходов к реализации данной проблемы

12 В чем разница между IOC (Inversion of Control) и Application Context?

IOC — инверсия управления. Вместо ручного внедрения зависимостей, фреймворк забирает ответственность за это.
ApplicationContext — реализация IOC спрингом.

Bean Factory — это базовая версия IOC контейнера

Application Context также включает дополнительные функции, которые обычно нужны для разработки корпоративных приложений

13 В чем разница между classPathXmlApplicationContext и annotationConfigApplicationContext?

classPathXmlApplicationContext — если вы хотите инициализировать контекст Spring при помощи xml
annotationConfigApplicationContext — если вы хотите инициализировать контекст Spring при помощи конфигурационного класса java

14 Почему возвращаемое значение при применении аспекта @Around может потеряться? Назовите причины.

Метод, помеченный аннотацией @Around , должен возвращать значение, которое он (метод) получил из joinpoint.proceed()

15 Как вы решаете какой бин инжектить, если у вас несколько подходящих бинов. Расскажите о @Primary и @Qualifier?

Если есть бин, который вы предпочитаете большую часть времени по сравнению с другими, то используйте @Primary , и используйте @Qualifier для нестандартных сценариев.

Если все бины имеют одинаковый приоритет, мы всегда будем использовать @Qualifier

Если бин надо выбрать во время исполнения программы, то эти аннотации вам не подойдут. Вам надо в конфигурационном классе создать метод, пометить его аннотацией @Bean , и вернуть им требуемый бин.

На мой взгляд это Functional Web Framework, Kotlin и и поддержка реактивного программирования.

17 Сравните Application Context, IOC Container, vs Web Container и EJB Container. Нужен ли Web Container для запуска Spring Boot приложения?

Web Container и EJB Containers являются частью приложения/веб-сервера, таких как Tomcat, Websphere, Weblogic. Они добавляют свою дополнительную функциональность к ним. Java EE определяет контракт для веб-приложений, эти контейнеры являются реализацией этих контрактов.

Spring контейнер может являться частью любого приложения, которое вы делаете на java. Spring может работать внутри веб-контейнера, ejb контейнера или даже без них.

18 Как мы можем выбрать подходящий бин при помощи application.properties?

И два компонента

Тогда в application.properties добавим свойство
application.greeting: real

Воспользуемся данным решением:

19 Какая минимальная версия Java поддерживается в Spring Boot 2 и Spring 5?

Spring 5.0 и Spring Boot 2.0 поддерживают Java 8 и более поздней версии.

20 В чём разница между @Controller и @RestController?

@RestController = @Controller + @ResponseBody

@RestController превращает помеченный класс в Spring-бин. Этот бин для конвертации входящих/исходящих данных использует Jackson message converter. Как правило целевые данные представлены в json или xml.

21 Почему иногда мы используем @ResponseBody, а иногда ResponseEntity?

ResponseEntity необходим, только если мы хотим кастомизировать ответ, добавив к нему статус ответа. Во всех остальных случаях будем использовать @ResponseBody.

Для @ResponseBody единственные состояния статуса это SUCCESS(200), если всё ок и SERVER ERROR(500), если произошла какая-либо ошибка.

22 В чем разница между Filters, Listeners and Interceptors?

Фильтры и перехватчики делают по сути одно и тоже: они перехватывают какое-то событие, и делают что-то до или после.

Java EE использует термин Filter, Spring называет их Interceptors.

Именно здесь AOP используется в полную силу, благодаря чему возможно перехватывание вызовов любых объектов

23 В чем разница между ModelMap и ModelAndView?

Model — интерфейс, ModelMap его реализация..

ModelAndView является контейнером для пары, как ModelMap и View.

Обычно я люблю использовать ModelAndView. Однако есть так же способ когда мы задаем необходимые атрибуты в ModelMap, и возвращаем название View обычной строкой из метода контроллера.

24 В чем разница между model.put() и model.addAttribute()?

Метод addAttribute отделяет нас от работы с базовой структурой hashmap. По сути addAttribute это обертка над put, где делается дополнительная проверка на null. Метод addAttribute в отличии от put возвращает modelmap.
model.addAttribute(“attribute1”,”value1”).addAttribute(“attribute2”,”value2”);

Нам это может понадобиться, если мы, например, захотим взять некоторое значение с HTML страницы и сохранить его в БД. Для этого нам надо это значение переместить в контроллер Спринга.

Если мы будем использовать Spring MVC form tags, Spring автоматически свяжет переменные на HTML странице с Бином Спринга.

Если мне придется с этим работать, я обязательно буду смотреть официальную документацию Spring MVC Form Tags.

Hibernate Validator никак не связан с БД. Это просто библиотека для валидации.

Hibernate Validator версии 5.x является эталонной реализацией Bean Validation 1.1

27 Где должны располагаться статические (css, js, html) ресурсы в Spring MVC приложении?

Расположение статических ресурсов можно настроить. В документации Spring Boot рекомендуется использовать /static, или /public, или /resources, или /META-INF/resources

28 Почему для конфиденциальных данных рекомендуется использовать POST, а не GET запросы?

В случае GET запроса передаваемые параметры являются частью url, и все маршрутизаторы, через которые пройдет наш GET запрос, смогут их прочитать.

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