Что такое spring mvc
Введение в Spring Frameworks: Spring MVC
Это моя первая статья.Прошу сильно молотком не бить.
Сегодня, этим сообщением начнется мой цикл статей о фреймворках Spring.
Я не буду вдаваться в тонкости теории, здесь на хабре достаточно статей об этом. Предпочитаю практику.
Тем не менее, стоит напомнить, что работать со Spring Framework можно как с помощью xml-конфигурации, так и с помощью аннотаций. Также, ничего не мешает комбинировать оба подхода.
Лично я предпочитаю работать с аннотациями, чего и вам советую. Поэтому все примеры, которые я буду приводить, будут использовать аннотации.
Думаю, стоит рассказать, что же мы сегодня будем делать.
Сегодня мы создадим простое веб-приложение, которое будет считать сумму двух чисел и, естественно, отправлять пользователю ответ.
Работать будем в IDE Intellij IDEA 12. Приступим.
Создадим новый проект (File-New Project).
Выбираем «Тип проекта» — Spring MVC. Даем проекту имя (Project name) и жмем Finish. При желании можно указать другой базовый пакет (Base package), так я изменил его на «com.springapp.controllers». Также, если можно указать путь к JDK (Project SDK) и Серверу(Application server), на котором приложение будет развертываться. Как можно заметить я использую 7 версию Java и сервер Glassfish.
Проект создан и должен иметь такую структуру.
Это шаблонный проект, который использует xml-конфигурацию. Но ведь мы хотим использовать аннотации. Значит удалим лишнее.
Удаляем файл mvc-dispatcher-servlet.xml и папку test (тесты в ней используют файл mvc-dispatcher-servlet.xml).
Лишний раз приведу скрин того, что должно быть на этом шаге.
Теперь необходимо создать тот самый dispatcher-servlet, только с помощью обычного java-класса. Я считаю, что программа всегда должна быть хорошо структурированна и, думаю, многие меня поддержат. Для начала создадим новый пакет «config», который будет хранить всю конфигурацию Spring. Для этого выбираем папку java и нажимаем «ALT-INSERT». Вводим имя пакета (com.springapp.config) и жмакаем «ОК».
Пакет создан. Добавим в него новый класс. Для этого, все также — ALT-INSERT и выбираем «Class». Назовем его MvcConfig.
Созданный класс должен иметь приблизительно такой вид.
Приступим к созданию конфигурации. Я приведу сразу готовый конфигурационный класс и ниже опишу все необходимые детали.
Начнем с самого начала.
@Configuration — говорим о том, что данный класс является конфигурационным для Spring, тоесть включает конфигурацию бинов.
@ComponentScan — указываем пакет, к котором будут искаться необходимые директивы для взаимодействия с бинами.
@EnableWebMvc — указываем, что данный класс является конфигурационным для Spring MVC.
@Bean — говорим о том, что аннотированный метод является бином.
Аннотацию @Override думаю знают все, кто хоть немного работал с Java.
Теперь более подробно о самих методах класса.
В методе getViewResolver() мы указываем, на мой взгляд, самый простой просмотрщик страниц, конфигурация которого состоит из добавления префикса и суфикса к странице.
В методе addResourceHandlers(ResourceHandlerRegistry registry) мы указываем расположение всех ресурсов, которые будут использоваться для страниц. Это могут быть и каскадные таблицы стилей, и java-script файлы, изображения и прочее.
Вот такая простая конфигурация.
Теперь необходимо объяснить веб-контейнеру, что мы будем использовать этот класс в качестве диспатчера. Для этого необходимо отредактировать файл web.xml. Идея создала для нас такой шаблонный код.
Нам необходимо заменить его этим
Детально расписывать не буду, скажу только — мы указываем, что конфигурация контекста и диспатчера выполняется с помощью аннотаций (c помощью параметра contextClass и его значения org.springframework.web.context.support.AnnotationConfigWebApplicationContext). Также указываем, в каком пакете искать конфигурационные классы (параметр contextConfigLocation).
Далее необходимо создать контроллер. Контроллер в Spring выполняет трасляцию адресов (то что мы видим в браузере) и перенаправление на страницы (в нашем случае hello.jsp).
Так как, Идея создала базовый контроллер за нас, нам не прийдется писать его вручную. На всякий случай, я приведу его код и поясню, что там происходит.
Начнем с аннотаций.
@Controller — собственно говорим о том, что это контроллер. Добавить нечего
@RequestMapping(«/») — с помощью этой аннотации указываем область видимости для этого котроллера (если эта аннотация применина к классу) или конкретный транслируемый адрес (если это метод). Также можно указать при каком конкретном запросе будет транслироваться тот или инной адрес (параметр method). В данной случае мы говорим о том, что данный контроллер будет видеть все адреса (то, что в браузере) и пытаться траслировать их на нужную страницу. Для большей ясности приведу пример.
Допустим есть 2 адреса mydomain.com/info, mydomain.com/about. Если мы указаем в RequestMapping вместо «/» — «/about», то контроллер никогда не будет пытаться траслировать адрес mydomain.com/info, так как просто не будем видеть его.
О том, что делает метод printWelcome(). Данный метод просто передает на страницу сообщение «Hello World» и, естественно, указывает имя самой страницы. Ну а с помощью просмотрщика, который мы описали в классе диспатчера (MvcConfig) это имя транслируется в полнить путь к странице, с помощью добавления префикса и суфикса.
Осталось привести только код самой страницы hello.jsp.
Собственно, можно приступить к развертке приложения. Для этого используем кнопку запуска.
Ждем пока, запустится сервер, развернется приложение. И вуаля, видим всеми любимый «Hello World».
Раз все работает, как надо, можно приступить к реализации просчета суммы двух чисел.
Для начала изменим нашу jsp страницу, добавив в нее форму для ввода чисел.
Ничего сложного. Мы добавили два поля для ввода чисел, кнопку отправки и дериктиву вывода результата. Для простоты, я решил использовать для вывода результата ту же страницу.
И последнее, добавить метод обработки POST-запроса в наш котроллер.
В этом методе мы, с помощью аннотации @RequestMapping указываем метод обработки POST, а также с помощью аннотации @RequestParam получает информацию из полей формы (value1, value2).
Запускаем (Идея предложит 4 варианта, выбираем «Redeploy»), видим, что наша форма есть.
Собственно, на этом все.
В следующий раз мы разберем, что такое Spring Data JPA и как с ним работать.
Вся информация, которую я привел является сугубо моим опытом или мнением и не претендует на эталон или стандарт. Я показываю способы как МОЖНО сделать, а не как НЕОБХОДИМО.
Буду рад конструктивной критике, так как опыт в сфере «статьестроя» у меня не большой.
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 — основные понятия и архитектура
В статье рассмотрим основные принципы взаимодействия популярного Java-фреймворка Spring и базового паттерна разработки MVC. Это отличное введение для новичка в практику использования Spring в современном программировании.
1. Что такое Spring MVC?
Spring MVC — структура для создания слабо связанных веб-приложений, разделяющая основные аспекты их разработки: объекты, бизнес-логику и внешний вид программы. Основное преимущество архитектуры MVC — возможность менять один из компонентов приложения, существенно не влияя на остальные.
Кратко пройдемся по каждому из них:
2. DispatcherServlet
Dispatcher Servlet — сердце Spring Web MVC, полностью настраиваемый фронт-контроллер, координирующий все действия по обработке запросов
аналог Struts ActionServlet / JSF FacesServlet :
3. Жизненный цикл запроса
Принцип его работы показан на изображении выше, каждый шаг промаркирован, давайте разберем все поэтапно:
4. Конфигурирование
Шаги по реализации веб-приложения MVC Spring с использованием Java-конфигурирования:
Чтобы включить автоопределение аннотированных контроллеров, необходимо добавить в конфигурацию сканирование компонентов. Он также дает путь к базовому пакету (например, today.highload.web ) в котором нужно искать файлы контроллера. Этот класс расширяет класс WebMvcConfigurerAdapter для обслуживания сервлета диспетчера.
Шаг 4: Теперь нам нужно определить контроллер. Контроллеры интерпретируют вводимые пользователем данные и преобразуют их в модель.
Шаг 5: Теперь нам нужно создать индексную страницу index.jsp для приложения, отображаемую при переходе по заданному URL-адресу:
5. Определение Контроллера
Разберем пример с применением нескольких распространенных аннотаций Spring MVC при отправлении запроса контроллерам от сервлета-диспетчера.
Аннотация @Controller говорит о том, что класс является контроллером Spring MVC. Аннотация @RequestMapping нужна для маппинга (связывания) с URL класса или конкретного метода обработчика.
6. Создание вида (JSP)
Используя фреймворк Spring MVC, отобразить страницу можно в десятках разных форматов, в числе которых — JSP, HTML, PDF, Excel, XML, Velocity templates, XSLT, JSON, каналы Atom и RSS, JasperReports и другие. Однако чаще всего применяются шаблоны JSP, написанные при помощи JSTL:
Преимущества Spring MVC
Собирая все вместе, подведем итоги:
7. Пример реализации фреймворка Spring MVC
Чтобы досконально разобраться с рассматриваемой концепцией, предлагаю ознакомиться с примерами реализации фреймворка из общедоступных источников:
Ну а если вам лучше один раз посмотреть и послушать, чем десять раз прочитать — несколько ссылок на обучающие видеоролики по теме:
Что такое Spring Framework? От внедрения зависимостей до Web MVC
Вы можете использовать это руководство для различных целей:
9000 слов, вероятно, не стоит читать ее на мобильном устройстве. Добавьте ее в закладки и вернитесь позже. И даже на компьютере ешь читай этого слона по одному кусочку за раз 🙂
Содержание
Примечание переводчика:
Текст статьи весьма большой и в переводе могут остаться неточности несмотря на многократную вычитку текста. Буду благодарен всем за конструктивные замечания.
Введение
Сложность экосистемы Spring
Многие компании используют Spring, однако когда вы захотите узнать об этом фреймворке и перейдете на сайт spring.io, то увидите, что вселенная Spring на самом деле состоит из 21 различных активных проектов. Ой!
Кроме того, если вы начали программировать с помощью Spring в последние пару лет, очень велика вероятность того, что вы перешли непосредственно к Spring Boot или Spring Data.
Однако это руководство касается только одного, самого важного из этих проектов: Spring Framework. Почему?
Потому что важно понимать, что Spring Framework является основой для всех других проектов. Spring Boot, Spring Data, Spring Batch — все это построено поверх Spring.
Это имеет два последствия:
15 минут на чтение этого руководства, которое охватывает самые важные 80% Spring Framework, многократно окупятся в вашей профессиональной карьере.
Что такое Spring Framework?
Краткий ответ:
По сути Spring Framework представляет собой просто контейнер внедрения зависимостей, с несколькими удобными слоями (например: доступ к базе данных, прокси, аспектно-ориентированное программирование, RPC, веб-инфраструктура MVC). Это все позволяет вам быстрее и удобнее создавать Java-приложения.
Только это не очень помогает, не так ли?
К счастью, есть и длинный ответ:
Остальная часть этого руководства.
Основы внедрения зависимостей
Если вы уже знаете, что такое внедрение зависимостей, не стесняйтесь переходить прямо к следующему разделу Контейнер Spring IOC / Dependency Injection. В противном случае читайте дальше.
Что такое зависимость?
Представьте, что вы пишете Java класс, который позволяет вам получить доступ к таблице пользователей в вашей базе данных. Вы бы назвали эти классы DAO (объект доступа к данным) или репозитории. Итак, вы собираетесь написать класс UserDAO.
Ваш класс UserDAO имеет только один метод, который позволяет вам находить пользователей в вашей таблице базы данных по их идентификаторам.
Чтобы выполнить соответствующий SQL запрос, вашему классу UserDAO требуется соединение с базой данных. А в мире Java вы, обычно, получаете соединение с базой данных из другого класса, называемого DataSource. Итак, ваш код теперь будет выглядеть примерно так:
Внедрение зависимостей с помощью new()
Наивным решением было бы просто создать новый источник данных с помощью конструктора, каждый раз, когда он вам нужен. Итак, для подключения к базе данных MySQL ваш UserDAO может выглядеть так:
Это работает, но давайте посмотрим, что произойдет, когда мы расширим наш класс UserDao другим методом, findByFirstName.
К сожалению, этому методу также нужен источник данных для работы. Мы можем добавить этот новый метод к нашему UserDAO и применить некоторые рефакторинги, введя метод newDataSource.
Этот подход работает, но имеет два недостатка:
Что произойдет, если мы хотим создать новый класс ProductDAO, который также выполняет операторы SQL? Ваш ProductDAO также будет иметь зависимость DataSource, которая теперь доступна только в вашем классе UserDAO. Затем у вас будет другой подобный метод или извлечен вспомогательный класс, содержащий ваш DataSource.
Мы создаем совершенно новый источник данных для каждого запроса SQL. Учтите, что DataSource открывает реальное сокет-соединение от вашей Java-программы к вашей базе данных. Это занимает время и довольно дорого. Было бы намного лучше, если бы мы открыли только один источник данных и использовали его повторно, вместо того, чтобы открывать и закрывать их тонны. Одним из способов сделать это может быть сохранение источника данных в закрытом поле в нашем UserDao, чтобы его можно было повторно использовать между методами, но это не помогает при дублировании между несколькими DAO.
Зависимости в глобальном классе приложения
Чтобы решить эти проблемы, вы можете подумать о написании глобального класса Application, который выглядит примерно так:
Ваш класс UserDAO теперь может выглядеть так:
Это улучшение по двум направлениям:
Однако у этого решения есть еще несколько недостатков:
Инверсия управления (IoC, Inversion of Control)
Давайте сделаем еще один шаг вперед.
Было бы неплохо, если бы вам в классе UserDAO вообще не приходилось беспокоиться о поиске зависимостей? Вместо того, чтобы активно вызывать Application.INSTANCE.dataSource(), ваш UserDAO мог бы (как-то) кричать, что он ему нужен, но больше не контролирует, когда / как / откуда он его получает?
Это то, что называется инверсией управления (Inversion of Control).
Давайте посмотрим, как может выглядеть наш класс UserDAO, с новым конструктором.
С точки зрения UserDao это выглядит намного лучше. Он больше не знает ни о классе приложения, ни о том, как создавать сами источники данных. Он только объявляет миру, что «если вы хотите создать (то есть использовать) меня, вам нужно дать мне источник данных».
Но представьте, что вы хотите запустить свое приложение. Если раньше вы могли вызывать «new UserService()», то теперь вам нужно обязательно вызвать новый UserDao(dataSource).
Контейнеры для внедрения зависимостей
Следовательно, проблема в том, что вы, как программист, все еще создаете UserDAO с помощью их конструктора и, таким образом, устанавливаете зависимость DataSource вручную.
Разве не было бы хорошо, если бы кто-то знал, что ваш UserDAO имеет зависимость от конструктора DataSource, и знал, как ее создать? А затем волшебным образом сконструирует для вас оба объекта: работающий DataSource и работающий UserDao?
Этот кто-то является контейнером внедрения зависимостей и является именно тем, что представляет собой среда Spring.
Контейнер Spring IOC / Dependency Injection
Как уже упоминалось в самом начале, Spring Framework по своей сути является контейнером внедрения зависимостей, который управляет написанными вами классами и их зависимостями для вас (см. Предыдущий раздел). Давайте выясним, как это происходит.
Что такое ApplicationContext? Для чего тебе это?
Тот, кто контролирует все ваши классы и может управлять ими соответствующим образом (читай: создайте их с необходимыми зависимостями), называется ApplicationContext во вселенной Spring.
Чего мы хотим добиться, так это следующего кода (я описал UserDao и DataSource в предыдущем разделе, перейдите по ссылке, если вы пришли сюда и пропустили его):
Это довольно круто, не правда ли? Вам, как вызывающей стороне, больше не нужно беспокоиться о создании классов, вы можете просто попросить ApplicationContext предоставить вам рабочие классы!
Но как это работает?
Что такое ApplicationContextConfiguration? Как построить ApplicationContexts из конфигураций.
В приведенном выше коде мы помещаем переменную с именем someConfigClass в конструктор AnnotationConfigApplicationContext. Вот быстрое напоминание:
То, что вы действительно хотите передать в конструктор ApplicationContext, это ссылка на класс конфигурации, который должен выглядеть следующим образом:
Этого класса конфигурации уже достаточно для запуска самого первого приложения Spring.
Теперь давайте выясним, что именно Spring и AnnotationConfigApplicationContext делают с тем классом конфигурации, который вы написали.
Почему мы создали AnnotationConfigApplicationContext? Существуют ли другие классы ApplicationContext?
Существует много способов создания Spring ApplicationContext, например, с помощью файлов XML, аннотированных классов конфигурации Java или даже программно. Для внешнего мира это представлено через единый интерфейс ApplicationContext.
Посмотрите на класс MyApplicationContextConfiguration сверху. Это класс Java, который содержит аннотации Spring. Вот почему вам необходимо создать аннотацию AnnotationConfigApplicationContext.
Если вместо этого вы хотите создать свой ApplicationContext из файлов XML, вы должны создать ClassPathXmlApplicationContext.
Есть и много других, но в современном приложении Spring вы обычно начинаете с контекста приложения, основанного на аннотациях.
Вам придется думать о методах внутри вашего класса конфигурации ApplicationContext как о фабричных методах. На данный момент есть один метод, который знает, как создавать экземпляры UserDao, и один метод, который создает экземпляры DataSource.
Эти экземпляры, которые создаются этими фабричными методами, называются bean-компонентами. Spring контейнер создал их и они находятся под его управлением.
Но это приводит к вопросу: сколько экземпляров определенного компонента должно быть создано Spring?
Что такое Spring bean scope?
Сколько экземпляров наших DAO следует создать в Spring? Чтобы ответить на этот вопрос, вам нужно узнать о bean scope (область применения бина).
Вы можете прочитать полный список доступных областей применения бинов здесь, но пока достаточно знать, что вы можете повлиять на область действия с помощью еще одной аннотации.
Аннотация области (scope annotation) определяет, сколько экземпляров создаст Spring. И, как упоминалось выше, это довольно просто:
Суть: большинство приложений Spring почти полностью состоят из одноэлементных bean-компонентов, в которые время от времени добавляются другие области действия bean-компонента (прототип, запрос, сессия, websocket и т.д.).
Теперь, когда вы знаете о ApplicationContexts, Beans & Scopes, давайте еще раз рассмотрим зависимости или то, как наш UserDAO может получить DataSource.
Что такое Spring Java Config?
До сих пор вы явно настраивали свои bean-компоненты в конфигурации ApplicationContext с помощью аннотированных @Bean методов Java.
Это то, что вы бы назвали Spring Java Config, в отличие от указания всего в XML, который исторически был подходом для Spring. Просто краткий обзор того, как это выглядит:
Это означает, что, если ваш MyApplicationContextConfiguration находится в пакете com.marcobehler, Spring будет сканировать каждый пакет, включая подпакеты, который начинается с com.marcobehler для поиска потенциальных компонентов Spring.
Давайте добавим аннотацию @Component к вашему UserDAO.
Теперь Spring имеет всю информацию, необходимую для создания bean-компонентов UserDAO:
Внедрение зависимости через конструктор и Autowired
В более новых версиях Spring действительно достаточно умен, чтобы внедрять эти зависимости без явной аннотации @Autowired в конструкторе. Так что это также будет работать.
Почему я упомянул @Autowired тогда? Поскольку это не повредит, то есть делает вещи более явными и потому что вы можете использовать @Autowired во многих других местах, кроме конструкторов.
Давайте рассмотрим различные способы внедрения зависимостей — внедрение конструкторов — всего лишь один из них.
Что такое Field Injection? Что такое Setter Injection?
Проще говоря, Spring не должен использовать конструктор для внедрения зависимостей.
Он также может напрямую внедрять поля.
Кроме того, Spring также может внедрять сеттеры.
Эти два стиля внедрения (поля, сеттеры) имеют тот же результат, что и внедрение конструктора: вы получите работающий Spring Bean. На самом деле есть еще один метод, называемый внедрением метода, который мы не будем здесь описывать.
Но очевидно, что они отличаются друг от друга, что означает, что было очень много споров о том, какой стиль внедрения является лучшим и какой вы должны использовать в своем проекте.
Внедрение зависимости через конструктор или через поле
Было много споров в сети что лучше внедрение зависимости через конструктор или через поле и множеством громких голосов, утверждавших, что внедрение с помощью метода установки (setter) вредна.
Чтобы не добавлять шума к этим аргументам сформулируем суть этой статьи:
Резюме о контейнере Spring IoC
К настоящему моменту вы должны знать почти все, что вам нужно знать о контейнере зависимостей Spring.
Конечно, это еще не все, но если вы хорошо разбираетесь в ApplicationContexts, Beans, зависимостях и различных методах внедрения зависимостей, то вы уже на правильном пути.
Давайте посмотрим, что еще может предложить Spring, кроме инъекций чистой зависимости.
Spring AOP (Аспектно-ориентированное программирование) и прокси
Внедрение зависимостей поможет вести вас к более структурированным программам, но внедрение зависимости здесь и там не совсем то, в чем заключается суть экосистемы Spring. Давайте еще раз посмотрим на простую ApplicationContextConfiguration:
Вот где проявляется функция скрытая особенность Spring:
Однако Spring может обманывать и создавать что-то еще, кроме вашего класса UserService. Как? Почему?
Spring может создавать прокси
Потому что под капотом любой метод Spring @Bean может вернуть вам то, что (в вашем случае) выглядит и ощущается как UserService, но на самом деле это не так.
Он может вернуть вам прокси.
Прокси-сервер в какой-то момент делегирует службу UserService, которую вы написали, но сначала он выполнит свою собственную функциональность.
В частности, Spring по умолчанию создаст динамические прокси-серверы Cglib, которым не нужен интерфейс для работы прокси-серверов (например, внутренний механизм прокси-сервера JDK): вместо этого Cglib может прокси-классы посредством их подклассов на лету. (Если вы не уверены относительно отдельных шаблонов прокси, прочитайте больше о прокси в Википедии.)
Почему Spring желает создавать прокси?
Потому что это позволяет Spring дать вашим компонентам дополнительные функции без изменения кода. В сущности, это то, что является аспектно-ориентированным (или: AOP) программированием.
Spring аннотация @Transactional
Ваша реализация UserService выше может выглядеть примерно так:
Проблема: хотя Spring может создать ваш компонент UserService через конфигурацию applicationContext, он не может переписать ваш UserService. Он не может просто вставить туда код, который открывает соединение с базой данных и фиксирует транзакцию с базой данных.
Но что она может сделать, так это создать прокси вокруг вашего UserService, который будет транзакционным. Таким образом, только прокси-сервер должен знать, как открывать и закрывать соединение с базой данных, а затем может просто делегировать его вашему UserService.
Давайте еще раз посмотрим на эту безопасную ContextConfiguration.
Поначалу это может показаться немного не интуитивным, но большинство разработчиков Spring очень скоро сталкиваются с прокси в сеансах отладки. Из-за прокси трассировки стека Spring могут быть довольно длинными и незнакомыми: когда вы входите в метод, вы можете очень хорошо сначала войти в прокси — что отпугивает людей. Это, однако, совершенно нормальное и ожидаемое поведение.
Нужно ли Spring использовать прокси Cglib?
Прокси являются выбором по умолчанию при программировании AOP с помощью Spring. Однако вы не ограничены использованием прокси, вы также можете пройти полный маршрут AspectJ, который при желании изменяет ваш фактический байт-код. Однако покрытие AspectJ выходит за рамки данного руководства.
Также смотрите раздел: В чем разница между Spring AOP и AspectJ?
Резюме о поддержке Spring AOP
Управление ресурсами Spring
Мы уже давно говорим о внедрении зависимостей и прокси. Давайте теперь посмотрим на то, что я бы назвал важными утилитами удобства в среде Spring. Одной из таких утилит является поддержка ресурсов Spring.
Подумайте, как бы вы попытались получить доступ к файлу в Java через HTTP или FTP. Вы можете использовать класс URL в Java и написать некоторый код.
Точно так же, как бы вы читали в файлах из пути к классам вашего приложения? Или из контекста сервлета, это означает из корневого каталога веб-приложений (по общему признанию, это становится все реже и реже в современном приложении packaged.jar).
Опять же, вам нужно написать довольно много стандартного кода, чтобы он работал, и, к сожалению, код будет отличаться для каждого варианта использования (URL-адреса, пути к классам, контексты сервлетов).
Но есть решение: абстракция ресурсов Spring. Это легко объяснить в коде.
Короче говоря, Spring дает вам возможность доступа к ресурсам с помощью приятного небольшого синтаксиса. Интерфейс ресурса имеет несколько интересных методов:
Как видите, он позволяет вам выполнять самые распространенные операции с ресурсом:
Это позволяет вам делать все, что вы хотите с ресурсом, независимо от того, живете ли вы в Интернете, на вашем пути к классам или на жестком диске.
Абстракция ресурсов выглядит как такая крошечная функция, но она действительно сияет в сочетании со следующей удобной функцией, предлагаемой Spring: Properties.
Что такое Spring Environment?
Большая часть любого приложения — это чтение свойств (properties), таких как имя пользователя и пароли базы данных, конфигурация почтового сервера, детализированная конфигурация оплаты и т.д.
Spring пытается упростить вам регистрацию и автоматический поиск свойств во всех этих различных источниках с помощью абстракции environment.
Что такое environment?
В двух словах, среда состоит из одного или многих источников свойств выполняемого Spring приложения. Например:
(Примечание. Среда также состоит из профилей, то есть профилей «dev» или «production», но мы не будем вдаваться в подробности о профилях в этом пересмотре этого руководства).
По умолчанию среда веб-приложения Spring MVC состоит из параметра ServletConfig/Context, источников системных свойств JNDI и JVM. Они также являются иерархическими, это означает, что они имеют порядок важности и перекрывают друг друга.
Тем не менее, довольно легко определить новые @PropertySources самостоятельно:
Теперь стало намного понятнее, почему мы говорили об управлении ресурсами Spring раньше. Потому что обе функции идут рука об руку.
Аннотация @PropertySource работает с любым допустимым классом конфигурации Spring и позволяет вам определять новые дополнительные источники с помощью абстракции ресурсов Spring: помните, что все дело в префиксах: http://, file://, classpath: и т.д.,
Определение свойств через @PropertySources — это хорошо, но разве нет лучшего способа, чем пройти через среду, чтобы получить к ним доступ? Да, есть.
Spring аннотация @Value и внедрение значений свойств
Spring Web MVC
Spring Web MVC, также известный как Spring MVC, является веб-средой Spring. Это позволяет создавать все, что связано с сетью, от небольших веб-сайтов до сложных веб-сервисов. Он также поддерживает фреймворки, такие как Spring Boot.
Что такое MVC?
Если вы совершенно не знакомы с MVC, вы можете прочитать страницу Model-View-Controller в Википедии, прежде чем продолжить чтение.
В контексте рендеринга HTML-страниц, скажем, страницы учетной записи пользователя, вот как выглядит MVC в Spring:
Итак, вашей конечной целью является написание контроллеров с помощью Spring, которые сопоставляются с конкретными HTTP-запросами, такими как (/account), и предоставляют пользователю соответствующую HTML-страницу, которая визуализируется путем объединения представления и данных, необходимых для этой страницы.
Та же логика верна для написания веб-сервисов JSON или XML.
Прежде чем мы рассмотрим реализацию класса типа контроллер, давайте сделаем шаг назад и посмотрим, как мы реализовали бы веб-страницу в низко-низко-низкоуровневом Java: с помощью старого доброго API сервлета Java (на котором основывается Spring MVC). ).
HttpServlet памятка
На данный момент игнорируем MVC: чтобы написать что-нибудь, связанное с HTTP с Java, вы бы использовали сервлеты или, в частности, HttpServlets (примечание для придир: да, есть и другие способы, спасибо за замечание). Сервлеты могут обрабатывать HTTP-запросы, а также возвращать браузеру или клиенту соответствующий HTTP-ответ.
После написания вашего сервлета вы должны зарегистрировать его в контейнере сервлетов, таком как Tomcat или Jetty. Регистрация сервлета всегда включает путь, чтобы указать, за какие URL в вашем веб-приложении отвечает ваш сервлет. Давайте предположим путь «/*», поэтому каждый входящий HTTP-запрос к вашему приложению обрабатывается одним сервлетом.
Вот как может выглядеть этот сервлет:
Обрабатывать каждый запрос к вашему приложению всего двумя способами — это немного громоздко, и для того, чтобы получить правильный MVC, требуется довольно много работы.
Кроме того, вашему MyServlet (контроллеру) необходимо выполнить довольно много ручного HTTP-специфического подключения, проверки URI запроса, просмотра строк, преобразования requestParameters и ответов и так далее.
Было бы намного приятнее, если бы вам не пришлось заботиться обо всем этом слесарном деле и позволить Spring сделать это за вас. Вот тут и приходит DispatcherServlet.
Что делает DispatcherServlet?
Uber-контроллер в среде Spring MVC Spring представляет собой сервлет, который называется DispatcherServlet.
Он называется DispatcherServlet, потому что он может буквально обрабатывать любой входящий HTTP-запрос, анализировать его содержимое и пересылать данные в виде симпатичных маленьких объектов Java в класс Controller.
Он также достаточно умен, чтобы брать выходные данные с этих контроллеров и конвертировать их в HTML / JSON / XML, в зависимости от того, что подходит. Весь процесс выглядит следующим образом (с пренебрежением большим количеством промежуточных классов, потому что DispatcherServlet не выполняет всю работу сам).
Это именно то, что вы хотите.
Давайте посмотрим на эти классы типа Controller более подробно.
Как писать классы типа Controller
Наконец, мы можем написать наш Controller класс, который обрабатывает запросы /account.
Страница /account будет HTML-страницей с несколькими динамическими переменными, такими как имя пользователя, адрес, информация о подписке и т.д.
В этих двух строчках происходит МНОГО, давайте посмотрим, что именно.
Наш метод Java возвращает строку с именем account. На самом деле это не просто строка, а ссылка на представление (HTML-шаблон). Давайте посмотрим на эти шаблоны на секунду.
Как генерировать HTML представление (view) с помощью Spring Web MVC
Spring MVC по умолчанию предполагает, что вы хотите визуализировать некоторый HTML. И, конечно же, вы не хотите сами отображать HTML-строки с помощью конкатенации строк, скорее вы захотите использовать шаблонную среду, такую как Velocity или Freemarker. Spring интегрируется со всеми этими технологиями.
Итак, вы могли бы иметь следующее представление (шаблон):
classpath:/templates/account.vm
Вот и все, разработка Spring MVC полностью завершена!
Как генерировать JSON / XML (представления) с помощью Spring Web MVC
С веб-сервисами вы не генерируете HTML, а генерируете XML или JSON. Это довольно просто, с Spring MVC также.
Конечно, вам нужна соответствующая библиотека, такая как Jackson, добавленная в ваш проект, но тогда вы можете просто аннотировать свой Controller дополнительной аннотацией, чтобы сигнализировать Spring: пожалуйста, преобразуйте мои объекты Java напрямую в XML / JSON, вместо того, чтобы я давал вам ссылка на вид.
Но как Spring узнает, должен ли он возвращать XML, JSON или что-то еще?
Как работает согласование контента (content negotiation) Spring MVC
Существует множество способов, с помощью которых вы, как клиент, можете указать Spring MVC, какой формат ответа вы хотите при выполнении запроса к приложению Spring MVC.
Если вы хотите узнать больше о согласовании контента, ознакомьтесь с официальной документацией.
Какой тип ввода HTTP-запроса понимает Spring?
Spring MVC понимает в основном все, что предлагает HTTP — с помощью сторонних библиотек.
Это означает, что вы можете выбросить в него тела запросов JSON, XML или HTTP (Multipart) Fileupload, и Spring удобно преобразует этот ввод в объекты Java.
Какие HTTP-ответы может написать Spring MVC?
Spring MVC может записывать все что угодно в HttpServletResponse — с помощью сторонних библиотек.
Будь то HTML, JSON, XML или даже тела ответов WebSocket. Более того, он берет ваши объекты Java и генерирует эти тела ответов для вас.
А как насчет других концепций Spring MVC?
Официальная документация Spring MVC буквально содержит сотни страниц, описывающих, как работает веб-фреймворк.
Поэтому, если вы хотите узнать больше о RequestParams, Моделях, Представлениях, ViewHandlers, RootContexts, Фильтрах, Кэшировании и Безопасности, я приглашаю вас проверить это. Это просто не входит в рамки данного руководства, чтобы охватить все.
Однако в разделе часто задаваемых вопросов данного руководства есть ответы на несколько дополнительных вопросов.
Вы могли бы подумать, что она присуща Spring Boot (как я ошибочно делал слишком долго), но, как правильно заметил Мацей Волковяк, это часть простого старого Spring MVC. Вот его источник:
Это верно, Spring MVC @RestController — это не что иное, как Spring MVC @Controller в сочетании с аннотацией Spring MVC @ResponseBody — хотя вы можете подумать, что это что-то связанное со Spring Boot.
Резюме: Spring MVC
Spring MVC — это старый добрый фреймворк MVC, который позволяет довольно легко писать HTML / JSON / XML веб-сайты или веб-сервисы. Он прекрасно интегрируется с контейнером внедрения зависимостей Spring, включая все его вспомогательные утилиты.
Вкратце: он позволяет вам сосредоточиться на написании Java-классов, вместо того, чтобы иметь дело с сланцевым кодом сервлета, то есть работать с Http-запросами и ответами, и дает вам хорошее разделение проблем между вашей моделью и представлениями.
Дополнительные модули Spring Framework
Мы рассмотрели контейнер IoC Spring, Spring Web MVC и несколько других небольших модулей Spring. Но есть еще много. Что они делают?
О чем дополнительные модули Spring Framework?
Spring Framework состоит из еще большего количества удобных утилит, чем вы видели до сих пор. Давайте назовем их модулями и не путайте эти модули с 20 другими проектами Spring на spring.io. Наоборот, все они являются частью рамочного проекта Spring.
Итак, о каком удобстве идет речь?
Вы должны понимать, что в основном все, что Spring предлагает в этих модулях, также доступно на чистой Java. Предлагается либо JDK, либо сторонней библиотекой. Spring Framework всегда опирается на эти существующие функции.
Вот пример: отправка вложений электронной почты с помощью Java API Почты, безусловно, выполнима, но немного неудобна в использовании. Смотрите здесь примера кода.
Spring предоставляет приятную маленькую API-оболочку поверх Java Mail API, с тем дополнительным преимуществом, что все, что он предлагает, прекрасно вписывается в контейнер внедрения зависимостей Spring.
Итак, подытоживая, цель Spring Framework состоит в том, чтобы «упростить» доступную функциональность Java, подготовить ее к внедрению зависимостей и, следовательно, упростить использование API в контексте Spring.
Есть ли список всех дополнительных модулей Spring Framework?
Я хотел бы дать вам краткий обзор наиболее распространенных утилит, функций и модулей, с которыми вы можете столкнуться в проекте Spring. Однако обратите внимание, что подробное описание всех этих инструментов невозможно в рамках данного руководства. Вместо этого взгляните на официальную документацию для получения полного списка.
Spring Framework: FAQ
Какую версию Spring я должен использовать?
Выбор версии Spring относительно прост:
В реальности вы можете найти проекты на Spring версии 4.x-5.x, используемые компаниями, хотя имеются и редкие, унаследованные проекты на Spring 3.x (первоначальный выпуск: 2009).
Как часто выпускаются новые версии Spring? Как долго они поддерживаются?
Вот хороший маленький график, показывающий историю версий Spring:
Вы можете видеть, что первоначальная версия Spring была
17 лет назад, а основные версии платформы выпускались каждые 3-4 года. Это не учитывает филиалы обслуживания, как бы то ни было.
Примечание. Текущий EOL() (больше никаких обновлений и поддержки) для всех версий Spring, кроме 5.2, в настоящее время настроен на 31 декабря 2020 года.
Какие библиотеки вам нужны, чтобы начать работу с Spring?
На самом деле существует только одна зависимость, которая вам нужна, когда вы хотите настроить проект Spring. Это называется spring-context. Это абсолютный минимум, чтобы заставить работать инъекционный контейнер Spring.
Для дополнительных функций Spring (таких как поддержка Spring JDBC или JMS) вам потребуются другие дополнительные библиотеки.
Вы можете найти список всех доступных модулей в официальной документации Spring, хотя с точки зрения зависимостей Maven artifactIds действительно следуют за именем модуля. Вот пример:
Чем отличаются версии Spring?
Подобно JVM, версии Spring безумно обратно совместимы, что означает, что вы можете (по существу) по-прежнему запускать файлы Spring 1.0 xml с последней версией Spring 5.0 (хотя я, по общему признанию, еще не пробовал это). Кроме того, обновление с, скажем, 3 до 5 также возможно с небольшими усилиями (см. Это руководство по миграции).
Таким образом, в целом, новые версии Spring основаны на более старых версиях Spring и имеют минимальные критические изменения (по сравнению, скажем, с Python 2 против 3). Итак, все основные концепции, которые вы изучили для Spring версии 3 или 4, остаются верными для Spring версии 5.
Вы можете получить отличный обзор того, что изменилось за последние 7 лет в отдельных версиях Spring, здесь:
Чтобы дать вам резюме:
Ядро (внедрение зависимостей, управление транзакциями и т.д.) Всегда остается неизменным или расширяется. Однако Spring идет в ногу со временем и предлагает поддержку для новых версий языка Java, улучшений инфраструктуры тестирования, веб-сокетов, реактивного программирования и т.д.
Что сейчас происходит в 20 других проектах Spring.io? Как насчет изменения реального байт-кода?
В рамках данного руководства я не могу вдаваться в подробности всех различных проектов, но давайте рассмотрим те, с которыми вы, скорее всего, столкнетесь.
Вывод: все эти библиотеки расширяют Spring Framework и основываются на его основных принципах внедрения зависимостей.
В чем разница между Spring и Spring Boot?
Если вы прочитали это руководство, вы должны понимать, что Spring Boot построен поверх Spring. Несмотря на то, что скоро появится подробное руководство по Spring Boot, вот пример того, что означают «самоуверенные значения по умолчанию» в Spring Boot.
Проблема в том, что вы должны сами написать и настроить все эти отдельные части. Spring Boot, с другой стороны, берет эти отдельные части и связывает их вместе. Пример:
Итак, Spring Boot — это все, что нужно для того, чтобы собрать существующие части Spring Framework, предварительно сконфигурировать и упаковать их с минимальными затратами на разработку.
В чем разница между Spring AOP и AspectJ?
Как упомянуто выше в разделе Нужно ли Spring использовать прокси Cglib?» в Spring по умолчанию используется AOP на основе прокси. Он оборачивает ваши компоненты в прокси для достижения таких вещей, как управление транзакциями. Это имеет несколько ограничений и предостережений, но является довольно простым и понятным способом реализации наиболее распространенных проблем AOP, с которыми сталкиваются разработчики Spring.
AspectJ, с другой стороны, позволяет вам изменять фактический байт-код путем ткачества времени загрузки или ткачества времени компиляции. Это дает вам гораздо больше возможностей в обмен на гораздо большую сложность.
Однако вы можете настроить Spring на использование AOP AspectJ вместо AOP по умолчанию на основе прокси.
Вот пара ссылок, если вы хотите получить больше информации по этой теме:
В чем разница между Spring и Spring Batch?
Spring Batch — это фреймворк, упрощающий написание пакетных заданий, т.е. «Читать эти 95 CSV-файлов каждую ночь в 3 часа ночи и вызывать внешнюю службу проверки для каждой записи».
Опять же, он построен на основе Spring Framework, но во многом является собственным проектом.
Тем не менее, обратите внимание, что по сути невозможно создать надежное пакетное задание без хорошего понимания общего управления транзакциями в Spring Framework и его отношения к Spring Batch.
В чем разница между Spring и Spring Web MVC?
Если вы прочитали это руководство, вы должны понимать, что Spring Web MVC является частью среды Spring.
Это могут быть RestControllers (где вы отправляете XML или JSON клиенту) или старые добрые HTML-контроллеры, где вы генерируете HTML с такими фреймворками, как Thymeleaf, Velocity или Freemarker.
В чем разница между Spring и Struts?
Вопрос должен быть: чем отличается Spring Web MVC от Struts?
Краткий исторический ответ таков: Spring Web MVC начинал как конкурент Struts, который, как утверждается, был плохо спроектирован разработчиками Spring (см. Википедию).
Современный ответ заключается в том, что, хотя Struts 2, безусловно, все еще используется в странном устаревшем проекте, Spring Web MVC является основой для всего, что связано с сетью во вселенной Spring. От Spring Webflow до RestControllers Spring Boot.
Что лучше? Spring XML или аннотации или Java конфигурация?
Spring начинался только с конфигурации XML. Затем медленно, все больше и больше появлялись аннотации / возможности Java конфигурации.
Сегодня вы заметитите, что конфигурация XML в основном используется в более старых, унаследованных проектах, а в новых проектах используется конфигурации на основе Java или аннотаций.
Обратите внимание на две вещи:
Что лучше? Внедрение зависимостей на основе конструктора или поля?
Как уже упоминалось в разделе о внедрении зависимостей, этот вопрос вызывает много разных мнений. Самое главное, что ваш выбор должен быть одинаковым для всего вашего проекта: не используйте внедрение на основе конструктора для 83% ваших бинов и внедрение на основе поля для остальных 17%.
Разумным подходом будет использование рекомендованного способа в документации Spring: использование внедрения конструктора для обязательных зависимостей, установка / установка поля для необязательных зависимостей, а затем проверка этих дополнительных зависимостей по всему классу на ноль.
Самое главное, имейте в виду: общий успех вашего программного проекта не будет зависеть от выбора вашего любимого метода внедрения зависимостей.
Есть ли альтернативы контейнеру внедрения зависимостей в Spring?
Да, две популярные в экосистеме Java:
Обратите внимание, что Dagger предлагает только внедрение зависимостей, без дополнительных удобных функций. Guice предлагает внедрение зависимостей и другие функции, такие как управление транзакциями (с помощью Guice Persist).
Заключение
Если вы читали это далеко, то теперь у вас должно быть достаточно глубокое понимание того, что такое Spring Framework.
Вы узнаете, как это связано с другими библиотеками экосистемы Spring (такими как Spring Boot или Spring Data) в последующих руководствах, но сейчас я хочу, чтобы вы помнили эту метафору при попытке ответить на вопрос «Что такое Spring Фреймворк?»
Представьте, что вы хотите отремонтировать дом (
= создать программный проект).
Spring Framework — это ваш DIY-магазин (
= контейнер для инъекций зависимости), который предлагает множество различных инструментов, от горелок Бунзена (
= ресурсы / свойства) до кувалд (
= Web MVC) для вашего обновления. Эти инструменты просто помогут вам быстрее и удобнее отремонтировать ваш дом (создать приложение на Java).
(примечание: не спрашивайте меня, как я придумал эти сравнения;))
Вот и все на сегодня. Если у вас есть какие-либо вопросы или предложения, напишите мне по адресу marco@marcobehler.com или оставьте комментарий ниже. Для практических занятий ознакомьтесь с учебным курсом Spring Framework.
Спасибо за прочтение. Auf Wiedersehen.
Благодарности
(Благодарности автора оригинальной статьи — прим. перев.)