где инициализируется контекст приложения в mvc
Spring MVC — основные принципы
Фреймворк Spring MVC обеспечивает архитектуру паттерна Model — View — Controller (Модель — Отображение (далее — Вид) — Контроллер) при помощи слабо связанных готовых компонентов. Паттерн MVC разделяет аспекты приложения (логику ввода, бизнес-логику и логику UI), обеспечивая при этом свободную связь между ними.
DispatcherServlet
Вся логика работы Spring MVC построена вокруг DispatcherServlet, который принимает и обрабатывает все HTTP-запросы (из UI) и ответы на них. Рабочий процесс обработки запроса DispatcherServlet’ом проиллюстрирован на следующей диаграмме:
Ниже приведена последовательность событий, соответствующая входящему HTTP-запросу:
Конфигурирование
Вам будет необходимо связать (замапить) запросы, которые Вы хотите обработать при помощи DispatcherServlet, используя мапинг URL в файле web.xml. Ниже приведён пример объявления и мапинга DispatcherServlet’а HelloWeb:
Файл web.xml будет находиться в каталоге WebContent/WEB-INF. После инициализации HelloWeb, фреймворк попытается загрузить контекст приложения из файла с именем [servlet-name]-servlet.xml, находящегося в каталоге WebContent/WEB-INF. В нашем случае, это будет HelloWeb-servlet.xml.
Далее, тэг указывает, какие веб-адреса обрабатываются каким DispatcherServlet’ом. В нашем случае, все HTTP-запросы, заканчивающиеся на «.jsp», будут обработаны HelloWeb.
Если Вы не хотите использовать [servlet-name]-servlet.xml / WebContent/WEB-INF в качестве файла и директории по умолчанию, Вы можете настроить имя файла и директорию, добавив слушатель сервлета (servlet listener) ContextLoaderListener в web.xml, как показано ниже:
Теперь давайте проверим конфигурацию для HelloWeb-servlet.xml, размещённую в каталоге WebContent/WEB-INF:
Ниже приведены важные моменты в HelloWeb-servlet.xml:
Определение Контроллера
DispatcherServlet отправляет запрос контроллерам для выполнения определённых функций. Аннотация @Controllerannotation указывает, что конкретный класс является контроллером. Аннотация @RequestMapping используется для мапинга (связывания) с URL для всего класса или для конкретного метода обработчика.
Аннотация Controller определяет класс как Контроллер Spring MVC. В первом случае, @RequestMapping указывает, что все методы в данном Контроллере относятся к URL-адресу «/hello». Следующая аннотация @RequestMapping(method = RequestMethod.GET) используется для объявления метода printHello() как дефолтного метода для обработки HTTP-запросов GET (в данном Контроллере). Вы можете определить любой другой метод как обработчик всех POST-запросов по данному URL-адресу.
Вы можете написать вышеуказанный Контроллер по-другому, указав дополнительные атрибуты для аннотации @RequestMapping следующим образом:
Атрибут «value» указывает URL, с которым мы связываем данный метод (value = «/hello»), далее указывается, что этот метод будет обрабатывать GET-запросы (method = RequestMethod.GET). Также, нужно отметить важные моменты в отношении приведённого выше контроллера:
Создание Вида (JSP)
Spring MVC поддерживает множество типов Видов для различных технологий отображения страницы. В том числе — JSP, HTML, PDF, Excel, XML, Velocity templates, XSLT, JSON, каналы Atom и RSS, JasperReports и проч. Но чаще всего используются шаблоны JSP, написанные при помощи JSTL.
Давайте напишем простой Вид «hello» в /WEB-INF/hello/hello.jsp:
В данном случае, переменная $ выводит тот самый атрибут, установленный нами в Контроллере. Внутри Вида Вы можете отобразить любое количество атрибутов.
Примеры реализации фреймворка Spring MVC
Основываясь на приведённых выше концепциях, предлагаю выполнить несколько важных уроков, которые в дальнейшем помогут нам создавать приложения Spring Web:
Spring MVC Hello World Example
Пример, разъясняющий написание простейшего приложения Hello World.
Spring MVC Form Handling Example
В этом примере объясняется, как написать приложение Spring Web с помощью форм HTML, отправить данные контроллеру и отобразить обработанный результат.
Spring Static Pages Example
Получаем доступ к статическим страницам вместе с динамическими.
Русские Блоги
Процесс инициализации контекста Spring mvc
Во-первых, посмотрите на Spring MVC с точки зрения веб-приложений.
В модели сервлета реализация запроса-ответа зависит от взаимодействия двух основных элементов:
1. Настройте сервлет и его отношение отображения (в web.xml)
2. Завершите логику ответа в классе реализации сервлета.
После расширения масштаба проекта все отношения отображения запроса и ответа определены в web.xml, что приведет к постоянному расширению web.xml и станет трудным в обслуживании. В ответ на эту проблему SpringMVC предлагает следующее решение:Уточните основной сервлет, чтобы охватить обработку всех запросов Http.Этот усовершенствованный сервлет обычно называется:Основной дистрибьютор. В SpringMVC основным дистрибьютором являетсяorg.springframework.web.servlet.DispatcherServlet。
Основной дистрибьютор должен решить следующие две проблемы:
Вопрос 1: основной сервлет должен иметь возможность устанавливать полный набор процедур для стандартизированной обработки всех запросов Http.
Вопрос 2: основной сервлет должен иметь возможность распределять разные запросы Http на разные объекты сервлета для обработки в соответствии с определенными правилами.
В ответ на две вышеупомянутые проблемы решение SpringMVC:Стандартизируйте весь поток обработки и назначьте каждый этап обработки различным компонентам для обработки。
Стандартизируйте поток обработки: разделите поток обработки на несколько шагов (задач) и используйте четкую логическую главную линию для последовательного соединения всех шагов
Компонентизация потока обработки: определите каждый шаг (задачу) в потоке обработки как интерфейс и назначьте каждому интерфейсу свой режим реализации.
Основным содержанием стандартизации потока обработки является рассмотрение логических шагов, которые должна включать общая программа отклика сервлета:
Так называемая компонентность на самом деле представляет собой использование языков программирования для выражения этой логической семантики. В языке Java наиболее подходящей грамматической структурой для выражения семантики логической обработки является интерфейс, и интерфейс может иметь разные реализации. Таким образом, указанные выше четыре процесса определены как четыре разных интерфейса. Это:
Во-вторых, посмотрите на Spring MVC с точки зрения Spring.
Из вышесказанного видно, что компоненты являются ядром основного диспетчера (DispatchServlet), они являются логическим носителем обработки HTTP-запросов, DispatcherServlet является диспетчерским центром обработки логики, а компоненты являются объектами диспетчеризации операций. Роль контейнера Spring здесь состоит в том, чтобы помочь DispatcherServlet лучше управлять компонентами.
Мы знаем, что компоненты SpringMVC определены как интерфейсы. Когда мы определяем компонент в основном файле конфигурации SpringMVC, мы используем класс реализации компонента. Конкретный класс реализации используется для указания режима поведения компонента. Классы реализации представляют разные модели поведения, и они могут сосуществовать весной. Контейнер Spring управляет этими классами реализации, а способ их использования определяется самим приложением.
Вышеупомянутое изображение является изображением из официального справочника Spring. DispatchServlet извне получает HTTP-запросы, и обработка запросов выполняется компонентом, а интерфейс компонента управляется контейнером Spring IOC (WebApplicationContext). Из этого рисунка видно, что реализация веб-приложений Spring MVC зависит от основных функций (IOC и т. Д.), Предоставляемых Spring. Разница между двумя контекстами WebApplicationContexts на рисунке будет обсуждена позже.
Три, файл конфигурации входа Spring MVC web.xml
Какие файлы конфигурации есть у Spring mvc:
Spring изнутри. Этапы инициализации контекста
Доброго времени суток уважаемые хабравчане. Уже 3 года я работаю на проекте в котором мы используем Spring. Мне всегда было интересно разобраться с тем, как он устроен внутри. Я поискал статьи про внутреннее устройство Spring, но, к сожалению, ничего не нашел.
Всех, кого интересует внутреннее устройство Spring, прошу под кат.
На схеме изображены основные этапы поднятия ApplicationContext. В этом посте мы остановимся на каждом из этих этапов. Какой-то этап будет рассмотрен подробно, а какой-то будет описан в общих чертах.
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. Весь этот процесс можно увидеть на рисунке ниже.
Давайте еще раз разберем что же у нас тут происходит. У нас имеется BeanDefinition для класса ClassName. Код класса приведен ниже.
Если PropertySourcesPlaceholderConfigurer не обработает этот BeanDefinition, то после создания экземпляра ClassName, в поле host проинжектится значение — «$
Соответственно в эти поля проинжектятся правильные значения.
Для того что бы PropertySourcesPlaceholderConfigurer был добавлен в цикл настройки созданных BeanDefinition, нужно сделать одно из следующих действий.
Для XML конфигурации.
PropertySourcesPlaceholderConfigurer обязательно должен быть объявлен как static. Без static у вас все будет работать до тех пор, пока вы не попробуете использовать @ Value внутри класса @Configuration.
3. Создание кастомных FactoryBean
На первый взгляд, тут все нормально и нет никаких проблем. А что делать если нужен другой цвет? Создать еще один бин? Не вопрос.
А что делать если я хочу каждый раз случайный цвет? Вот тут то и приходит на помощь интерфейс FactoryBean.
Создадим фабрику которая будет отвечать за создание всех бинов типа — Color.
Добавим ее в xml и удалим объявленные до этого бины типа — Color.
Теперь создание бина типа Color.class будет делегироваться ColorFactory, у которого при каждом создании нового бина будет вызываться метод getObject.
Для тех кто пользуется JavaConfig, этот интерфейс будет абсолютно бесполезен.
4. Создание экземпляров бинов
Созданием экземпляров бинов занимается BeanFactory при этом, если нужно, делегирует это кастомным FactoryBean. Экземпляры бинов создаются на основе ранее созданных BeanDefinition.
5. Настройка созданных бинов
Интерфейс BeanPostProcessor позволяет вклиниться в процесс настройки ваших бинов до того, как они попадут в контейнер. Интерфейс несет в себе несколько методов.
Процесс донастройки показан на рисунке ниже. Порядок в котором будут вызваны BeanPostProcessor не известен, но мы точно знаем что выполнены они будут последовательно.
Для того, что бы лучше понять для чего это нужно, давайте разберемся на каком-нибудь примере.
При разработке больших проектов, как правило, команда делится на несколько групп. Например первая группа разработчиков занимается написанием инфраструктуры проекта, а вторая группа, используя наработки первой группы, занимается написанием бизнес логики. Допустим второй группе понадобился функционал, который позволит в их бины инжектить некоторые значения, например случайные числа.
На первом этапе будет создана аннотация, которой будут помечаться поля класса, в которые нужно проинжектить значение.
По умолчанию, диапазон случайных числе будет от 0 до 10.
Затем, нужно создать обработчик этой аннотации, а именно реализацию BeanPostProcessor для обработки аннотации InjectRandomInt.
Код данного BeanPostProcessor достаточно прозрачен, поэтому мы не будем на нем останавливаться, но тут есть один важный момент.
BeanPostProcessor обязательно должен быть бином, поэтому мы его либо помечаем аннотацией @Component, либо регестрируем его в xml конфигурации как обычный бин.
Первая группа разработчиков свою задачу выполнила. Теперь вторая группа может использовать эти наработки.
В итоге, все бины типа MyBean, получаемые из контекста, будут создаваться с уже проинициализированными полями value1 и value2. Также тут стоить отметить, этап на котором будет происходить инжект значений в эти поля будет зависеть от того какой @ Scope у вашего бина. SCOPE_SINGLETON — инициализация произойдет один раз на этапе поднятия контекста. SCOPE_PROTOTYPE — инициализация будет выполняться каждый раз по запросу. Причем во втором случае ваш бин будет проходить через все BeanPostProcessor-ы что может значительно ударить по производительности.
Полный код программы вы можете найти тут.
Хочу сказать отдельное спасибо EvgenyBorisov. Благодаря его курсу, я решился на написание этого поста.
Также советую посмотреть его доклад с JPoint 2014.
Русские Блоги
1, полный короткий
содержание
DispatcherServlet expects a WebApplicationContext (an extension of a plain ApplicationContext ) for its own configuration. WebApplicationContext has a link to the ServletContext and the Servlet with which it is associated. It is also bound to the ServletContext such that applications can use static methods on RequestContextUtils to look up the WebApplicationContext if they need access to it.
For many applications, having a single WebApplicationContext is simple and suffices. It is also possible to have a context hierarchy where one root WebApplicationContext is shared across multiple DispatcherServlet (or other Servlet ) instances, each with its own child WebApplicationContext configuration.
DispatcherServlets требуют контекста для собственной конфигурации. Этот контекст имеет ссылку на сервелконтестик и связанный с ним сервлетом. Он также связывается с серветовым элементом, так что приложение может получить доступ к этому контексту с помощью статического метода запросовContextIL.
Для многих приложений просты и достаточно, чтобы иметь контекст. Тем не менее, также может быть иерархический контекст, один из которых используется в нескольких экземплярах диспетчеров, каждый экземпляр имеет собственную контекстную конфигурацию.
Корневой контекст обычно содержит базовые бобы, такие как базы данных и бизнес-услуги, которые необходимо поделиться между несколькими экземплярами сервлета. Эти бобы могут быть унаследованы и могут быть переписаны в подтексту (то есть, переопределено), а дочерний контекст обычно содержит локальные бобы текущего сервлета.
Иерархическая структура выглядит следующим образом:
Эти два контекста инициализируются в процессе запуска контейнера, затем, как контейнер знает их, мы знаем, что инициализация веб-контейнера сначала загружается файл Web.xml, и соответствует определению слушателя и сервлета. Загрузка и Инициализация. Затем мы можем настроить слушатель (ContextLoaderListener) или сервлете (DispatcherServlet), контейнер загрузит и инициализирует их в соответствии с Web.xml, и они несут ответственность за загрузку соответствующего профиля.
В файле конфигурации Web.xml есть две основные конфигурации: контекстно-процессоры и диспетчерСерсервисты. Та же конфигурация о пружинном профиле также имеет две части: init-param и init-paral в диспетчере. Затем разница между двумя частями является инициализация весеннего контейнера и инициализации веб-контейнера. DispatcherServlet и ContextloaderListener предоставляют интерфейсы пружину в веб-контейнере. ServletContext предоставляет среду хост-среды для контейнера IOC IOC. В среде Host Spring MVC установил контейнерную систему IOC.
Посмотрите на конфигурацию web.xml:
2, инициализация корневого контекста
Сама Spring Framework не имеет веб-функции, Spring MVC использует интерфейс webapplicationContext, чтобы расширить ApplicationContext, чтобы реализация по умолчанию WebapplicationContext INSEXT IS XMLWebaplicationContext. Так как же весна MVC создала контейнер IOC в веб-среде? Давайте посмотрим на исходный код webapplationContext:
Что такое контекст Spring и как его создать?
Контекст (а у него есть даже интерфейс — org.springframework.context.ApplicationContext ) — это некоторое окружение, в котором работает приложение на Spring Framework. Страшные аббревиатуры DI, IoC — это всё про него. Собственно, контекст создаёт и хранит экземпляры классов вашего приложения, определяет их зависимости друг с другом и автоматически их задаёт.
Безусловно, для того чтобы Spring создал контекст с экземплярами классов, ему нужно предоставить дополнительную информацию — мета-данные, из каких классов/объектов состоит ваше приложение, как они создаются, какие у них есть зависимости и т. д.
Итого: Spring Context + мета-данные = работающее приложение.
Где найти контекст?
Контекст является ключевой функциональностью Spring и лежит в maven-зависимости spring-context (на момент написания — org.springframework:spring-context:5.1.4.RELEASE ). Обычно эта зависимость является транзитивной для остальных проектов Spring. И если вы, например, подключаете spring-boot-starter, то она подключится автоматически, и не нужно думать про то, где её взять.
Но если вы хотите попробовать «голый» Spring, т. е. только ту часть, которая называется IoC-контейнер, то достаточно подключить лишь spring-context.
Какие бывают контексты и как их создать?
У интерфейса ApplicationContext есть большое количество реализаций: — ClassPathXmlApplicationContext ; — FileSystemXmlApplicationContext ; — GenericGroovyApplicationContext ; — AnnotationConfigApplicationContext ; — и даже StaticApplicationContext ; — а также некоторые другие.
Они отличаются друг от друга именно тем, каким способом задаются мета-данные и где хранится эта конфигурация. Например: — ClassPathXmlApplicationContext — метаданные конфигурируются XML-файлом(-ами) и они лежат в classpath, т. е. в ресурсах модуля; — FileSystemXmlApplicationContext — метаданные тоже конфигурируются XML-файлом(-ами), но они находятся где-то в файловой системе, например, /etc/yourapp/spring-context.xml ; — AnnotationConfigApplicationContext — метаданные конфигурируются с помощью аннотаций прямо на классах.
Современным способом конфигурирования считаются аннотации ( AnnotationConfigApplicationContext ), дальше будем создавать именно их.
Приведём пример создания такого контекста в методе main:
Итого: создаём контекст.
Следите за новостями, оставляйте комментарии и посмотрите программу курса «Разработчик на Spring Framework», вдруг захочется погрузится полностью!