Чтобы разработать современное веб приложение, необходимо иметь навыки как в создании серверной части, так и клиентской. Наиболее часто встречаемое в последнее время сочетание в корпоративной среде — это Java c использованием Spring Framework для сервера и React для клиента. Однако не все разработчики обладают Full stack навыками (знаниями как в серверной так и в клиентской части), а для начинающих разработчиков создание такой конфигурации оказывается совсем непосильной задачей.

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

Более подробно рассмотрим используемые технологии


Серверная часть:


Сборка проекта — gradle 4.8.1 ( дополнительно gradle-node-plugin для сборки фронта)
Язык — Java 1.8 (или старше)
Фреймворк — Spring 5.x
База данных — HSQL 2.3.1 (для начала будет достаточно)

Клиентская часть:


Сборка проекта — webpack 4.17.2
Язык — JS6
Фреймворк — react 16.4.0, redux 3.7.2, bootstrap (reactstrap 6.3.1)
Если вас всё устраивает, то можно продолжить.

Запуск проекта


Думаю будет намного веселее, если сначала мы всё запустим и убедимся что всё работает!
Скачать проект можно отсюда

Для запуска понадобится совсем немного времени и терпения. Главное делать всё по порядку:
Более подробная информация по установке (по просьбам читателей) внизу статьи


  1. Установить java 1.8 (не забываем прописать JAVA_HOME)
  2. Установить node.js
  3. Открыть командную строку (Надеюсь что вы сами разберётесь как это сделать в вашей операционной системе)
  4. Перейти в папку проекта (Например cd C:\Git\react-start)
  5. Выполнить команду npm i (Эта команда скачает все зависимости для фронта и сложит их в папку node_modules)
  6. Выполнить команду gradle build (Эта команда соберёт ваш проект и сложит всё в папку build)
  7. Выполнить команду gradle bootRun (Теперь ваш проект запущен)
  8. Остаётся только перейти по ссылке и насладится результатом.

Вы должны увидеть нечто подобное:



Вступление


Моя основная задача в этой статье показать структуру проекта. Поэтому я буду в основном максимально доступно рассказывать какой файл в проекте за что отвечает с некоторыми лирическими отступлениями. Для бек энд разработчиков будет интересна в основном клиентская часть и наоборот.

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


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

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

Сервер:


build.gradle — Главный файл для сборки нашего проекта. в нём описаны все необходимые нам зависимости и ссылки на репозиторий где их брать. А также там прописан плагин gradle-node-plugin который при сборке серверной части автоматически собирает и фронт что несомненно очень удобно.

gradlew и gradlew.bat и папка gradle — необходимые части для запуска сборщика. Кстати если команда gradle build по каким то причинам не выполняется, то возможно вам придется установить gradle. Сделать это можно с помощью официальной инструкции.

README.md — Универсальный файл для отображения в репозитории информации о проекте.

В папке src/main/webapp/WEB-INF/ лежат два файла jboss-web.xml и web.xml при локальной работе они не используются, но если нужно будет запускать проект на web серверах типа WildFly они обязательно понадобятся.

application.yml — также не маловажный файл. В нем описывается конфигурация Spring. В частности там есть port: 8090 — порт на котором будет запущено приложение и настройки подключения к базе данных.

Если уже заговорили про базы данных то в проекте используется HSQL — это файловая база данных работающая на java. При старте проекта в папке пользователя создастся папка db/ в которой и будет храниться сама база данных. Вы можете использовать любую свою базу данных которая Вам больше нравится, например Postgress, на это нет никаких принципиальных ограничений.

Сам код серверной части располагается в папке src/main/java.

Клиент:


.babelrc — здесь хранятся всякие конфигурации для для babel. Для тех кто не знает babel — это штука которая преобразует всякие новомодные вещи во front-end разработке, такие как JS6, JS7, JSX, в обыкновенный js. В этом файле например у меня подключен плагин «plugins»: [«transform-decorators-legacy»] который позволяет использовать decorators — это как @аннотация в java. Я их не использовал, но Вы можете. Для этого всё уже настроено я проверял.

.npmrc — ссылка на репозиторий для js зависимостей.

package.json — очень важный файл здесь хранится описание всего нашего приложения, ссылки на js зависимости и команды для сборки и запуска клиентской части. Причём зависимости разбиты на две части это dependencies — зависимости которые необходимы для работы самого приложения и devDependencies — зависимости необходимые толmко для сборки проекта. В разделе scripts описаны команды buils и start которые используются для запуска только фронтальной части проекта например фронт можно запустить командой npm run start (Запустится он на порту 9090). По сути этот файл — это аналог build.gradle для клиентской части.

webpack.config.babel.js — самый главный файл во всей конфигурации — настройки сборщика webpack. По этому поводу можно писать отдельную статью, но я всё равно хочу пройтись по основным частям этого файла чтобы сформировать у Вас общее представление о его возможностях.

devServer
devServer: {
        contentBase: `/${publicPath}/`,
        historyApiFallback: {
            rewrites: [{from: /./, to: `/index.html`}]
        },
        open: true,
        port: 9090,
        publicPath: `/`,
        proxy: [{
            context: ['/api', '/endpoint'],
            target: {
                host: "localhost",
                protocol: 'http:',
                port: 8090
            }
        }]
    },


DevServer используется для разработки клиентской части. Как уже говорилось выше мы можем запустить наш фронт на отдельном порту npm run start (Запустится он на порту 9090). Все изменения в коде js будут сразу вступать в силу на этом сервере. СontentBase — корневой путь до нашего приложения. Если на сервере будет запущено несколько приложений то это важно. open: true — при запуске сервера приложение будет автоматически открываться в браузере. proxy — раздел который отвечает за пересылку обращений к серверной части которая у нас будет запущена на порту 8090.

output
output: {
        filename: 'assets/javascripts/[hash].js',
        path: path.join(__dirname, 'src/main/resources/static'),
        publicPath: `/`
    },


output — этот раздел задает место сборки нашего проекта. Если выполнить команду npm run build, то в папке src/main/resources появится клиентская часть нашего проекта.

module
module: {
        rules: [
            {
                exclude: /node_modules/,
                include: path.join(__dirname, 'src/main/js/'),
                test: /\.jsx?$/,
                use: 'babel-loader'
            },
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            },
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
            },
            {
                test: /\.(ico|png|gif|jpe?g)$/,
                use: {
                    loader: 'file-loader',
                    options: {name: 'assets/images/[name]/[hash].[ext]'}
                }
            },
            {
                test: /\.(svg|woff|woff2|eot|ttf)$/,
                use: {
                    loader: 'file-loader',
                    options: {name: 'assets/fonts/[name]/[hash].[ext]'}
                }
            },
            {test: /\.html$/, use: 'html-loader'},
        ]
    },


Раздел module указывает webpack что нужно искать в проекте файлы с расширениями .jsx, файлы стилей, картинок и шрифтов и тоже включать их в наш проект.

Раздел entry содержит ссылку на главный файл нашего js приложения.

Ну и в заключении HtmlWebpackPlugin создаст index.html файл в который включит все созданные зависимости.

Код клиентской части лежит в папке src/main/js.

Структура самого кода


В проекте я для примера сделал связь клиентской и cсерверной части через Rest и WebSocket. Кому что больше нравится. Описание самих технологий Spring Framework и Rect в интернете великое множество. Эта статья для тех у кого что-то не получается, или что-то лень. Это настроенное готовое рабочее решение содержащее в себе все необходимое чтобы перерасти в полноценный большой проект.

Также Вы можете взять этот проект в качестве отправной точки в изучении Java EE или React приложений.controller/RestController.java

Сервер:


Код клиентской части лежит в папке src/main/java.

То как там всё располагается полностью подчиняется структуре Spring Framework. Для тех кто с ним знаком не найдет там ничего интересного, а для тех кто только начинает опять же просто коротко пройдусь по файлам.

Main.java — главный файл. Содержит метод main который и запускает всё приложение.

configuration/WebSocketConfig.java — для точек входа при работе через webSocket

Контролеры — Классы отвечающие за взаимодействие серверной и клиентской частей.

controller/IndexController.java — контроллер отвечающий за отображение нашей клиентской части. Перебрасываем пользователя на url application/** (Это контекстный путь до нашего приложения)

controller/RestController.java — как видно из названия этот контроллер отвечает за обмен данными между клиентской и серверной частью по Rest. Аннотация @RequestMapping(value = "/api/rest", method = RequestMethod.GET) говорит что по запросу по адресу /api/rest сервер отдаст нам список пользователей.

Метод PUT я использовал для добавления пользователей и DELETE соответственно для удаления пользователя по ID.

controller/WebSocketController.java — определяет путь для обмена данными по webSocket. Метод getAndSendMessage получает сообщение от клиента и оправляет его обратно.

Сервисы — обычно отвечают за логику нашего приложения.

service/ORMUserService.java — в моем случае отвечает за формирование списка пользователей, а также добавление и удаление пользователей в базу данных используя в качестве данных параметры полученные от клиентской части. Для удаления пользователя — это id пользователя, а для создания — это имя, роль и пароль пользователя.

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

domain/User.java — класс который будет соответствовать таблице Table(name = «USER») в базе данных.

Данные для колонки @Column(name = «ID») будут генерироваться автоматически.

domain/Message.java — в моем случае не использует сопоставления с базой данных. данные в нём будут храниться пока приложение запущено. Служит у меня для формирования сообщений отправляемых по webSocket.
На этом с серверной частью у меня всё.

Клиент:


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

Что сделано на фронте:

  • Реализован главный layout приложения и несколько вкладок.
  • Реализован перевод для всего приложения.
  • Реализован state приложения на Redux.
  • Реализовано отображение таблицы пользователей получаемых с сервера через Rest
  • Реализовано удаление пользователей по id
  • Реализовано добавление пользователей
  • Реализована отправка и получение сообщений через WebSocket

Думаю для начала этого более чем достаточно.

Заключение


Все ваши вопросы и пожелания оставляйте в комментариях или пишите мне на почту. Буду рад помочь.

Подробная информация по установке и запуску


ОС «Wondows 10»

Установка Java 1.8 подробная инструкция



Перед началом установки нажимаем сочетание клавиш Win+R и вводим cmd
вводим java -version и видим



Это значит что java на этом компьютере не установлена.

Если компьютер вывел версию java и эта версия не ниже 1.8, то переходите к пункту установки Gradle.

Скачиваем Java по ссылке

Нужно нажать чекбокс Accept License Agreement.

Нужная нам версия Windows x64

Скачивание java



Запускаем

Запуск java



Жмём все время Next и Ok в конце close.

После установки повторно вызываем командную строку Win+R и вводим cmd вводим java -version и видим уже версию установленной нами Java



Откройте свойства Вашего компьютера

Свойства компьютера



Дополнительные параметры — переменные среды

Переменные среды



Убедитесь что в системных переменных есть JAVA_HOME со значением C:\Program Files\Java\jdk1.8.0_181\

JAVA_HOME



И в переменной Path есть строка C:\Program Files\Java\jdk1.8.0_181\bin

Path



Переходим к следующему пункту

Установка Gradle подробная инструкция


Откройте консоль заново и введите gradle -version
Так как он у нас ещё не установлен, то мы увидим по это:



Качаем архив по ссылке

Распаковываем вот например в такую папку C:\gradle-4.10.1

Далее по аналогии с java открываем раздел с системными переменными и уже самостоятельно добавляем в него переменную GRADLE_HOME со значением C:\gradle-4.10.1\bin

GRADLE_HOME



И в переменную path тоже добавляем C:\gradle-4.10.1\bin можно даже рядом со строчкой C:\Program Files\Java\jdk1.8.0_181\bin, но это не обязательно.

gradle path



Обязательно перезапустите консоль Win+R cmd и введите gradle -version



Всё теперь и gradle установлен!

Node JS подробная инструкция



Скачиваем Node JS по ссылке

И устанавливаем
Установка Node js



Перезапускаем командную строку и вводим npm -version и мы обязательно увидим установленную версию



Запуск проекта


Отлично все подготовительные работы выполнены

Качаем проект в виде архива

Весит он всего 135 Kb

Git



И распаковываем в C:\react-start-master\

Папка проекта



Запускаем командную строку и переходим в C:\react-start-master\
Для тех кто не помнит чтобы подняться вверх по дереву папок в командной строке нужно ввести cd..

Так мы переходим до корня диска C:\>
Дальше вводим cd react-start-master и получаем путь C:\react-start-master>


вводим npm i


Сейчас производится скачивание зависимостей JS для нашего проекта



Варнинги допустимы (WARN — предупреждение)
В проекте по явится папка C:\react-start-master\node_modules все зависимости будут в ней.

Сразу после этого вводим в консоли gradle build



Будут скачаны все зависимости для Java в том числе и Spring.
В проекте появится папка C:\react-start-master\build



все обязательно соберётся и мы увидим сообщение об удачной сборке
BUILD SUCCESSFUL

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



Проект начнёт запускаться



В конце запуска в консоли будет примерно следующее



Всё проект запущен!



Не закрывайте консоль просто сверните.

Откройте браузер и введите localhost:8090/application/ или перейдите по этой ссылке

Вы увидите запущенный проект



Запуск только JS



Откройте ещё одну консоль.

Перейдите в папку проекта C:\react-start-master>

Выполните команду npm run start

Если все зависимости для JS уже были скачаны как объяснялось выше (команда npm i)

То браузер сам запустится по ссылке localhost:9090/
И все изменения в Js коде проекта будут там автоматически отображаться!


На этом всё, спасибо за внимание.

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


  1. Mountaineer
    11.09.2018 19:52

    Поправте версию BE фреймворка. На Github ето Spring Boot 2.0.4, а здесь Spring 4.0.1.


    1. impressionbit Автор
      11.09.2018 19:57

      Версия Spring считается по версии ядра — spring-batch-core, а не Spring Boot. Вот посмотрите уже пятая версия вышла


      1. aol-nnov
        12.09.2018 09:44

        spring-batch — абсолютно точно не ядро спринга. (зануда моде офф)


  1. yaushev_st
    11.09.2018 21:52

    Спасибо! Интересно почитать. Вы написали, что Spring используется с React в современных приложениях. А Spring+Angular используют сейчас?


    1. Mountaineer
      11.09.2018 21:59

      Да, большая часть проектов, что началась за последний год в нашей фирме — ето связка Spring+Angular.


  1. Mountaineer
    11.09.2018 21:56

    Первый раз токое слышу :) Версия Spring — ето версия spring-core, а не spring-batch-core(!) и тут вы правы, что Spring Boot 2.0.4 — ето Spring 5, а не 4. Об етом я выше и написал. Вот, посмотрите зависимости: mvnrepository.com/artifact/org.springframework.boot/spring-boot/2.0.4.RELEASE


  1. aol-nnov
    12.09.2018 09:45

    можно еще более быстрый старт: www.jhipster.tech :)


    1. zmejg
      12.09.2018 19:24

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


      1. aol-nnov
        12.09.2018 19:28

        Этот генератор не страшный. Его можно выкинуть на любом этапе. А фронт там разный генерится и реакт тоже заявлен. Я, правда, только бэк им генерил… )


        1. zmejg
          12.09.2018 19:51

          У нас был проект, написанный для Кубернетес-а аутсорсерами со всеми зависимостями — API Gateway, Jhipster registry, Hazelcast, etc. Вычищать JHipster пришлось почти месяц. Редуцировали как раз до модели Spring Boot + Vue + REST.


          1. aol-nnov
            12.09.2018 19:59

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


            1. zmejg
              12.09.2018 21:54

              Да, ваше внутреннее чувство вас не подводит — это действительно вундервафля. Ванильный спринг более предсказуем, понятен и лучше документирован. Последние версии JHipstera нам уже не поддались из-за ошибок YARNA при апгрейде. В ходе «даунгрейда» столкнулись с множеством связей во внешних модулях. Например при наличии актуатора и модуля LDAP автоматически включался мониторинг, который делал 5 AD логинов в секунду с pod-а. Отловили только tcpdump-ом. И в Hibernate он свои щупальца запускает, в общем везде, где нужна «оптимизация» с его точки зрения.
              Нужно наверное быть разработчиком JHipster что бы использовать его без сайд-эффектов.


  1. Grandapple
    12.09.2018 11:05

    Здравствуйте. После gradle runBoot у меня написал build successful и на localhost ничего не запустилось. Я в java знаю только синтаксис, что я делаю не так?


    1. impressionbit Автор
      12.09.2018 11:11

      build successful пишет после сборки gradle build. После запуска gradle bootRun должен начать запускаться Spring. И не закрывайте консоль.


      1. Grandapple
        12.09.2018 11:58

        Spring надо как то отдельно ставить. Потому что после этой команды build successful пишет.


        1. impressionbit Автор
          12.09.2018 21:58

          Сделал подробную инструкцию по запуску в конце статьи


          1. Grandapple
            13.09.2018 11:51

            Спасибо вам за подробную инструкцию. У меня не запускался потому что выходила ошибка.


  1. impressionbit Автор
    12.09.2018 13:01

    Я вечером сделаю подробную инструкцию.


  1. EvilBeaver
    13.09.2018 07:38

    Я так и не понял, где в серверной части формируется тот самый <html>, внутри которого будет тот самый <script>, который заставит браузер скачать результатБабеля.js?


    1. impressionbit Автор
      13.09.2018 08:20

      html и js и css формируются при сборке проекта Gradle. В файле build.gradle подключена зависимость com.moowork.gradle:gradle-node-plugin:1.2.0 которая умеет запускать webpack.
      В этом же файле есть строчки:

      task webpack(type: NpmTask) {
          args = ['run', 'build']
      }
      processResources.dependsOn 'webpack'

      Которые запускают webpack при сборке или старте проекта.
      Gradle этим и хорош что может делать такие вещи.

      Результат работы webpack складывается в папку build\resources\main\static\
      За запуск index.html отвечает src\main\java\ru\impressionbit\server\controller\IndexController.java который открывает этот html по любому url localhost:8090/**. Кок он его находит — это магия Spring. По какому хосту и порту запускать проект описано в файле src\main\resources\application.yml
      server:
      address: 0.0.0.0
      port: 8090



  1. Balynsky
    13.09.2018 08:04

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

    Конечно в первую очередь все зависит от решаемой задачи, возможно Вам действительно необходимо именно связка Spring + React/Vue/Angular. Но не стоит забывать, что если вы фронтенд девелопер и не имеете навыков бекенд(например, не работали с Node.js ), это никак не помешает Вам создать современное веб приложение используя Serverless решения от крупных вендоров. Например, рекомендую посмотреть на решение Firebase от Google, DynamoDB от Amazon.


    1. impressionbit Автор
      13.09.2018 08:28

      Я написал что это для: «Наиболее часто встречаемое в последнее время сочетание в корпоративной среде» и там серверная часть часто сделана всё же на Java. Поэтому я и написал эту статью.