В одной из классических статей для новичков, мелькавших недавно на Хабре, рассказывалось про создание базового Web приложения на Java. Все начиналось с сервлета, потом создания JSP страницы и, наконец, деплоймента в контейнер. Посмотрев на это свежим взглядом я понял, что для как раз для новичков это, наверняка, выглядит совершенно жутко — на фоне простых и понятных PHP или Node.js, где все просто — написал контроллер, вернул объект, он стал JSON или HTML. Чтобы немного развеять это ощущение, я решил написать "Гайд для новичков в Spring". Цель это статьи — показать, что создание Web приложений на Java, более того — на Spring Framework это не боль и мучительное продирание через web.xml, persistence.xml, beans.xml, и собирание приложения как карточного домика по кусочкам, а вполне себе быстрый и комфортный процесс. Аудитория — начинающие разработчики, разработчики на других языках, ну и те, кто видел Спринг в его не самые лучше времена.


Введение


В этой статье мы посмотрим, что включает в себя современный Спринг, как настроить локальное окружение для разработки Веб приложений, и создадим простое веб-приложение, которое берет данные из БД и отдает HTML страницу и JSON. Как ни странно, большинство статей (на русском языке) для начинающих, которые я нашел в топе поиска описывают и ручное создание контекста, и запуск приложения, и конфигурацию через XML — ничего из этого в современном Спринге делать, разумеется, не обязательно.


Что такое Spring?


Для начала пара слов, что же такое Spring. В настоящее время, под термином "Spring" часто подразумевают целое семейство проектов. В большинстве своем, они развиваются и курируются компанией Pivotal и силами сообщества. Ключевые (но не все) проекты семейства Spring это:


  • Spring Framework (или Spring Core)
    Ядро платформы, предоставляет базовые средства для создания приложений — управление компонентами (бинами, beans), внедрение зависимостей, MVC фреймворк, транзакции, базовый доступ к БД. В основном это низкоуровневые компоненты и абстракции. По сути, неявно используется всеми другими компонентами.


  • Spring MVC (часть Spring Framework)
    Стоит упомянуть отдельно, т.к. мы будем вести речь в основном о веб-приложениях. Оперирует понятиями контроллеров, маппингов запросов, различными HTTP абстракциями и т.п. Со Spring MVC интегрированы нормальные шаблонные движки, типа Thymeleaf, Freemaker, Mustache, плюс есть сторонние интеграции с кучей других. Так что никакого ужаса типа JSP или JSF писать не нужно.


  • Spring Data
    Доступ к данным: реляционные и нереляционные БД, KV хранилища и т.п.


  • Spring Cloud
    Много полезного для микросервисной архитектуры — service discovery, трасировка и диагностика, балансировщики запросов, circuit breaker-ы, роутеры и т.п.


  • Spring Security
    Авторизация и аутентификация, доступ к данным, методам и т.п. OAuth, LDAP, и куча разных провайдеров.


  • Spring Integration
    Обработка данных из разных источников. Если надо раз в час брать файл с ФТП, разбивать его на строки, которые потом фильтровать, а дальше отправлять в какую-то очередь — это к Spring Integration.

Типичное веб приложение скорее всего будет включать набор вроде Spring MVC, Data, Security. Ниже мы увидим, как это все работает вместе.


Особняком стоит отметить Spring Boot — это вишенка на торте (а некоторые думают, что собственно сам торт), которые позволяет избежать всего ужаса XML конфигурации. Boot позволяет быстро создать и сконфигурить (т.е. настроить зависимости между компонентами) приложение, упаковать его в исполняемый самодостаточный артефакт. Это то связующее звено, которое объединяет вместе набор компонентов в готовое приложение. Пару вещей, которые нужно знать про Spring Boot:


  • Он не использует кодогенерацию. Из кода, который генерится, присутствует только метод main. Если вы ищете утилиту для генерации приложений — это скорее JHipster
  • Не использует XML для конфигурации. Все конфигурится через аннотации
  • Используются автоконфигурации по максимуму. Если у вас добавлена зависимость на Mongo, и не указано, куда подключаться — Boot попробует localhost:27017
  • Используется convention over configuration. Для большинства конфигураций не нужно ничего настраивать
  • Его легко отодвинуть в сторону и "перекрыть" конфигурацию по умолчанию. Например, если в настройках указать хост для подключения к Монго — он автоматически перекроет localhost

Настройка окружения


Для того, чтобы создать простое приложение, знать, как создать проект Maven с нуля, как настроить плагины, чтобы создать JAR, какие бывают лейауты в JAR, как настроить Surefire для запуска тестов, как установить и запустить локально Tomcat, а уж тем более, как работает DispatcherServlet — совершенно не нужно.


Современное приложение на Spring создается в два шага:


  1. Идем на Spring Initializr.
  2. … и все, второго шага нет

Spring Initializr позволяет "набрать" в свое приложение нужных компонентов, которые потом Spring Boot (он автоматически включен во все проекты, созданные на Initializr) соберет воедино.


В качестве среды разработки подойдет что угодно, например бесплатная IntelliJ IDEA CE прекрасно справляется — просто импортируйте созданный pom.xml (Maven) или build.gradle (Gradle) файл в IDE.


Стоит отдельно отметить компонент Spring Boot который называется DevTools. Он решает проблему цикла локальной разработки, который раньше выглядел как:


  1. Собрать WAR локально
  2. Задеплоить его в Очень Крутой Энтерпрайз Аппликейшен Сервер на тестовом сервере, потому что настройка ОКЭАС локально требует недюжинной сноровки
  3. Выпить чашку кофе, т.е. ОКЭАС перезапустить приложение в лучшем случае через несколько минут
  4. Скопировать стектрейс ошибки
  5. Перейти на п. 1

В те древние времена даже родилась поговорка, что Spring это DSL для конвертации XML конфигов в стектрейсы.

С включенными Spring Boot DevTools цикл разработки сокращается до:


  1. Запустить приложение через зеленый треугольничек в IDEA

DevTools будут автоматом проверять изменения в скомпилированном коде или шаблонах, и очень быстро перезапускать (hot reload) только "боевую" часть приложения (как nodemon, если вы знакомы с миром node.js). Более того, DevTools включают интеграцию с Live Reload и после установки расширения в браузере, достаточно скомпилировать проект в IDEA, чтобы он автоматом обновился в браузере.


Разработка


Окей, пора приступать к практической части. Итак, наша цель — создать веб-приложение, которое отдает welcome страницу, обращается с нее же к собственному API, получает JSON с данными из базы и выводит их в таблицу.


Новый проект


Для начала, идем на start.spring.io и создаем проект с зависимостями Web, DevTools, JPA (доступ к реляционным базам), H2 (простая in-memory база), Mustache (движок шаблонов). Сгенерированный pom.xml импортируем в IDEA. Все, приложение готово к запуску! Можно его запустить из командной строки командой ./mvnw spring-boot:run или прямо из IDEA — запустив метод main. Да, серверов приложений, контейнеров и деплоймента не нужно.


Точнее, контейнер нужен — только он предоставлен и настроен Spring Boot-ом — используя Embedded Tomcat

Контроллер


Итак, наш следующий шаг — создать контроллер и вернуть "домашнюю" страницу. Код контроллера выглядит так просто, как и ожидается:


@Controller
public class IndexController {
    @GetMapping("/")
    public ModelAndView index() {
        Map<String, String> model = new HashMap<>();
        model.put("name", "Alexey");
        return new ModelAndView("index", model);
    }
}

Пара вещей, на которые стоит обратить внимание.


  1. Метод возвращает ModelAndView — дальше Spring знает, что нужно взять вью index.html из папки resources/templates (это соглашение по умолчанию) и передать туда модель
  2. Модель в нашем случае просто словарь, но это может быть и строго-типизированная модель (объект) тоже

С Котлин это бы выглядело еще лучше и проще, но это потребует введения сразу большого количества новых понятий — язык, фреймворк. Лучше начинать с малого.

Класс, помеченный как @Controller автоматически регистрируется в MVC роутере, а используя аннотации @(Get|Post|Put|Patch)Mapping можно регистрировать разные пути.


Все файлы из каталога resources/static/ считаются статическими, там можно хранить CSS и картинки.

Шаблон


Мы используем Mustache (Handlebar) синтаксис, поэтому шаблон очень напоминает обычный HTML


<!DOCTYPE html>

<html lang="en">

<body>
<h1>Welcome to Spring, {{ name }}</h1>
</body>

</html>

После компиляции проекта (?/Ctrl + F9) — можно сразу идти на http://localhost:8080 и увидеть созданную страницу.


Доступ к базе


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


@Entity
public class Visit {

    @Id
    @GeneratedValue
    public Long id;

    public String description;
}

Предвидя череду комментариев "Как же без геттеров и сеттеров" и "Где же equals / hashCode" — эти элементы упущены сознательно с целью упрощения кода. Совершенно чудовищная ошибка дизайна Java которая заставляет писать эту ерунду (геттеры и методы сравнения), это, конечно, отдельный разговор. Котлин эту проблему, кстати, решает.

Мы снова очень активно используем аннотации — в этот раз из Spring Data (точнее, JPA — это дремучая спецификация для доступа к данным). Этот класс описывает модель с двумя полями, одно из которых генерится автоматически. По этому классу будет автоматически создана модель данных (таблицы) в БД.


Теперь для этой модели пора создать репозиторий. Это еще проще, чем контроллер.


@Repository
public interface VisitsRepository extends CrudRepository<Visit, Long> {
}

Все, репозиторий можно использовать для работы с базой — читать и писать записи. У внимательного читателя должен сработать WTF детектор — что здесь вообще происходит? Мы определяем интерфейс и внезапно он начинает работать с базой? Все так. Благодаря магии Spring Boot и Spring Data "под капотом" происходит следующее:


  • Увидев в зависимостях H2 (встраиваемая БД), Boot автоматически конфигурит DataSource (это ключевой компонент для подключения к базе) чтобы приложение работало с этой базой
  • Spring Data ищет всех наследников CrudRepository и автоматически генерит для них дефолтные реализации, которые включают базовые методы репозитория, типа findOne, findAll, save etc.
  • Spring автоматически конфигурит слой для доступа к данным — JPA (точнее, его реализацию Hibernate)
  • Благодаря аннотации @Repository этот компонент становится доступным в нашем приложении (и мы его используем через пару минут)

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


@Controller
public class IndexController {

    final VisitsRepository visitsRepository;

    public IndexController(VisitsRepository visitsRepository) {
        this.visitsRepository = visitsRepository;
    }
...
}

Увидев в нашем конструкторе параметр типа VisitRepository, Spring найдет созданный Spring Data-ой репозиторий и передаст его в конструктор.


Теперь можно писать в базу в методе контроллера.


@GetMapping("/")
    public ModelAndView index() {
        Map<String, String> model = new HashMap<>();
        model.put("name", "Alexey");

        Visit visit = new Visit();
        visit.description = String.format("Visited at %s", LocalDateTime.now());
        visitsRepository.save(visit);

        return new ModelAndView("index", model);
    }

REST контроллер


Следующий шаг — это вернуть все записи из базы в JSON формате, чтобы потом их можно было читать на клиенте.


Для REST в Spring есть отдельный тип контроллера который называется @RestController, код которого не сильно отличается от обычного контроллера.


@RestController
@RequestMapping("/api")
public class ApiController {

    final VisitsRepository visitsRepository;

    public ApiController(VisitsRepository visitsRepository) {
        this.visitsRepository = visitsRepository;
    }

    @GetMapping("/visits")
    public Iterable<Visit> getVisits() {
        return visitsRepository.findAll();
    }
}

На что обратить внимание:


  • В этот раз мы определяем "префикс" для всех методов контроллера использую базовый @RequestMapping
  • Внедрение зависимостей работает точно так же, как и для обычных контроллеров (как и вообще для всего в Spring)
  • Метод теперь возвращает не имя шаблона, а модель. Spring автоматически преобразует это в массив JSON объектов
  • Мы используем persistence модель для сериализации в JSON, что в общем случае не самая лучшая практика

Теперь при запросе http://localhost:8080/api/visits (предварительно скомпилировав проект и дав DevTools обновить приложение) мы получим JSON с нужными данными.


Клиентский код


Оставим за рамками этой статьи, пример можно увидеть в исходном коде. Цель этого кода — исключительно продемонстрировать как получить JSON данные с сервера, интеграции с клиентскими фреймворками React, Angular etc намеренно оставлены вне рамок этой статьи.


Тестирование


Spring так же предоставляет мощные средства для Integration и Unit тестирования приложения. Пример кода, который проверяет контроллер:


@Test
    public void indexControllerShouldReturnHtmlPage() throws Exception {
        mockMvc.perform(get("/"))
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("Welcome to Spring")));
    }

Используя абстракции типа MockMvc можно легко тестировать внешний интерфейс приложения, в то же время имея доступ к его внутренностям. Например, можно целиком заменить компоненты приложения на моки (заглушки).


Аналогично для API тестов есть набор хелперов для проверки JsonPath выражений.


@Test
public void apiControllerShouldReturnVisits() throws Exception {
    mockMvc.perform(get("/"));

    mockMvc.perform(get("/api/visits"))
            .andExpect(jsonPath("$.*.description", iterableWithSize(1)));
}

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


Деплоймент


Чтобы собрать и запустить наше приложение в продакшене есть несколько вариантов.


  1. Задеплоить полученный JAR (или даже WAR) в сервлет контейнер, например Tomcat. Это не самый простой путь, его нужно выбирать только если у вас уже есть работающий сервлет контейнер или сервер приложений.
  2. Использовать магию Spring Boot. JAR файл, собранный используя плагин Spring Boot (который автоматически добавляется в проекты созданные через Spring Initializr), является полностью самодостаточным.

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


  1. ./mvnw package
  2. java -jar ./target/demo-0.0.1-SNAPSHOT.jar

Для деплоймента этого JAR файла не нужно ничего, кроме установленной Java (JRE). Это так называемый fat JAR — он включает в себя и встроенный сервлет контейнер (Tomcat по умолчанию) и фреймворк, и все библиотеки-зависимости. По сути, он является единственным артефактом деплоймтента — его можно просто копировать на целевой сервер и запускать там.


Более того, файл можно сделать "выполняемым" и запускать его просто из командной строки (Java, конечно, все равно необходима).


На базе этого файла можно легко создать Docker образ или установить его как демон. Больше деталей доступно в официальной документации.


Заключение


Получилось, все же, очень сжато — но уложить даже самый простой вводный курс по Spring в рамки одной статьи не очень просто. Надеюсь, это поможет кому-то сделать первый шаги в Spring-е, и хотя понять его фундаментальные концепции.


Как вы успели заметить, в тексте статьи много раз звучало слово "магия Spring". По сути своей, это очень "магический" фреймворк — даже взглянув на самую верхушку айсберга мы уже видели, что Spring много всего делает в фоне. Это является и плюсом, и минусом фреймворка. Плюс несомненно в том, что многие сложные вещи (очень многие) можно сделать одной аннотацией или зависимостью. Минус же это скрытая сложность — чтобы решить какие-то сложные проблемы, заставить фреймворк работать в крайних случаях или понимать все тонкости и аспекты нужно его неплохо знать.


Чтобы сделать этап "знать" как можно проще, Spring обладает отличной документацией, огромным сообществом, и чистыми исходниками, которые вполне можно читать. Если расположить Spring на шкале Рича Хики, он (Spring) несомненно попадет в easy, но уж точно не simple. Но для современного энтерпрайза (и не только энтерпрайза) он дает невероятные возможности чтобы получить production-ready приложение очень быстро и концентрироваться на логике приложения, а не инфраструктуры вокруг.


Ссылки


Поделиться с друзьями
-->

Комментарии (94)


  1. Urgen
    20.07.2017 05:17
    +1

    Для новичка статья не очень. Знакомство со спрингом нужно начинать явно не с бута. Для тех, кто со спрингом уже знаком, статья так же не будет полезна, т.к. таких туториалов достаточно на том же spring.io.


    1. AndreyRubankov
      20.07.2017 08:45
      +1

      Если я правильно понял автора, его цель была развеять миф, что Java for Web — это сложно.
      Правда, текста сильно вышло. Можно было сделать короче на 20% — эффект был бы лучше, по-моему.


      1. izzholtik
        20.07.2017 09:34
        +4

        Почему вообще кто-то думает, что это сложно? Меня просто убил заплюсованный комментарий в соседей статье, призывающий забить на сложные сервлеты и использовать фреймворки. Что сложного в сервлете, поставить одну аннотацию и переопределить один метод?..
        Так и в этой статье. Что такого сложного в деплое WARника на сервер, что автор советует просто нажимать зелёный треугольник и не думать над происходящим?


        1. Shatun
          20.07.2017 10:40

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


          1. otvorot2008
            20.07.2017 12:19
            +1

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

            Да, это только с одной оговоркой — если все заранее настроено, и работает как часы


            1. Shatun
              20.07.2017 17:46
              +1

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


        1. Urgen
          20.07.2017 10:45

          Честно говоря, в своё время меня тоже отпугнула сложность сервлетов, и первый свой web-проект я писал на Vaadin :) Уже потом я осознал необходимость изучения таких низкоуровневых технологий.


        1. alek_sys
          20.07.2017 10:46
          +3

          «Напишите такую книгу, которую бы вам самим хотелось прочитать» — то же, и со статьей. Я хотел написать статью, которой мне самому не хватало когда-то. Речь в комментарии, о котором идет речь (я его оставил), шла что для новичка это не нужно. Это даже не то, чтобы сложно, это скорее лишний уровень абстракции в самый первый день. Так же, как и деплой WAR-ника на сервер (который же надо поставить и запустить, а мы новички). И там даже ниже в комментариях был вопрос — а как убрать имя WAR из пути? Конечно, мы же все знаем — назвать его root.war. Но я намекну — для человека, который первый раз в жизни видит Tomcat это не так же очевидно.

          Речь не про то, что какие-то концепции (сервлеты) — сложные. Скорее про то, что они не нужны в самом начале пути, их можно узнавать постепенно.


        1. AndreyRubankov
          20.07.2017 11:28
          +1

          Для человека, который имеет опыт и багаж знаний — это действительно не сложно.
          А вот для человека, который мало что знает, но хочет «войти в ИТ» и стоит на раздолье: C# | Java | PHP | JS | Python — это будет страшно и не понятно, в конечном случае, выберет то, что будет проще выучить ;-)

          И плевать, что WAR и энтерпрайз сервера — это «просто». Новичек не захочет тратить полгода на изучение сервера и всех его внутренностей, чтобы написать свое первое простое приложение. Он захочет «тык-тык и в продакшен!». И он будет прав(((


          1. izzholtik
            20.07.2017 12:48
            +1

            Но…
            WAR (который просто JAR с другим расширением (который просто ZIP с другим расширением)) из любой IDE собирается Maven'ом в один клик. Проект из архетипа генерируется тоже в один клик. Даже без IDE.
            Деплой в томкат — тупо копирование файла и рестарт. Деплой в GlassFish/WildFly — тупо загрузка файла через веб-панель. Установка библиотек — просто копирование в lib с перезапуском сервера. Даже в томкате актуальной версии для актуальных версий библиотек никакие конфиги править не надо. Это всё несколько оптимистично, но новичок в рамках проекта «домашняя страничка с чатиком» ни на что непреодолимо запутанное не нарвётся.


            1. alek_sys
              20.07.2017 15:05
              +4

              Здесь вопрос не в сложности, а в количестве новых понятий. Архитип — уже новое. Maven плагин для толстого Jar надо ещё настроить, для тонкого — знать какие зависимости и куда деплоить. Надо смотреть что такое WlidFly и GlassFish, выбирать из них. Получается много не сложных по отдельности составных частей — но все вместе получится не очень просто.

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


              1. intet
                21.07.2017 09:00

                У java высокая планка в хода по сравнению с другими языками. Чтобы запустить простейшее приложение необходимо сразу разобраться с кучей вещей. Возможность выбора из нескольких вариантов ясности тоже не добавляет. Например надо выбрать сервер, а для человека начинающего изучать web разница и между tomcat с glassfish далеко не очевидна. Новичек посмотрит на этот зоопарк и плюнет уйдя на nodeJs, где сложность растет постепенно.


                1. olegchir
                  24.07.2017 09:57
                  +1

                  У меня с NodeJS было как-то все наоборот. Нужно было за день запилить сайтик на NodeJS, которым раньше не пользвоался. В первый же час знакомства, попытка прикрутить фреймворк для нормальной авторизации в веб-приложении, вылилась в очень слабую документацию и чтение исходников этого фреймворка. Сайтик запилил, но после этого еще несколько дней приходил в себя от выгорания. Такой-то постепенный рост сложности, дайте два!


        1. mayorovp
          20.07.2017 12:41

          Что сложного в сервлете, поставить одну аннотацию и переопределить один метод?..

          Когда лично я только убегал из мира Java, ни о каких аннотациях речи не шло — требовалось прописать сервлет в web.xml, притом два раза. Вот на тех, кто навеки запомнил мир Java именно таким, пост и рассчитан.


          1. izzholtik
            20.07.2017 13:05

            Дважды? Видимо, это действительно было очень давно, я о таком даже не слышал. Или имеется в виду маппинг сервлетов?
            Если уж на то пошло, чем плох web.xml, кроме того, что XML вышел из моды? Неудобно — да, но в JEE (особенно до 6) есть множество гораздо более кривых вещей.


            1. mayorovp
              20.07.2017 13:19

              Да, надо было зарегистрировать сам сервлет и отдельно его маппинг. Ужасен он тем, что его модель — сильно избыточна: зачем вообще придумали сервлеты и маппинги отдельно?


              1. Urgen
                20.07.2017 20:09

                Не знаю, есть ли это в jee 6, но в jee 7 в web.xml сервлеты можно вообще не прописывать, у меня, например, в одном из тестовых проектов в web.xml заполнено только название проекта. Сами сервлеты в коде помечаются аннотацией WebServlet.


                1. izzholtik
                  20.07.2017 23:10

                  ЕМНИП, как раз в 6 версии ввели.

                  Я хоть сколько-то нетривиальное аннотирование недолюбливаю по одной простой причине: если сервер/контейнер не поддерживает какую-то функцию, или поддержка настроена неправильно, она просто не будет работать. Молча, без каких-либо ошибок в логах. И при переносе на другую платформу / большом обновлении / внезапном регрессе начинается бестолковое метание в попытках понять, что же именно отвалилось, почему не вызывается часть логики, валятся NPE, и нет ли противоречий в конфигурации, размазанной по куче файлов.


                  1. Urgen
                    21.07.2017 10:08

                    А конфигурацию и не нужно размазывать по куче файлов. Это уже ошибка в архитектуре проекта и она не имеет отношения к конкретной реализации, будь то конфигурирование с помощью аннотаций или xml.


                1. AstarothAst
                  21.07.2017 08:51
                  -1

                  Я больше скажу — сам web.xml не обязателен.


              1. sshikov
                23.07.2017 19:53

                Странный вопрос.

                Маппинг — это отображение url на сервлет. Вот этот сервлет обрабатывает вот такие url. И еще вот такие (потенциально — несколько).

                Сервлет — это на минутку, сам сервлет. У него есть параметры (настройки). Любые.

                Отношение между маппингом и сервлетом — M:1. Я готов согласиться, что это сделано не идеально удобно (я бы наверное сделал маппинг дочерним элементом для сервлета), но что это непонятно — хм. Это может быть непонятно если человек почему-то новичок в веб технологиях вообще. Или если доки совсем не читать.


                1. mayorovp
                  23.07.2017 19:57

                  Вот если бы сделали дочерним элементом — то и файл web.xml был бы и не таким страшным...


                  1. sshikov
                    23.07.2017 20:04

                    Тут дело такое — с самого начала в J2EE предполагалось, что компоненты может настраивать как программист, так и деплоер. И эта роль была предусмотрена, вместе с сервер-специфичными xml дескрипторами для него.


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


                    1. mayorovp
                      24.07.2017 10:15

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


                      Настраиваемые маппинги возможны только для внешнего API, но не для внутреннего. Потому что при исправлении маппинга нужно как минимум исправить еще и то место, где формируется ссылка. А уж если на правильной иерархии маппингов что-то хитрое завязано — то ее и вовсе при деплое сменить невозможно.


        1. debose
          24.07.2017 10:04

          Это действительно сложно для новичка. Когда в одиночку с нуля пытаешься разобраться как запустить свой первый хэлло ворлд то постоянно что-то идёт не так. В интернете огромное количество туториалов разной степени устаревшести. В результате можно запросто потратить несколько дней только на то чтобы получить наконец вожделенную строку в броузере. Все эти war, jar, application server, servlets, jee, spring mvc, tomcat, jboss, glassfish всё это поначалу ужасно непонятно. И каждый апп сервер надо ещё как-то настроить. Spring boot очень всё упрощает.


    1. Crandel
      20.07.2017 08:48
      +1

      Я только начинаю учить джава и после питона с джанго — статья очень понятна. Может тем, кто незнаком с вебом вообще будет трудно, но тем кто использовал уже какие-то веб фреймворки с ОРМ, МВС и тд. особых проблем не должно быть


      1. hssergey
        20.07.2017 10:26

        На джанго, кстати, очень похоже идеологией. Можно провести простое соответствие:
        models.py в джанго — entity в спрингбуте
        urls.py и views.py — controller в спрингбуте (маппинг урлов записывается прямо в контроллере)
        реализация всяких сложных запросов через джанговский foo.objects.filter() — методы findByBlablabla в спрингбутовских репозиториях

        Еще в спрингбуте «искаропки» идет не упомянутый в статье шаблонизатор Thymeleaf, который хоть и отличается по синтаксису от джанговского, но тоже без проблем позволяет использовать условия, циклы и т.п.


        1. Crandel
          20.07.2017 12:12

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


    1. alek_sys
      20.07.2017 09:52
      +2

      Да, для тех, кто знаком со Спрингом читать статью вообще не интересно, целевая аудитория исключительно новички.

      А вот по поводу Boot не соглашусь, тут я, увы, из другого лагеря — я считаю, что в наши дни не использовать Boot — это странно. Т.е. допускаю, что для некоторых команд и проектов проще без него, но в среднем по больнице — это именно то, чего не хватало Spring. Чего-то, что просто работает. А уж тем более для начинающих разработчиков.


      1. poxvuibr
        20.07.2017 11:48

        в наши дни не использовать Boot — это странно

        Поддерживаю. И для новичка Spring Boot это то, что надо.


        1. Urgen
          20.07.2017 13:11
          +1

          В буте слишком много «магии», которую можно добавить исключительно через зависимости и конфиг application.properties/yml. Во многом за счёт этой магии приложения на буте сейчас набирают популярность. Новичок в это лезть не будет, а потом, столкнувшись с первыми проблемами, типа «что за странная ошибка при старте приложения?» надолго погрязнет в поиске какого-то решение вместо того, чтобы заниматься изучением основ.


          1. poxvuibr
            20.07.2017 13:30
            +1

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

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


          1. sshikov
            24.07.2017 19:38

            +1. Спринг на мой взгляд и так не слишком сложен, во всяком случае большая его часть.


    1. krupt
      20.07.2017 09:52
      +1

      К сожалению, это современная тенденция разработки. Берем Spring Boot, делаем что-то дома и идем на собеседование на Senior Java Developer. Если у собеседующего такой же опыт со Spring Framework, то собеседование пройдено. А если собеседующий начнет ходить вокруг всего, то печаль-беда для кандидата :)
      Но если есть понимание как это все работает, то тут соглашусь с автором, что Spring Boot позволяет получить каркас приложения очень быстро.


      1. alek_sys
        20.07.2017 10:01
        +2

        Ну я бы все таки так не обобщал, собеседование это вообще сложная и непростая тема, безотносительно технологий и опыта (я, кстати, недавно переводил статью как раз про собеседования в Pivotal).

        И в целом, видеть за обучающей статьей людей, которые могут «злоупотребить» отраслью и пройти собеседование на Senior Java Developer — это немного негативный взгляд на мир. Можно же видеть и по другому — это современная тенденция разработки, что для начинающих разработчиков доступно все больше инструментов, чтобы они могли стать продуктивными с самого начала, выпускать продукт как можно раньше, и параллельно учиться, узнавать сложные концепции шаг за шагом. И мне кажется, это прекрасно как для отрасли, так и для разработчиков.


        1. krupt
          20.07.2017 10:05
          +1

          Соглашусь с Вами. Но многие начинающие специалисты не лезут внутрь сложных концепций и это печально.


          1. poxvuibr
            20.07.2017 11:49

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

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


    1. hssergey
      20.07.2017 10:32

      А почему не с бута? Голый спринг весьма трудоемко конфигурить. Да, гибкости больше, но гораздо больше вариантов ошибиться в хмл конфигурации во всяких xsi:schemaLocation.


      1. AstarothAst
        20.07.2017 10:39

        Java config позволяет избежать всех жутких xml'ей, да и погибче будет.


        1. sshikov
          23.07.2017 19:56

          Никогда не понимал, в чем эта жуть. Многословно (иногда) — ну может быть. Не так гибко, как конфигурирование в java — очевидно тоже да, код более гибок, чем статический xml.


          1. AstarothAst
            24.07.2017 10:12
            +1

            xml не дебажится, к примеру. Это обстоятельство вместе с «многословно» и «не гибко» и порождает «жуть». Ну, или как-то так…


  1. slavap
    20.07.2017 09:52

    Спасибо, просто и достаточно для начала.


  1. StanislavL
    20.07.2017 10:05

    @Controller
    public class IndexController {
    
        final VisitsRepository visitsRepository;
    
        public IndexController(VisitsRepository visitsRepository) {
            this.visitsRepository = visitsRepository;
        }
    ...
    }
    


    Тут наверное @Autowire потерялся на конструкторе. Чтоб понятнее было.


    1. StanislavL
      20.07.2017 10:12
      +2

      Я нашел что со спринга 4.3 для единственного конструктора необязателен @Autowire. Был не прав.


      1. zirix
        20.07.2017 12:33

        @Repository
        public interface VisitsRepository extends CrudRepository<Visit, Long> {
        }

        Аннотация Repository не нужна для CrudRepository и его детей.
        Кроме CrudRepository есть еще PagingAndSortingRepository и JpaRepository. Имеет смысл заглянуть в них.


        И забыли написать как генерировать SQL запрос через репозиторий. если добавить в него метод:


        List<Visit> findByDescription(String description);

        то сгенерируется запрос вида select * from Visit where description =? (т.е. генерируется из названия метода)
        Можно самому написать запрос на HQL добавив аннотацию Query("здесь любой HQL запрос")


        Для "final VisitsRepository visitsRepository;" я бы добавил private.
        И можно заменить конструктор на @Autowired:


        @Controller
        public class IndexController {
        
            @Autowired
            private VisitsRepository visitsRepository;
        ...
        }


        1. semio
          20.07.2017 15:12
          +1

          Аннотация Repository не нужна для CrudRepository и его детей.
          Кроме CrudRepository есть еще PagingAndSortingRepository и JpaRepository.

          *Не нужна для наследников Repository. Можно так же кастомные репозитории делать, наследуясь прямо от Repository. Если не нужны методы CRUD, к примеру.

          И можно заменить конструктор на @Autowired:

          Делать @Autowired на полях не рекомендуется. В документации этот момент прописан.


          1. rraderio
            20.07.2017 16:57

            Делать @Autowired на полях не рекомендуется. В документации этот момент прописан.

            Что-то не могу найти, не поделитесь ссылкой?


            1. semio
              20.07.2017 17:21
              +1

              Например вот: Setter-based dependency injection, в рамочке, где «Constructor-based or setter-based DI?»


            1. alek_sys
              20.07.2017 22:31

              Ссылки сейчас не найду, но общее правило, которому рекомендуют придерживаться и которое сильно облегчает рефакторинг и написание тестов это: если зависимость для вашего компонента обязательна и без нее он не работает, то это должно быть в конструкторе (т.к. это контракт), если это зависимость опциональная, и без нее компонент работает (логгер) — ее можно через property injection. Но вообще кейсов для Property Injection очень мало и с ними надо быть очень осторожным.


              1. shpektaras
                21.07.2017 11:54

                Не соглашусь, что @Autowired через сеттер или конструктор

                облегчает рефакторинг и написание тестов
                . Я бы даже сказал наоборот — утруждает.

                Мне нужна зависимость? Я пишу одну строку кода поля и ставлю одну аннотацию. Все. Никаких сеттеров, конструкторов, присвоения значений в конструкторе не нужно.
                Мне нужно написать тест? Легко — аннотации Mock, @InjectMocks и MockitoAnnotations.initMocks(this) в тестовом классе — и мой объект для юнит-тестирования готов. Никаких лишних телодвижений, меньше кода и усилий. Не понимаю, почему кто-то считает @Autowired над полем плохим тоном, я за несколько лет работы со спрингом так и не нашел объяснения для себя почему так делать плохо, а вот через конструктор — хорошо.


                1. AndreyRubankov
                  21.07.2017 14:24
                  +3

                  1. Инжект в приватное поле требует поставить field.setAccessible(true), которые не всегда возможен.
                  2. Начиная с Java 9 field.setAccessible(true) — будет не всегда доступен (если не будет выключен полностью).
                  3. Начиная с Java 10 — его собирались выключить полностью или еще больше ограничить.

                  4. Инжект в поле класса — это прямой путь в ад в мир, где ты не можешь работать с объектом без рефлексии и без контейнеров. Чтобы выполнить трифиальный тест — нужно подымать контейнер для того, чтобы заинжектить поле!!!

                  5. Инжект в поле класса не дает возможности сделать оптимизацию по кешированию конструкторов (см. метод референс) и заставляет всегда выполнять инжект только через рефлексию.


                  1. shpektaras
                    22.07.2017 13:13
                    -1

                    Спасибо за ответ, но я все равно не вижу преимуществ DI через конструктор вместо инджекта в поле.

                    1. Инжект в приватное поле требует поставить field.setAccessible(true), которые не всегда возможен.

                    Не всегда — это когда? Я таких случаев не знаю.
                    2. Начиная с Java 9 field.setAccessible(true) — будет не всегда доступен (если не будет выключен полностью).
                    3. Начиная с Java 10 — его собирались выключить полностью или еще больше ограничить.

                    И я почти уверен, что до релиза там будет все как было, так как они поломают кучу библиотек, а backward-compatibility это главная фишка Java. Это все-таки не Unsafe, на backward-compatibility которого можно наплевать, так как он формально не часть спецификации.
                    4. Чтобы выполнить трифиальный тест — нужно подымать контейнер для того, чтобы заинжектить поле!!!

                    Не нужно, гуглить в сторону @InjectMocks и MockitoAnnotations.initMocks(this)

                    5. Инжект в поле класса не дает возможности сделать оптимизацию по кешированию конструкторов (см. метод референс) и заставляет всегда выполнять инжект только через рефлексию.

                    И чем же это плохо? При setAccessible(true) у вас перформенс будет почти тот же, что и при сеттере. Кеш конструктора сделает оптимизацию вам в лучшем случае на несколько миллисекунд.


                    1. fogone
                      22.07.2017 16:14

                      Лучше инжекшна через конструктор только вообще без инжекшена. Мой любимый подход — это когда бины полностью отвязаны от контейнера и полностью самодостаточны. Вся контейнерная логика находится в java-конфигурации, которая инстанцирует бины явно с нужными параметрами конструктора, вызовами нужных сеттеров, при помощи билдеров или фабрик, не важно как по большому счету. Все инжекшены на уровне конфигурации. Тем самым мы выносим логику инициализации контекста выполнения и создаем косвенность на уровне конфигурации. Зато нам не важно какие объекты у нас выступают в роли бинов. Как заанотированы их поля или конструкторы. Мы можем тестировать этот бин без какой-либо привязке к инжекшенам вообще и делать бины более нативными для языка. Лично для меня это намного более чисто, гибко и представляется как конструктор, где бины это просто классы, которые мы собираем при помощи конфигурации и можем при желании сделать конфигурации разными, гибкими и настраиваемыми. Т.е. есть у меня есть класс TcpServer из реактора, я могу просто получить в один бин конфиг, передать его в другой метод декларации бина и там на основе этих параметров инстанцировать уже сам сервер. Хотя сам сервер если разобраться ничего не знает о контейнерах. То же и с другими компонентами — хорошо, если они никак не завязаны на инжекшн и могут работать независимо — в этом смысле правильное поведение бина, если он всё зависимости принимает в конструктор и просто не создается без них. Мы так проектируем обычные классы, почему те, что мы используем в контейнере должны чем-то отличаться? К слову в самом спринге всё примерно так и сделано (бут вообще весь состоит из таких конфигураций). Нигде вы не увидите аннотаций @Service или @Inject, потому, что всё драйвится внешними конфигами, в свою очередь потому, что это дает возможность гибкости и настриваемости. Не вижу причин, почему мой код не может быть таким.


                    1. AndreyRubankov
                      22.07.2017 16:28

                      Не всегда — это когда? Я таких случаев не знаю.

                      Например Java Security Policy позволяет запретить рефлексию (но в этом случае, думаю, весь Spring не будет работать)
                      если погуглить немного: https://stackoverflow.com/questions/14639753/reflection-security

                      до релиза там будет все как было, так как они поломают кучу библиотек, а backward-compatibility это главная фишка Java.

                      С обратной совместимостью будет все хорошо — если запускать приложение «по-старому», все будет работать.
                      Но если запускать приложения как Модульные — то это уже будет новый функционал и тут уже можно внедрить новые правила, что успешно и сделали.
                      если погуглить немного: http://in.relation.to/2017/04/11/accessing-private-state-of-java-9-modules/

                      Не нужно, гуглить в сторону @InjectMocks и MockitoAnnotations.initMocks(this)

                      Mockito — это замечательная библиотека, не спорю. Именно Mockito и выступает в роли IoC контейнера в тестах. Или если это правильное Spring Boot приложение с тестами, — там будет свой Spring Boot IoC for Tests.
                      ps: Проблемы тестирования через Mockito достойны целой статьи.

                      И чем же это плохо? При setAccessible(true) у вас перформенс будет почти тот же, что и при сеттере.

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

                      К слову, добавлю еще один пункт:
                      6. Классы, которые используют инъекцию зависимостей в поля (@Inject, @Autowired, Resource etc.) нельзя будет переиспользовать без IoC контейнера. Это не самая большая проблема, но она тоже существует.


          1. zirix
            20.07.2017 18:42
            -1

            Говоря "не рекомендуется" вы пугаете новичков.
            Делать через конструктор — это рекомендация, помогает защититься от некоторых ошибок.


    1. rraderio
      20.07.2017 16:51

      Почему не поставить @Autowire на поле visitsRepository?


      1. StanislavL
        20.07.2017 16:54

        Можно, но необязательно. В повых версиях спринг сам разберется. А вообще рекомендуют по возможности @Autowire на конструктор. Так меньше шансов что останется не инициализированными. Но в случае циклических зависимостей не прокатит.


        1. fogone
          21.07.2017 08:39

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


          1. StanislavL
            21.07.2017 08:54

            Я обычно спрашиваю, что будет если в singleton scope bean завайрить session scope bean. И копать глубже как эта магия устроена.


          1. alek_sys
            21.07.2017 09:24

            А вот раз уж вы подняли такую тему — а зачем? Я уже давно работаю со Спрингом, но как циклическую зависимость сделать — не знаю. Более того, никогда не делал — и считаю наличие циклической зависимости проблемой дизайна. Т.е. круто, что человек это знает, но вот если не знает, то это о чем говорит?


            1. fogone
              21.07.2017 14:54

              В реальности обычно этого никто не знает, потому что, во-первых, плохой тон, во-вторых, нужно очень редко. Поэтому когда человек, говорит, что не знает, тогда я спрашиваю: а как вы думаете такое вообще возможно? А если бы вы сами это собирались реализовать? И тд. А некоторые сами пытаются предположить как оно там или предполагают как это можно было бы реализовать. Кто-то говорит, что это бедпрактис. Что бы человек ни сказал, всё дает представление о нем. А ответ: не знаю и даже представить не могу — больше других.


              1. zip_zero
                25.07.2017 00:10

                Дайте угадаю: если кто-то и ответил, то запросил настолько космических денег, что вы его не стали рассматривать.

                А все-таки: зачем вы на собеседованиях интересуетесь про такие страшные детали?
                Вопрос не праздный.
                1. Собеседование длится всего-то час или около того. Вам больше время некуда потратить?
                2. Если разработчик ответил правильно — как этот ответ характеризует его как специалиста и профессионала?

                Те компании, которые вопят, что «нет нормальных разработчиков», как правило, не умеют правильно проводить технические интервью. Или умеют, но не хотят – потому что нормальный специалист их технологический стек на тряпки раздерёт вместе с архитектором и тим-лидом, а job security – это страшная сила :)


                1. fogone
                  25.07.2017 11:59

                  Дайте угадаю: если кто-то и ответил, то запросил настолько космических денег, что вы его не стали рассматривать.

                  Выше писал о том, что правильный ответ здесь не важен, к тому же его обычно никто не знает. И лично мне на денежные претензии кандидата глубоко плевать, потому что моя сфера — это исключительно его техническая компетенция.
                  1. Собеседование длится всего-то час или около того. Вам больше время некуда потратить?

                  У меня много способов потратить время, но если я провожу интервью, то я трачу время именно на это. Мне нужно максимально быстро получить представление о человеке как о специалисте и коллеге, а этот вопрос не только позволяет в редких случаях выявить человека, который разбирается в нужном мне стеке, но и дает огромное количество материала для обсуждения и понимания уровня кандидата. Лично меня вообще не интересует может ли кандидат засунуть квадратный люк в круглый колодец или знает ли он наизусть все методы SessionImplementor, на мой взгляд в первую очередь важны личные качества кандидата и как он себе представляет разработку, насколько он четко мыслит в этом направлении. Всё это можно выяснить начиная с этого вопроса.
                  2. Если разработчик ответил правильно — как этот ответ характеризует его как специалиста и профессионала?

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


          1. rraderio
            21.07.2017 14:18

            У вас в коде есть циклические зависимости?


            1. fogone
              21.07.2017 14:56

              обычно нет, но как я написал выше, тут дело не в самой циклической зависимости, а в том как человек будет думать об этой задаче. Знает ли он о том, что спринг умеет проксировать бины? Предложит ли он использовать прокси для решения такой задачи? Может быть какая-то смешная третья опция… для собеседования всё хорошо.


              1. rraderio
                21.07.2017 15:25

                смешная

                это избавится от циклических зависимостей?


                1. fogone
                  21.07.2017 17:41

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


  1. AFH
    20.07.2017 10:47

    Как писать на Spring в 2017

    А не пора уже про webflux писать? Пробовали уже использовать? Не хотите про это дело написать?


    1. alek_sys
      20.07.2017 10:47
      +2

      Пора, пробовал, написал :)


      1. AFH
        21.07.2017 04:56

        Кстати, за информацию про DevTools отдельное спасибо!


  1. xpendence
    20.07.2017 13:51
    +1

    Очень приятно, что моя статья вдохновила Вас на написание этой полезной статьи о Spring. Надеюсь, Ваша статья поможет мне вникнуть в Spring (пока с этим туго).

    Как хорошо, что среди легиона «професиональных комментаторов» попадаются люди, конструктивно подходящие к проблемам новичков :)


    1. AstarothAst
      20.07.2017 15:08
      -1

      вникнуть в Spring (пока с этим туго)

      Ютуб, ищите доклады Жеки Борисова «Спринг-потрошитель» две части — он там все кишки Спринга выворачивает с разбором и лайфкодингом. Полезно даже если ничего не понятно, просто потому что все с живыми примерами.


      1. Crandel
        20.07.2017 22:15

        ищите доклады Жеки Борисова «Спринг-потрошитель»

        Спасибо за наводку, кто еще не нашел, вот ссылка


        1. alek_sys
          21.07.2017 01:26

          Еще посоветую отличное видео про Spring Boot от двух ключевых людей в команде — Andy Wilkinson и Stephane Nicoll. Правда, видео идет два с половиной часа на английском, но зато там как раз про внутренности Spring Boot, как и что он конфигурит, какие у него могут быть критерии и т.п.


          1. Crandel
            21.07.2017 07:35

            Большое спасибо, очень полезно, жаль плюс не смогу поставить


  1. pmcode
    20.07.2017 17:36

    По этой теме есть отличное видео с Joker 2016. Человек наглядно показывает как быстро и просто можно сделать REST API со спрингом.


  1. Fen1kz
    20.07.2017 19:45
    +1

    Я думаю что я из ЦА статьи (убежал с серверной джавы ещё до массовой распространенности Boot), но мне статья не понравилась, простите.


    Дело в том, что я не верю туториалам вида:


    Шаг 1: Ставите тулзу-для-проекта/скачиваете этот проект со специального сайта-билдера
    Шаг 2: Вот вам hello world!


    Потому что для чего-то сложнее чем hello world придется отойти на шаг вправо/влево, а там расстрел и долгие мучительные копания в конфигах незнакомой системы сборки или ещё чего.


    То есть вместо объяснения магии Spring, вы показываете как всё типа просто, но вся ваша простота улетучится как только надо будет отойти на шаг влево/вправо. И вместо простоты там будет расстрел похлеще JSP.


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


    1. GerrAlt
      20.07.2017 22:25

      Поддержу, в свое время сам нарвался на похожие проблемы с туториалами по Spring-Data-REST. Если делаешь все как в туториале — все супер просто и понятно, потом надо чуть-чуть допилить/поменять и оказывается что чтобы это сделать надо наворотить кучу кода, переопределить какие-то методы у оберток, о которых вообще не рассказывалось, состряпать пару не очевидных реализаций каких-то интерфейсов и подсунуть через @Configuration.

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


      1. alek_sys
        20.07.2017 22:33

        Вообще это хорошая мысль, может я соберусь продолжить эту статью второй, и пусть на базовом уровне, но объяснить внутренности приложения — типа бинов, конфигурации, автоконфигурации, что делает Boot. Думаю, будет очень полезно. Может даже на этом же приложении рассмотреть, развеять магию и сорвать покровы!


        1. dikkini
          20.07.2017 23:56

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


  1. dikkini
    20.07.2017 23:23

    Ну получается что за последнии 5 лет мало что изменилось :-)


  1. dm4x
    21.07.2017 01:11

    Спасибо автору, статья полезная, особенно в плане получения общего представления как, что и зачем. Для старта самое то, чтобы не испугаться раньше времени )
    По себе заметил интересную особенность: проект на чистом спринге + xml я смог настроить и привести в рабочее состояние быстрее чем аналогичный на boot+java-config.


  1. kovserg
    21.07.2017 01:11

    А какие у вас размеры WAR файлов в гигабайтах?


    1. alek_sys
      21.07.2017 01:18

      Make JAR, not WAR! Размер JAR с включенным сервером (!) Tomcat, Spring, Spring Data, H2 in-memory DB (пример из этой статьи) — 0.028 Gb (28 Mb). Причем как я уже говорил, кроме этого файла на сервере не нужно ничего (кроме JRE). Так что сарказм, скорее, себя не оправдал :)

      PS. Сразу отвечу про использование памяти — сам фреймворк съедает примерно 32 Мб памяти. У Дейва Сайера, одного из участников команды Spring Boot есть исследование про потребление памяти Спрингом.


  1. SlavikF
    21.07.2017 02:30

    Я вот, PHP-ник, посматриваю в сторону Java. Появилась нужда использовать некоторые библиотеки, а они нормально написаны под Java, а под PHP — так себе…
    Очень интересно было прочитать эту статью, тем более, что пару месяцев назад я уже экспериментировал с этим.

    Вот какие у меня остались вопросы после экспериментов и прочтении статьи. Может это и не совсем вопросы, которые должен задавать новичок, но:

    1) Магия конфигурирования сервера
    Вот есть у меня PHP. Он обычно стоит за Апачем, или за Nginx. А вот Spring работает сам по себе (хотя его конечно тоже можно запроксировать). Ну вот захотелось мне, чтобы сервер слушал на порту 80. И внезапно выяснилось, что для этого надо запускаться по рутом, а это нехорошо. Я погуглил, советов много, но как-то всё не прямо.
    Ну и кроме порта есть и другие настройки. В общем и целом, если хочется что-то подтюнить — то у PHP есть для этого два места: конфиг Апача и php.ini. Там и настройки памяти, и настройки потоков, параллельности и т.д. Я уверен, что всё это можно настраивать и в Spring, но как-то тяжелей тут всё это осмыслить.

    2) Магия с базами данных
    Когда я читаю, что «будет автоматически создана модель данных (таблицы) в БД», то мне становится как-то неуютно: что-то там происходит автоматически, а мне про это не рассказали.
    Конечно, с этим можно разобраться. Но если на PHP я пишу простенькое приложение, то я напишу SELECT да и всё, а тут надо вникать в эту магию и она выглядит не очень просто.


    1. SlavikF
      21.07.2017 02:35

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


    1. alek_sys
      21.07.2017 09:43

      Вопросы очень хорошие.

      1) Идея Spring во многом — это абстракции. Мы не работает с базой данных напрямую, работаем с абстракцией — база это (теоретически) заменяемая реализация. То же самое и с сервером — мы не настраиваем сервер напрямую, мы используем конфиг файл, по которому потом Spring Boot настроит сервер. Это делается в файле application.properties — там есть целая секция Web Properties (включая порт). И теперь если мы заменим Tomcat на Jetty (а это тривиальное изменение в pom.xml) — то те же настройки будут применены и там.

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

      Про root думаю актуально и для Апача, просто он скорее всего уже был установлен (под рутом) и работает как демон — поэтому занимает порт 80.

      2) Важный момент — модель будет автоматически создана только в нашем конкретном приложении, т.к. это in-memory база (и считается временной для разработки). При подключении к реальной базе — Spring (точнее, Spring Data, а еще точнее конкретная ORM — Hibernate) может либо создать схему автоматически, либо проверить, что модель энтитей соответствует структуре таблиц. Первый вариант в продакешене, разумеется, никто не использует — все работают с миграциями БД (flyway или liquibase). Второй вполне работает для прототипов или интеграционных тестов.

      SELECT и любые другие запросы писать точно так же можно, просто они остались за рамками статьи.


      1. zesetup
        24.07.2017 23:55

        1) можно сделать перенаправление портов с iptables 80->8080 443->8443.
        2) можно запустить Apache Web-server(или что-то другое) как прокси для tomcat


    1. alix_ginger
      23.07.2017 15:29

      И внезапно выяснилось, что для этого надо запускаться по рутом, а это нехорошо.
      Так и апач под рутом нужно запускать, чтобы на порту < 1024 слушать. Это не спринг виноват.


      1. SlavikF
        23.07.2017 23:41

        Не совсем так…
        Да, Апач старует, как рут, но но запросы обрабатываются в контексте пользователя. Обычно www-data.
        Для этого у Апача есть какой-то свой механизм (it uses setuid to switch to user context of specified user in httpd.conf).

        Конечно, можно и в Spring написать подобный механизм, но его нужно писать, а там уже есть.


  1. Gravit
    21.07.2017 06:22
    +1

    Как раз недавно пытался разобраться с Java Web и Spring в частности.
    Одна запись в xml не туда — приложение не деплоится, странные исключения в логах, которые в гугле не ищутся, ошибка 404 в любой непонятной ситуации. Туториалы только по одной IDE — Esclipe, ручное создание xml файлов, ручное прописывание каждого контроллера. О Spring Boot слышу впервые из этой статьи, и это то, что мне нужно. Спасибо автору за такую статью! Жду продолжения.


    1. Urgen
      21.07.2017 11:23

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


    1. bgBrother
      29.07.2017 20:00

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


  1. kalyaka
    25.07.2017 11:58

    А как написанное приложение задеплоить в Tomcat? Поменял в pom.xml jar на war, в томкате (версия 8.5) через manager app установил полученную war-ку. При обращении к приложению получаю 404 с описанием «The origin server did not find a current representation for the target resource or is not willing to disclose that one exists». С Java Web и Spring никогда не работал.