что такое контекст в spring

Spring: в поисках контекста

Пару месяцев назад в моем профиле был опубликован подробный пост по загрузке классов на JVM. После этого доклада мои коллеги задались хорошим вопросом: а какой механизм использует Spring для разбора конфигураций и как он загружает классы из контекста?

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

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

Немного теории

Сразу определим, что ApplicationContext — это главный интерфейс в Spring-приложении, который предоставляет информацию о конфигурации приложения.

Перед тем, как перейти непосредственно к демонстрации, взглянем на этапы формирования ApplicationContext:

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

В этом посте разберем первый этап, так как нас интересует именно чтение конфигураций и создание BeanDefinition.

BeanDefinition — это интерфейс, который описывает бин, его свойства, аргументы конструктора и другую метаинформацию.

Что касается конфигурации самих бинов, у Spring есть 4 способа конфигурации:

Xml конфигурация

За основу берем простой проект:

Здесь следует немного пояснить, какие методы и для чего используются:

В 25 строке идет объявление и инициализация ApplicationContext через конфигурацию Xml.

Конфигурационный Xml-файл выглядит следующим образом:

При конфигурации бина указываем реально существующий class. Обратите внимание на заданное свойство lazy-init=”true”: в этом случае бин будет создаваться только после запроса его из контекста.

Смотрим, как Spring при поднятии контекста разрулит ситуацию с классами, объявленными в конфигурационном файле:

Разберемся с деталями Xml конфигурации:

— Чтением файла конфигурации занимается класс XmlBeanDefinitionReader, который реализует интерфейс BeanDefinitionReader;

XmlBeanDefinitionReader на входе получает InputStream и загружает Document через DefaultDocumentLoader:

— После этого каждый элемент этого документа обрабатывается и, если он является бином, создается BeanDefinition на основе заполненных данных (id, name, class, alias, init- method, destroy-method и др.):

— Каждый BeanDefinition помещается в Map, который хранится в классе DefaultListableBeanFactory:

В коде Map выглядит следующим образом:

Теперь в том же конфигурационном файле добавим еще одно объявление бина с классом film.BadVillain:

Смотрим, что получится, если распечатать список созданных BeanDefenitionNames и загруженные классы:

Несмотря на то, что класса film.BadVillain, указанного в конфигурационном файле, не существует, Spring отрабатывает без ошибок:

Cписок BeanDefenitionNames содержит 2 элемента; то есть, те 2
BeanDefinition, сконфигурированные в нашем файле, были созданы.

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

Попытаемся получить еще и сами бины по их именам:

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

Если в первом случае был получен валидный бин, то во втором случае прилетел exception.

Обратите внимание на stack trace: сработала отложенная загрузка классов. Выполняется обход всех загрузчиков классов в попытке найти искомый класс среди загруженных ранее. И после того, как нужный класс не был найден, с помощью вызова метода Utils.forName, происходит попытка найти несуществующий класс по имени, что привело к получению закономерной ошибки.

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

Всё потому, что мы прописали lazy-init:true и запретили Spring создавать экземпляр бина, где и генерируется полученный ранее exception. Если убрать это свойство из конфигурации либо изменить его значение lazy-init:false, то описанная выше ошибка также вылетит, но не будет проигнорирована и приложение остановиться. В нашем случае контекст был проинициализирован, но мы не смогли создать экземпляр бина, т.к. указанный класс не был найден.

Groovy конфигурация

При конфигурации контекста с помощью Groovy-файла, необходимо сформировать GenericGroovyApplicationContext, который принимает на вход строку с конфигурацией контекста. Чтением контекста в данном случае занимается класс GroovyBeanDefinitionReader. Эта конфигурация работает по сути так же, как и Xml, только с Groovy-файлами. К тому же, GroovyApplicationContext нормально работает и с Xml-файлом.

Пример простого конфигурационного Groovy-файла:

Пробуем проделать то же самое, что и с Xml:

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

Ошибка вылетает сразу: Groovy так же, как и Xml, создает BeanDefenition’ы, но в данном случае постпроцессор сразу выдаёт ошибку.

Конфигурация через аннотации с указанием пакета для сканирования или JavaConfig

Данная конфигурация отличается от двух предыдущих. В конфигурация через аннотации используется 2 варианта: JavaConfig и аннотация над классами.

Здесь используется один и тот же контекст: AnnotationConfigApplicationContext(“package”/JavaConfig.class). Работает он в зависимости от того, что было передано в конструктор.

В контексте AnnotationConfigApplicationContext есть 2 приватных поля:

Создаем конфигурационный файл с максимально простым бином. Смотрим, что загрузится:

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

Если в случае с Xml и Groovy загрузилось столько BeanDefinition, сколько было объявлено, то в данном случае в процессе поднятия контекста загружаются как объявленные, так и дополнительные BeanDefinition. В случае реализации через JavaConfig все классы загружаются сразу, в том числе и класс самого JavaConfig, так как он сам является бином.

Еще один момент: в случае с Xml и Groovy конфигурациями загрузилось 343 файла, здесь же произошла более “тяжелая” загрузка количеством 631 доп файл.

Этапы работы ClassPathBeanDefinitionScanner:

Рассмотрим работу сканера на простом примере.

Создаем собственную аннотацию для поиска соответствующих классов:

Создаем 2 класса: один со стандартной аннотацией Component, второй — с кастомной аннотацией:

В результате получаем сформированные BeanDefinition для этих классов и успешно загруженные классы.

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

Вывод

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

Источник

Spring Context

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

Под словом «Spring» обычно подразумевают просто IoC-контейнер, помогающий структурировать Java-приложения. В действительности под словом «Spring» скрывается целый мир.

IoC-контейнер, это замечательный способ собрать приложение «по кусочкам» из разных компонентов. Spring же предоставляет удобные способы как написания данных «кусочков», так и объединения их в единое приложение. Например, у нас есть два класса.

Данные, компоненты уже достаточно неплохо. Наличие же интерфейсов мы для простоты опустим. Самый простой способ объединить эти компоненты в единое приложение – это написать что-то вроде:

Несмотря на простоту, данный код обладает серьёзными недостатками, которые являются критическими для больших проектов. Действительно, в данном примере вполне очевидно, что экземпляр класса ServiceDependency необходимо создавать раньше, чем экземпляр объекта MyService. А в больших проектах таких сервисов и зависимостей может быть столько, что перебор программистом порядка создания объектов занимал бы совсем неприличное время.

Хочется автоматического разрешения зависимостей, и чтобы даже не задумываться о создании объектов. Здесь и приходит на помощь Spring, а если быть точнее, то Spring Context. Модифицируем немного наши классы, добавив так называемые аннотации стереотипов.

И всё! Обратите внимание, что здесь не написано ни одно new наших сервисов.

Разберём подробнее

MainClass помечен аннотацией @Сonfiguration, говорящей о том, что в данном классе содержится конфигурация так называемого контекста Spring.

Как ни странно, из конфигурации в данном классе только аннотация @ComponentScan, которая говорит Spring искать все классы, помеченные Spring-аннотациями (в частности аннотациями стереотипов @Service).

Каждый класс помечен аннотацией @Service. Данные аннотация говорит Spring создать экземпляр объекта данного класса и положить его в некоторое множество таких объектов. Такие объекты, собственно, и называются beans, а множество таких объектов, соответственно, называется контекстом.

Ну и в методе main создаётся тот самый контекст: Spring автоматически определит зависимости beans друг с другом (начиная со Spring 4.0 наличие аннотации @Autowired не обязательно, если есть ровно один конструктор) и создаст экземпляры объектов в нужном порядке. Остаётся только получить экземпляр этого объекта и вызвать метод.

Данный пример, разумеется, рассматривает только один из вариантов создания beans в Spring Context. В действительности, возможностей Spring Context гораздо больше.

Есть вопрос? Напишите в комментариях!

Источник

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

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

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

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

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

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

Введение

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

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

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

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

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Источник

Spring изнутри. Этапы инициализации контекста

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

Доброго времени суток уважаемые хабравчане. Уже 3 года я работаю на проекте в котором мы используем Spring. Мне всегда было интересно разобраться с тем, как он устроен внутри. Я поискал статьи про внутреннее устройство Spring, но, к сожалению, ничего не нашел.

Всех, кого интересует внутреннее устройство Spring, прошу под кат.

На схеме изображены основные этапы поднятия ApplicationContext. В этом посте мы остановимся на каждом из этих этапов. Какой-то этап будет рассмотрен подробно, а какой-то будет описан в общих чертах.

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

1. Парсирование конфигурации и создание BeanDefinition

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

Xml конфигурация

Для Xml конфигурации используется класс — XmlBeanDefinitionReader, который реализует интерфейс BeanDefinitionReader. Тут все достаточно прозрачно. XmlBeanDefinitionReader получает InputStream и загружает Document через DefaultDocumentLoader. Далее обрабатывается каждый элемент документа и если он является бином, то создается BeanDefinition на основе заполненных данных (id, name, class, alias, init-method, destroy-method и др.). Каждый BeanDefinition помещается в Map. Map хранится в классе DefaultListableBeanFactory. В коде Map выглядит вот так.

Конфигурация через аннотации с указанием пакета для сканирования или JavaConfig

Конфигурация через аннотации с указанием пакета для сканирования или JavaConfig в корне отличается от конфигурации через xml. В обоих случаях используется класс AnnotationConfigApplicationContext.

Если заглянуть во внутрь AnnotationConfigApplicationContext, то можно увидеть два поля.

ClassPathBeanDefinitionScanner сканирует указанный пакет на наличие классов помеченных аннотацией @Component (или любой другой аннотацией которая включает в себя @Component). Найденные классы парсируются и для них создаются BeanDefinition.
Чтобы сканирование было запущено, в конфигурации должен быть указан пакет для сканирования.

Groovy конфигурация

Данная конфигурация очень похожа на конфигурацию через Xml, за исключением того, что в файле не XML, а Groovy. Чтением и парсированием groovy конфигурации занимается класс GroovyBeanDefinitionReader.

2. Настройка созданных BeanDefinition

После первого этапа у нас имеется Map, в котором хранятся BeanDefinition. Архитектура спринга построена таким образом, что у нас есть возможность повлиять на то, какими будут наши бины еще до их фактического создания, иначе говоря мы имеем доступ к метаданным класса. Для этого существует специальный интерфейс BeanFactoryPostProcessor, реализовав который, мы получаем доступ к созданным BeanDefinition и можем их изменять. В этом интерфейсе всего один метод.

Метод postProcessBeanFactory принимает параметром ConfigurableListableBeanFactory. Данная фабрика содержит много полезных методов, в том числе getBeanDefinitionNames, через который мы можем получить все BeanDefinitionNames, а уже потом по конкретному имени получить BeanDefinition для дальнейшей обработки метаданных.

Давайте разберем одну из родных реализаций интерфейса BeanFactoryPostProcessor. Обычно, настройки подключения к базе данных выносятся в отдельный property файл, потом при помощи PropertySourcesPlaceholderConfigurer они загружаются и делается inject этих значений в нужное поле. Так как inject делается по ключу, то до создания экземпляра бина нужно заменить этот ключ на само значение из property файла. Эта замена происходит в классе, который реализует интерфейс BeanFactoryPostProcessor. Название этого класса — PropertySourcesPlaceholderConfigurer. Весь этот процесс можно увидеть на рисунке ниже.

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

Давайте еще раз разберем что же у нас тут происходит. У нас имеется BeanDefinition для класса ClassName. Код класса приведен ниже.

Если PropertySourcesPlaceholderConfigurer не обработает этот BeanDefinition, то после создания экземпляра ClassName, в поле host проинжектится значение — «$» (в остальные поля проинжектятся соответсвующие значения). Если PropertySourcesPlaceholderConfigurer все таки обработает этот BeanDefinition, то после обработки, метаданные этого класса будут выглядеть следующим образом.

Соответственно в эти поля проинжектятся правильные значения.

Для того что бы PropertySourcesPlaceholderConfigurer был добавлен в цикл настройки созданных BeanDefinition, нужно сделать одно из следующих действий.

Для XML конфигурации.

PropertySourcesPlaceholderConfigurer обязательно должен быть объявлен как static. Без static у вас все будет работать до тех пор, пока вы не попробуете использовать @ Value внутри класса @Configuration.

3. Создание кастомных FactoryBean

На первый взгляд, тут все нормально и нет никаких проблем. А что делать если нужен другой цвет? Создать еще один бин? Не вопрос.

А что делать если я хочу каждый раз случайный цвет? Вот тут то и приходит на помощь интерфейс FactoryBean.

Создадим фабрику которая будет отвечать за создание всех бинов типа — Color.

Добавим ее в xml и удалим объявленные до этого бины типа — Color.

Теперь создание бина типа Color.class будет делегироваться ColorFactory, у которого при каждом создании нового бина будет вызываться метод getObject.

Для тех кто пользуется JavaConfig, этот интерфейс будет абсолютно бесполезен.

4. Создание экземпляров бинов

Созданием экземпляров бинов занимается BeanFactory при этом, если нужно, делегирует это кастомным FactoryBean. Экземпляры бинов создаются на основе ранее созданных BeanDefinition.

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

5. Настройка созданных бинов

Интерфейс BeanPostProcessor позволяет вклиниться в процесс настройки ваших бинов до того, как они попадут в контейнер. Интерфейс несет в себе несколько методов.

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

что такое контекст в spring. Смотреть фото что такое контекст в spring. Смотреть картинку что такое контекст в spring. Картинка про что такое контекст в spring. Фото что такое контекст в spring

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

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

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

По умолчанию, диапазон случайных числе будет от 0 до 10.

Затем, нужно создать обработчик этой аннотации, а именно реализацию BeanPostProcessor для обработки аннотации InjectRandomInt.

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

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

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

В итоге, все бины типа MyBean, получаемые из контекста, будут создаваться с уже проинициализированными полями value1 и value2. Также тут стоить отметить, этап на котором будет происходить инжект значений в эти поля будет зависеть от того какой @ Scope у вашего бина. SCOPE_SINGLETON — инициализация произойдет один раз на этапе поднятия контекста. SCOPE_PROTOTYPE — инициализация будет выполняться каждый раз по запросу. Причем во втором случае ваш бин будет проходить через все BeanPostProcessor-ы что может значительно ударить по производительности.

Полный код программы вы можете найти тут.

Хочу сказать отдельное спасибо EvgenyBorisov. Благодаря его курсу, я решился на написание этого поста.

Также советую посмотреть его доклад с JPoint 2014.

Источник

Весенние веб-контексты

Узнайте об общих способах настройки и организации контекстов приложений в веб-приложении Spring.

1. введение

При использовании Spring в веб-приложении у нас есть несколько вариантов организации контекстов приложений, которые связывают все это.

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

2. Контекст Корневого Веб-Приложения

Каждое веб-приложение Spring имеет связанный контекст приложения, связанный с его жизненным циклом: контекст корневого веб-приложения.

Это старая функция, которая предшествовала Spring Web MVC, поэтому она не привязана конкретно к какой-либо технологии веб-фреймворка.

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

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

2.1. ContextLoaderListener

По умолчанию прослушиватель загрузит контекст приложения XML из /WEB-INF/applicationContext.xml . Однако эти значения по умолчанию могут быть изменены. Например, мы можем использовать аннотации Java вместо XML.

Мы можем настроить этот прослушиватель либо в дескрипторе веб-приложения ( web.xml файл) или программно в средах Servlet 3.x.

В следующих разделах мы подробно рассмотрим каждый из этих вариантов.

2.2. Использование web.xml и XML ApplicationContext

Мы можем указать альтернативное расположение конфигурации контекста XML с помощью параметра contextConfigLocation :

Или несколько мест, разделенных запятыми:

Мы даже можем использовать шаблоны:

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

2.3. Использование web.xml и контекст приложения Java

Мы также можем указать другие типы контекстов, помимо контекста на основе XML по умолчанию. Давайте посмотрим, например, как вместо этого использовать конфигурацию аннотаций Java.

Каждый тип контекста может иметь расположение конфигурации по умолчанию. В нашем случае AnnotationConfigWebApplicationContext не имеет такового, поэтому мы должны его предоставить.

Таким образом, мы можем перечислить один или несколько аннотированных классов:

Или мы можем указать контексту сканировать один или несколько пакетов:

И, конечно, мы можем смешивать и сочетать эти два варианта.

2.4. Программная Конфигурация С Сервлетом 3.x

Версия 3 API сервлета произвела настройку через web.xml файл полностью необязателен. Библиотеки могут предоставлять свои веб-фрагменты, которые представляют собой части конфигурации XML, которые могут регистрировать прослушиватели, фильтры, сервлеты и так далее.

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

Модуль spring-web использует эти функции и предлагает свой API для регистрации компонентов приложения при его запуске.

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

2.5. Использование сервлета 3.x и контекста приложения XML

Давайте начнем с контекста XML, как и в разделе 2.2.

Мы реализуем вышеупомянутый метод OnStartup :

Давайте разберем реализацию по строкам.

Затем, во второй строке, мы сообщаем контексту, откуда загружать определения его компонентов. Опять же, setConfigLocations является программным аналогом параметра contextConfigLocation в web.xml :

Наконец, мы создаем ContextLoaderListener с корневым контекстом и регистрируем его в контейнере сервлета. Как мы видим, ContextLoaderListener имеет соответствующий конструктор, который принимает WebApplicationContext и делает его доступным для приложения:

2.6. Использование сервлета 3.x и контекста Java-приложения

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

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

Нам нужно только рассказать ему, как построить корневой контекст:

3. Контексты сервлетов диспетчера

Теперь давайте сосредоточимся на другом типе контекста приложения. На этот раз мы будем ссылаться на функцию, которая специфична для Spring MVC, а не является частью общей поддержки веб-приложений Spring.

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

Каждый DispatcherServlet имеет связанный контекст приложения. Бобы, определенные в таких контекстах, настраивают сервлет и определяют объекты MVC, такие как контроллеры и распознаватели представлений.

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

3.1. Использование web.xml и XML ApplicationContext

DispatcherServlet обычно объявляется в web.xml с именем и отображением:

Мы также можем указать один или несколько путей к XML-файлам, аналогично ContextLoaderListener :

3.2. Использование web.xml и контекст приложения Java

3.3. Использование сервлета 3.x и контекста приложения XML

Итак, давайте начнем с общего WebApplicationInitializer и контекста приложения XML.

Мы можем легко провести параллель между приведенным выше кодом и эквивалентом web.xml элементы конфигурации.

3.4. Использование сервлета 3.x и контекста Java-приложения

Это абстрактный класс, который, помимо создания корневого webapplicationcontext, как было показано ранее, позволяет нам зарегистрировать один сервлет диспетчера с минимальным шаблоном:

4. Родительский и дочерний контексты

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

Оказывается, да, это так. Фактически, корневой контекст является родительским для каждого контекста dispatcherservlet. Таким образом, компоненты, определенные в корневом webapplicationcontext, видны каждому контексту dispatcherservlet, но не наоборот.

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

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

В более простых приложениях MVC достаточно иметь один контекст, связанный только с одним сервлетом диспетчера. Нет необходимости в чрезмерно сложных решениях!

Тем не менее, отношения “родитель-потомок” становятся полезными, когда у нас настроено несколько сервлетов диспетчера. Но когда мы должны беспокоиться о том, чтобы иметь больше одного?

В общем случае мы объявляем несколько сервлетов диспетчера , когда нам нужно несколько наборов конфигурации MVC. Например, у нас может быть REST API наряду с традиционным приложением MVC или незащищенным и безопасным разделом веб-сайта:

Примечание: при расширении AbstractDispatcherServletInitializer (см. раздел 3.4) мы регистрируем как контекст корневого веб-приложения, так и один сервлет диспетчера.

Кроме того, обратите внимание, что AbstractDispatcherServletInitializer регистрирует сервлет под заданным именем ( dispatcher ), и, конечно, у нас не может быть нескольких сервлетов с одним и тем же именем. Итак, нам нужно переопределить getServletName :

5. Пример родительского и дочернего контекста

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

Кроме того, предположим, что некоторым контроллерам нужна служба, которая содержит значительные ресурсы; повсеместным случаем является постоянство. Затем мы захотим создать экземпляр этой службы только один раз, чтобы избежать удвоения использования ресурсов и потому, что мы верим в принцип “Не повторяйся”!

Давайте теперь перейдем к примеру.

5.1. Общий Сервис

В нашем примере с hello world мы остановились на более простой службе приветствия вместо настойчивости:

Мы объявим службу в корневом webapplicationcontext, используя сканирование компонентов:

Вместо этого мы могли бы предпочесть XML:

5.2. Контроллеры

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

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

5.3. Контексты сервлета Диспетчера

Как мы уже говорили ранее, у нас будет два разных контекста dispatcherservlet, по одному для каждого контроллера. Итак, давайте определим их на Java:

Или, если мы предпочитаем, в XML:

5.4. Собрать Все Это Воедино

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

Мы определим AbstractContextLoaderInitializer для загрузки корневого контекста:

Затем “безопасный”, который загружает другой контекст и сопоставляется с другим путем:

И мы закончили! Мы только что применили то, чего касались в предыдущих разделах.

Определение контекста корневого приложения:

“Нормальный” контекст диспетчера:

И, наконец, “безопасный” контекст:

6. Объединение Нескольких Контекстов

Есть и другие способы, кроме родительско-дочерних, объединить несколько конфигурационных местоположений, разделить большие контексты и лучше разделить различные проблемы. Мы уже видели один пример: когда мы указываем contextConfigLocation с несколькими путями или пакетами, Spring создает единый контекст, объединяя все определения компонентов, как если бы они были написаны в одном XML-файле или классе Java, по порядку.

Однако мы можем достичь аналогичного эффекта другими средствами и даже использовать разные подходы вместе. Давайте рассмотрим наши варианты.

6.1. Импорт Контекста В Другой

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

Импорт класса @Configuration в Java:

Загрузка какого-либо другого типа ресурса, например определения контекста XML, в Java:

Наконец, включение XML-файла в другой файл:

Таким образом, у нас есть много способов организовать службы, компоненты, контроллеры и т. Д., Которые сотрудничают для создания нашего потрясающего приложения. И самое приятное, что идеи понимают их все!

7. Веб-приложения для весенней загрузки

Spring Boot автоматически настраивает компоненты приложения, поэтому, как правило, меньше нужно думать о том, как их организовать.

Тем не менее, под капотом Boot использует пружинные функции, в том числе те, которые мы видели до сих пор. Давайте рассмотрим несколько примечательных различий.

Веб-приложения Spring Boot, работающие во встроенном контейнере не запускают WebApplicationInitializer по дизайну.

Если это необходимо, мы можем написать ту же логику в SpringBootServletInitializer или ServletContextInitializer вместо этого, в зависимости от выбранной стратегии развертывания.

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

Определенные таким образом объекты сопоставляются в соответствии с соглашениями: фильтры автоматически сопоставляются с/*, то есть с каждым запросом. Если мы регистрируем один сервлет, он сопоставляется с/, в противном случае каждый сервлет сопоставляется с его именем компонента.

8. Выводы

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

Источник

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

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