1. Главная страница » Компьютеры » Autowired spring что это

Autowired spring что это

Автор: | 16.12.2019

Синопсис

В прошлом посте когда мы создавали простое spring приложение, для связывания компонентов мы использовали конфигурационный xml файл. Но этот подход с xml файлом основан на old style Spring то есть на спринге до аннатационных времен. В этом посте поднимем такое же приложение, только для связывания бинов будем использовать аннтоации, точнее аннотацию @Autowired. Всю вводную теорию по спрингу как смог раскрыл в прошлом посте, в этом буду указывать только на отличия от подхода создания спрингового приложения которое связывается с помощью конфигурационного xml файла от спрингового приложения, которое связывается аннотацией @Autowired.

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

Для этого примера нам понадобится:

  1. JDK
  2. Поскольку пример рассчитан для начинающих осваивать не только спринг, но и джава, я буду показывать пример где все надо делать вручную, в том числе покажу как подключать библиотеки. Поэтому для этого примера, нужны будут библиотеки spring версии 4.0.5: spring-aop-4.0.5.RELEASE.jar, spring-beans-4.0.5.RELEASE.jar, spring-context-4.0.5.RELEASE.jar, spring-core-4.0.5.RELEASE.jar, spring-expression-4.0.5.RELEASE.jar для компиляции spring-приложения. Ссылка чуть ниже. Напоминаю maven, gradle это все это конечно не плохо, но когда тренируешься, лучше понимать самому все эти процессы не прибегая к спец средствам
  3. Библиотека логирования commons-logging-1.1.3.jar для выполнения spring-приложения. Ссылка чуть ниже
  4. Командная строка для запуска shell скрипта, который мы напишем сами

Готовый проект простого спринг приложения можно скачать по ссылке:

Стурктура проекта

После того как мы добавим все необходимые файлы в приложение и запустим build.sh скрипт, в текущем каталоге появится каталог build, со структурой собранного приложения:

Old spring style

Сначала приведу пример простого спринг приложения из предудщего поста, когда для связывания компонентов мы пользовались xml файлом:

Figure.java

Circle.java

Rectangle.java

Print.java

Execute.java

Аннотация @Autowired

Все классы точно такие же как и в предыдущем посте простое spring приложение. В этом посте мы их немного изменим, точнее кое-что добавим. Но сначала еще раз посмотрим на конфигурационный файл context.xml из прошлого поста, когда мы поднимали простое spring приложение. Обратим внимание на двадцатую строчку:

context.xml

В этой строчке бин circle связывается с бином print. Для того чтобы бин circle связать с бином print, мы устанавливаем бин circle для свойства figure бина print. Это значит, что если мы удалим эту строчку связывания, то бин print останется без окружности и выводить будет нечего. Чтобы бин cirlce все таки подвязывался к бину print можно поступить иначе, проаннотируем поле Figure аннотацией @Autowired, а тег

(то есть двадцатую строчку) выкинем из конфигурационного файла, то есть на данный момент класс Print.java выглядет так (с добавленной аннотацией @Autowired в седьмой строке):

Print.java

а конфигурационный файл context.xml выглядит так:

context.xml

BeanPostProcessor, кастомные и некастомные (спринговые) бины

В предыдущем конфигурационном файле context.xml регистрируется три бина, это: circle, rectangle и print. Заметим, что связывание компонентов никуда не делось, оно просто перенесено из конфигурационного файла в класс Print.java и теперь осуществляется через аннотацию @Autowired (строчка 7 в предыдущем листинге Print.java). Так это? Незнаю, давайте посмотрим. Собирем проект командой ./build.sh:

И что же получилось? Из вывода видим, что бины то создались, но при попытке вызова метода showSquare() вылетела NPE.
Если присмотреться к этой ошибке, то можно предположить, что в методе showSquare() осуществляется попытка разрезолвить указатель на null. Так оно и происходит, в методе showSquare(), осуществляется обращение к полю figure, но оно пустое. Это может означать то, что бин circle не автоподвязался к бину print. Но а в чем дело? Разве аннотации @Autowired мало? Выходит, что да. Дело в том, что мы то указали аннотацию @Autowired, но спиринг об этом ничего не знает, поэтому чтобы бины автоматически подвязались, нужно спринг поставить об этом в известность. Ставится спринг в известность об аннотации @Autowired добавлением в контекст бина

В дальнейших рассуждениях будем считать, что бины придуманные и поднимаемые нами это кастомные бины, а бины из спринговой библиотеки спринговые. Что мы щас сделали? Мы добавили спринговый бин (именно спринговый, не кастомный) AutowiredAnnotationBeanPostProcessor который реализует интерфейс BeanPostProcessor и эти бины спринг создаёт в первую очередь, потому что это бины, которые настраивают другие бины. То есть сначала спринг поднимает все бины которые реализуют интерфейс BeanPostProcessor, а потом, в момент создания и настройки спрингом нашего (кастомного) бина, BeanFactory (BeanFactory это фабрика которая создает бины) создает кастомный бин, и затем он (спринг) передает его (кастомный бин) на обработку в бин реализующий интерфейс BeanPostProcessor в нашем случае это в бин AutowiredAnnotationBeanPostProcessor (спринговый бин). Еще раз напоминаю бин AutowiredAnnotationBeanPostProcessor уже был предварительно создан спрингом до того, как он (спринг) создал кастомный бин. В AutowiredAnnotationBeanPostProcessor реализована логика которая выискивает все аннотированные поля аннотацией @Autowired и через рефлекшен кладет в них что-то, например другой бин. Затем, после обработки кастомного бина бин пост процессором, обработанный бин (уже с просечеными полями) возвращается спрингу (а точнее в BeanFactory), и спринг, уже полностью готовый бин, кладет в контейнер.

Добавили мы спринговый бин AutowiredAnnotationBeanPostProcessor, но он обрабатывает только аннотацию @Autowired, что если в классе у нас присутствуют другие аннотации, например @Required или еще какие-нибудь? Все тоже самое, если это аннотация @Required, то ее обрабатывает бин пост процессор org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor вопрос только сколько надо знать разных бин пост процессоров чтобы их включить в контекст для каждой аннотации? Чтобы не пришлось всех их запоминать и добавлять в контекст мы можем использовать нэймспэйс context:annotation-config который все это будет делать за нас. Вместо, того чтобы писать все эти длинные имена бин пост процессоров в конфигурации, можно написать:

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

context.xml

Запускаем еще раз сборку проекта командой ./build.sh, видим что бины поднимаются, но опять вылетает эксепшен, на этот раз BeanCreationException:

Ну что опять не так? По изучив немного логи, можно обратить внимание на это сообщение:

То есть спринг нам говорит о том, что он не знает с каким бином связать поле com.devblogs.component.figure.Figure с circle или с rectangle. Все было бы хорошо, если бы у нас был только один бин типа Figure, но у нас их два, оба являются чаилдами типа Figure. Спринг на данный момент не знает, с каким бином класс Print связывать, поэтому спринг не запустился. Чтобы спринг смог связать класс Print с каким-нибудь бином можно убрать из конфигурационного файла регистрацию какого-нибудь одного из бинов либо circle либо rectangle. В этом случае один из них перестанет быть бином, а тот бин который останется станет единственным бином который наследуется от Figure и тогда все заработает. Но нас такой вариант не устраивает, потому что мы хотим чтобы у нас было оба бина, а подвязывался к полю Figure только один из них. Чтобы спринг не напрягался какой бин ему выбрать, мы должны ему сказать, какой конкретно бин положить в поле Figure. Для этого используется аннотация @Qualifier, которая говорит спрингу какой бин надо положить ему в это поле. Добавьте в класс Print.java аннотацию @Qualifier вместе с названием бина, который указывается в скобках, с которым спринг будет связывать поле Figure. Теперь класс Print.java должен выглядеть так:

Читайте также:  Iphone 6 быстро заряжается и быстро разряжается

Print.java

Тут мы говорим спрингу с помощью аннотации @Qualifier (строчка 9), что бин circle нужно положить в бин print. Кроме того обратите внимание, что в классе Print.java теперь отсутствует сет метод для поля Figure. Его нет потому что, теперь спринг просечивает поле figure через рефлексию.
После того, как мы все это дело зараним командой ./build.sh все должно отработать без ошибок, и в выводе мы должны увидеть площадь окружности, потому что указатель figure в методе showSquare() наконец-то нашел в памяти объект Circle:

то есть помимо добавления всех необходимых бин пост процессоров в контекст, он еще просканирует пакеты на наличие аннотации @Component или @Service.
Тег context:component-scan сканирует определенный пакет или определенные пакеты и ищет в нем бины и те, что найдет создает. Что это значит? Это значит, что в конфигурационном файле мы можем опустить явное определение бинов переложив эту процедуру на спринг, в том числе тех бинов, которые требуют параметры конструктора, но об этом чуть далее, а пока параметры конструктов бинов circle и rectangle будем инициализировать через конфигурационный файл, а вот определение бина print мы можем убрать из конфигурационного файла. Удалите определение бина print из конфигурационного файла context.xml и наш конфигурационный файл должен выглядеть так:

context.xml

Теперь конфигурационный файл context.xml состоит только из двух определений бинов и из одного тега context:component-scan.
А теперь с измененным конфигурационным файлом, запустим билд скрпит build.sh:

Из предыдущего вывода видно, что два бина circle и rectangle создались успешно, а вот на этапе создании бина print, как раз того самого, который мы выкинули из конфигурационного файла, приложение зафейлилось 🙁 . Эксепшен NoSuchBeanDefinitionException говорит о том, что спринг не смог найти бин print в своем контексте, а не смог потому что он не был создан. Но что это значит? Чуть выше я писал, что если выкинуть определение бина из конфигурационного файла, то он перестает быть бином. Вот это и произошло. Ну хорошо, а как же тег

Разве он не заставил спринг про сканировал пакет com.devblogs.component (и все его под пакеты) на бины? Ответ спринг просканировал пакеты, но он там ничего не нашел. Почему? Давайте разбираться дальше.

Аннотации @Service и @Component

Спринг не обнаружил бинов в пакетах потому что по мнению спринга (а мнение спринга это последняя инстанция) их там нет. Давайте еще раз посмотрим на класс Print.java (я понимаю мы на него уже много раз за сегодня смотрели, и все таки еще раз):

Print.java

Можно из него понять что это бин или компонент спринга? Вот и спринг не может, а то что там есть аннотации @Autowired и @Qualifier это еще не факт, что это компонент. Спрингу нужны гарантии что это компонент. Для этого есть аннотация, которая так и называется @Component или @Service. Все что нам нужно сделать, это добавить аннотацию @Component перед названием класса.

Print.java

Теперь попробуем еще раз собрать и заранить приложение командой ./build.sh:

На этот раз приложение раниться без ошибок. Теперь о том, что на этом этапе происходит. После того, как спринг нашел в конфигурационном файле тег context:component-scan, он начинает процесс поиска бинов, заглядывая в каждый класс в пределах того пакета, который указан в теге. Допустим спринг заглядывает в класс Print.java, в нем он находит аннотацию @Component, и делает вывод, что это бин. Затем создает его инстанс и связывает с ним бин circle который был создан ручным способом в конфигурационном файле context.xml. По сути, если бы не параметры конструктора, то можно было бы в конфигурационном файле вообще ничего не определять, а только лишь добавить один тег

и он бы все сделал. Более того, даже инициализацию параметров конструктора можно перенести из конфигурационного файла в класс как мы это делали для инициализации свойства figure.

Аннотация @Value

Этот раздел будет как дополнение к предыдущему, на практике инициализация параметров внутри классов производится никогда, этот раздел скорее посвящен как можно делать, но как не нужно делать, поэтому сразу переходите от сюда к сборке проекта.
Для того чтобы перенести инициализацию параметров конструктора (или метода) из конфигурационного файла в класс для этого используются две аннотации, одна уже нам знакомая @Autowired, а другая @Value. Теперь давайте выкинем из конфигурационного файла context.xml оставшиеся определения бинов:

и оставим только тег

теперь конфигурационный файл context.xml состоит только из одного тега context:component-scan:

context.xml

Так как файл конфигурации упрощается до одной строки

Но и это еще не все. Можно вообще обойтись без конфигурационного файла, а воспользоваться объектом AnnotationConfigApplicationContext который создаётся в мэин методе приложения. В этот объект передается пакет с которого начинается сканировать классов на наличие аннотаций @Component и можно из него вытаскивать бины. В этом случае конфигурационный файл context.xml становится уже ненужным, так как все что там говорится делать, уже делается объектом AnnotationConfigApplicationContext, то есть поиск и создание бинов.
А в java классах Circle.java и Rectangle.java надо про аннотировать формальные параметры конструктора аннотацией @Value указывая в круглых скобках значение, которое мы хотим передать в конструктор, а сам конструктор про аннотировать аннотацией @Autowired, а так как мы еще выкинули определения бинов circle и rectangle из конфигурационного файла context.xml, то надо еще про аннотировать эти классы аннотацией @Component. Измененные классы Circle.java и Rectangle.java должны выглядеть так:

Circle.java

Rectangle.java

Собираем все вместе

Ну вот и все, теперь приведу еще раз все классы с конфигурационным файлом после всех сделанных изменений (изменения подсвечены):

Аннотация @Autowired отмечает конструктор, поле или метод как требующий автозаполнения инъекцией зависимости Spring. Данная аннотация впервые появилась в Spring 2.5.

Чтобы аннотация @Autowire присвоила переменной значения соответствующего bean’а, необходимо чтобы этот bean либо был объявлен в xml конфигурации приложения, либо существовал класс с соответствующей иньекцией управления.

Что такое @Autowired?

Если же опустить все умные слова, то полезную функциональность аннотации @Autowired можно описать следующим образом. Используя эту аннотацию, не нужно заботиться о том, как лучше всего передать классу или bean’у экземпляр другого bean’a. Фреймворк Spring сам найдет нужный bean и подставит его значение в свойство, которое отмечено аннотацией @Autowired.

@Autowired и свойство класса

Свойства класса с аннотацией @Autowired заполняются соответствующими значениями сразу после создания bean’а и перед тем, как любой из методов класса будет вызван. Пример:

@Autowired и сеттер

Традиционное использование аннотации. Пример:

@Autowired и конструктор класса

Только один конструктор может выполнять эту аннотацию. Этот конструктор может быть любого типа (private, protected), а не только pubic.

@Autowired и метод

Аннотация @Autowire может быть использована в методе с любым именем и с любым количеством принимаемых параметров. В этом случае Spring попытается присвоить каждому(!) аргументу значение соответствующих bean’а. Метод не обязан быть public. Например:

Использование (require=false)

Конструкция require=false сообщает фреймворку о том, что наличие соответствующего bean’а не является обязательным при компиляции программы. Пример:

В случае, если аннотации не была объявлена с required=false и bean отсутствует в приложении, пользователь получит исключение:

@Autowired и @Qualifier

С помощью аннотации @Qualifier можно отметить конкретного кандидата для автозаполнения если кандидатов несколько. Например, есть два bean’а:

Эти два bean’а описывают один и тот же класс FooService:

Чтобы при автозаполнении получить второй bean с идентификатором fooService2, необходимо воспользоваться аннотацией @Qualifier. Пример:

Проверить, что это именно второй bean очень легко: метод doStuff первого bean’а вернет строку fooService1.doStuff(), а метод doStuff второго bean’а — fooService2.doStuff().

Также @Qualifier можно использовать для конкретного аргумента в методе с множеством аргументов. Например:

В качестве бонуса — исходный код веб-приложения, демонстрирующего все вышеперечисленные приемы с аннотацией @Autowired: Скачать

чи обов’язково повинні бути
public String getName() <
return name;
>

Читайте также:  Mobile review com портал мобильных технологий

public void setName(String name) <
this.name = name;
>
в класі якщо писати так

public class FooService <
@Autowired
private String name;

public String getName() <
return name;
>

public void setName(String name) <
this.name = name;
>

public String doStuff() <
return name + ".doStuff()";
>
>

Этот урок освещает работу с 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 и добавляет более специфичную функциональность. Ниже в таблице представлены различия между ними:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

По умолчанию, так, как будет назван метод определения бина, по такому имени и нужно получать бин через метод 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 , рассмотрим теперь и другие.

Читайте также:  1500 Лошадиных сил машина

Другой основной аннотацией является @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 . Также вы можете создать свою собственную облать видимости. Ниже приведен список областей видимостей, описанных в документации на момент написания урока:

  • singleton — По умолчанию. Spring IoC контейнер создает единственный экземпляр бина. Как правило, используется для бинов без сохранения состояния(stateless)
  • prototype — Spring IoC контейнер создает любое количество экземпляров бина. Новый экземпляр бина создается каждый раз, когда бин необходим в качестве зависимости, либо через вызов getBean() . Как правило, используется для бинов с сохранением состояния(stateful)
  • request — Жизненный цикл экземпляра ограничен единственным HTTP запросом; для каждого нового HTTP запроса создается новый экземпляр бина. Действует, только если вы используете web-aware ApplicationContext
  • session — Жизненный цикл экземпляра ограничен в пределах одной и той же HTTP Session . Действует, только если вы используете web-aware ApplicationContext
  • global session — Жизненный цикл экземпляра ограничен в пределах глобальной HTTP Session (обычно при использовании portlet контекста). Действует, только если вы используете web-aware ApplicationContext
  • application — Жизненный цикл экземпляра ограничен в пределах ServletContext . Действует, только если вы используете 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-методов:

Использование стандартных JSR-330 аннотаций

Spring Framework поддерживает JSR-330 аннотации. Эти аннотации работают таким же способом, как и Spring аннотации. Для того, чтобы работать с ними, необходимо добавить в pom.xml следующую зависимость:

Ниже приведена таблица сравнения JSR-330 и Spring аннотаций для DI:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *