Статьи

Разработка современных приложений с Scala: сборка с SBT

Эта статья является частью нашего академического курса под названием « Разработка современных приложений с помощью Scala» .

В этом курсе мы предоставляем среду и набор инструментов, чтобы вы могли разрабатывать современные приложения Scala. Мы охватываем широкий спектр тем: от сборки SBT и реактивных приложений до тестирования и доступа к базе данных. С нашими простыми учебными пособиями вы сможете запустить и запустить собственные проекты за минимальное время. Проверьте это здесь !

1. Введение

Для многих опытных разработчиков Java язык программирования Scala не новичок. Он существует уже довольно давно (официально, с момента первого публичного релиза в 2004 году) и в последние годы набирает обороты.

Есть много причин, по которым можно выбрать Scala вместо Java, Clojure, Groovy, Kotlin, Ceylon,… и мы не будем обсуждать это в этом руководстве. Однако о чем мы будем говорить, так это об экосистеме инструментов, сред и библиотек, которые сообщество Scala создавало на протяжении многих лет, чтобы предоставить разработчикам собственный опыт Scala.

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

2. Инструменты сборки: прочная основа каждого проекта

Одна из первых вещей, с которой сталкивается каждый программный проект на довольно ранней стадии, — это то, как он будет построен и управлять своими зависимостями. Выбор инструментов варьируется от пользовательских сценариев оболочки, make , Apache Ant на основе XML, Apache Maven , Apache Ivy до действительно сложных, таких как, например, Gradle и Apache Buildr .

3. SBT: (не так) Simple Build Tool

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

В текущей версии 0.13.11 SBT все еще находится на пути к выпуску 1.0 (который, как мы надеемся, скоро будет). Самый простой способ установить SBT — это загрузить его как независимый от платформы архив , распаковать его в какое-то место и включить это место в переменную среды PATH операционной системы.

Обратите внимание, что соответствующая версия Java (по крайней мере, Java 7, но Java 8 является рекомендуемым дистрибутивом в наши дни) должна быть установлена ​​раньше.

Хотя S в SBT остается «простым» , для многих разработчиков это не так. Это может быть связано с тем, как описываются сборки проекта (по существу, пары ключ / значение), или может быть из-за того, что существует множество способов создания определений сборки (с использованием файлов .sbt и .scala ). Тем не менее, в этом разделе мы собираемся демистифицировать некоторые концепции SBT , надеясь сделать этот мощный инструмент действительно простым для понимания и использования.

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

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

Большинство инструментов сборки пытаются навязать некоторые соглашения относительно того, как должен выглядеть макет проекта. SBT не вводит еще один, но вместо этого следует тем же соглашениям макета проекта, что и Apache Maven , например:

  <Имя-проекта>
     |  - проект ;
     |  - источник
     |  - главный
     |  |  - Скала
     |  |  - Ресурсы
     |  - тест
           |  - Скала
           |  - Ресурсы

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

  • свойства с желаемой версией SBT , например:
    1
    sbt.version=0.13.11
  • sbt со списком дополнительных плагинов SBT , например:
    1
    addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")

Хотя plugins.sbt по-прежнему широко используется, более новые версии SBT склоняются к использованию немного другого подхода, как описано в разделе « Использование плагинов » документации SBT .

5. Построить определения

Как мы уже кратко затронули, SBT поддерживает несколько способов создания определений сборки: использование файлов .sbt и файлов .scala . Кроме того, что касается файлов .sbt , существуют так называемые « голые» файлы .sbt, которые больше не должны использоваться, но мы собираемся обсудить их немного, поскольку мы можем часто встречаться с ними в дикой природе.

По сути, независимо от того, какой подход вы собираетесь использовать, в конце результат всегда одинаков: предоставьте описание каждого проекта в виде набора пар ключ / значение, которые часто называют настройками . Наряду с настройками , SBT имеет очень важные понятия о задачах (например, компиляция , пакетирование , запуск ) и конфигурациях (например, компиляция , время выполнения , тестирование ). С учетом вышесказанного, в зависимости от задачи и / или конфигурации, каждый параметр может иметь различное значение, поэтому говорят, что параметры (или, точнее, ключи настроек ) связаны с некоторой областью действия (или просто областью действия). Под капотом это, конечно, сложнее, чем кажется, но размышление об определении сборки таким образом является хорошей отправной точкой. Для любопытных разделы определения .sbt и разделы Области онлайн-документации SBT намного глубже в деталях.

В следующих нескольких разделах мы рассмотрим различные определения сборки SBT, используя только простые одномодовые проекты, а также более сложные многомодульные проекты. Будут обсуждены оба стиля определения сборки с использованием файлов .sbt или .scala , чтобы мы могли выбрать тот, который вам по душе.

6. Одномодульные проекты

Проекты, состоящие из одного модуля, чрезвычайно просты в сборке, используя всего несколько строк в файле определения сборки SBT build.sbt , например:

01
02
03
04
05
06
07
08
09
10
11
lazy val main = (project in file("."))
  .settings(
    name := "single-module-sbt",
    version := "0.0.1-SNAPSHOT",
    scalaVersion := "2.11.8",
    libraryDependencies ++= Seq(
      "ch.qos.logback" % "logback-classic" % "1.1.2"
    ),
    resolvers += "Typesafe" at "https://repo.typesafe.com/typesafe/releases/",
    mainClass in (Compile, run) := Some("com.javacodegeeks.single.App")
  )

Это в основном полное определение сборки! В двух словах, мы просто назвали текущий каталог проектом:

1
lazy val main = (project in file("."))

И определил пару именованных пар (пары ключ / значение или настройки), таких как имя , версия , scalaVersion, libraryDependencies, resolvers . В этом отношении mainClass стоит отдельно, так как он дополнительно включает области конфигурации ( Compile ) и задачи ( run ).

На данный момент это рекомендуемый способ использования SBT с проектами Scala (и проектами, написанными на других языках, которые поддерживает SBT ). Использование стилей .scala и bare .sbt считается устаревшим в пользу этого. Тем не менее, давайте рассмотрим примеры обоих, начиная с .scala , поскольку есть вероятность, что один или несколько ваших текущих проектов уже могут их использовать.

Традиционный способ организации сборок на основе .scala — это наличие файла Build.scala в папке проекта в качестве точки входа. Например, одно и то же определение сборки будет выглядеть немного по-другому, но все же достаточно близко:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
import sbt._
import Keys._
 
object ProjectBuild extends Build {
  override val settings = super.settings ++ Seq(
    name := "single-module-sbt",
    version := "0.0.1-SNAPSHOT",
    scalaVersion := "2.11.8",
    libraryDependencies ++= Seq(
      "ch.qos.logback" % "logback-classic" % "1.1.2"
    ),
    resolvers += "Typesafe" at "https://repo.typesafe.com/typesafe/releases/"
  )
 
  lazy val main = Project(
    id = "single-module-scala",
    base = file("."),
    settings = Project.defaultSettings ++ Seq(
      mainClass in (Compile, run) := Some("com.javacodegeeks.single.App")
    )
  )
}

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
name := "bare-module-sbt"
 
version := "0.0.1-SNAPSHOT"
 
scalaVersion := "2.11.8"
 
libraryDependencies ++= Seq(
  "ch.qos.logback" % "logback-classic" % "1.1.2"
)
 
 
mainClass in (Compile, run) := Some("com.javacodegeeks.bare.App")

Это все тот же build.sbt , но определенный с использованием подхода, основанного исключительно на настройках (обратите внимание, новые строки между определениями параметров являются обязательными). Этот стиль определения сборки также описан в официальной документации .

Как уже упоминалось, определения сборок .scala и bare .sbt больше не рекомендуются для использования, поэтому мы не будем возвращаться к ним в руководстве.

7. Мультимодульные проекты

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

SBT поддерживает многомодульный проект очень естественно, поэтому нетрудно добавлять новые модули или поддерживать довольно сложные определения сборки. Давайте создадим пример многомодульного проекта, который состоит из 3 модулей: core-module , api-module и impl-module .

  <Имя-проекта>
       |  - проект
       |  - основной модуль
       |  |  - источник
       |  |  - главный
       |  ...
       |  - API-модуль
       |  |  - источник
       |  |  - главный
       |  ...
       |  - импл-модуль
             |  - источник
                   |  - главный
                          ...

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
lazy val defaults = Seq(
  version := "0.0.1-SNAPSHOT",
  scalaVersion := "2.11.8",
  resolvers += "Typesafe" at "https://repo.typesafe.com/typesafe/releases/"
)
 
lazy val core = (project in file("core-module"))
  .settings(defaults: _*)
  .settings(
    name := "core-module"
  )
 
lazy val api = (project in file("api-module"))
  .settings(defaults: _*)
  .settings(
    name := "api-module"
  )
 
lazy val impl = (project in file("impl-module"))
  .settings(defaults: _*)
  .settings(
    name := "impl-module",
    libraryDependencies ++= Seq(
      "ch.qos.logback" % "logback-classic" % "1.1.2"
    ),
    mainClass in (Compile, run) := Some("com.javacodegeeks.multi.App")
  )
  .dependsOn(core)
  .dependsOn(api)
 
lazy val main = (project in file("."))
  .aggregate(core, api, impl)

По сути, многомодульное (или мультипроектное) определение сборки — это просто набор отдельных проектов, совместно использующих некоторые общие настройки (например, значения по умолчанию для lazy val ) и объявляющих зависимости друг от друга, с помощью depenOn :

1
2
3
4
lazy val impl = (project in file("impl-module"))
  
  .dependsOn(core)
  .dependsOn(api)

SBT позаботится обо всем остальном (например, построение графа зависимостей и определение правильной последовательности сборки). Для любопытных это вполне объяснимо в разделе официальной документации .

8. Миграция из Maven

Возможно, в наши дни Apache Maven по-прежнему является одним из самых популярных инструментов сборки в экосистеме Java. Скорее всего, SBT предоставляет простой путь миграции из Apache Maven с использованием конфигурации externalPom (при условии, что вместе с build.sbt есть файл pom.xml ), например:

1
2
3
4
5
6
7
8
9
lazy val main = (project in file("."))
  .settings(
    organization := "com.javacodegeeks",
    name := "pom-module-sbt",
    version := "0.0.1-SNAPSHOT",
    scalaVersion := "2.11.8",
    mainClass in (Compile, run) := Some("com.javacodegeeks.maven.App"),
    externalPom()
  )

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

9. Интерактивность в сердце

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

1
2
$ sbt
>

SBT позволяет вам взаимодействовать с вашими определениями сборки в реальном времени, проверяя различные настройки и вызывая произвольные задачи. Не стоит забывать об интеграции с консолью Scala и продуманной консольной командой sbt (или просто консоль с интерактивной оболочкой).

1
2
3
4
5
6
7
$ sbt console
 
[info] Starting scala interpreter...
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_77).
Type in expressions for evaluation. Or try :help.
 
scala>

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

10. Интеграция с IDE

И последнее, но не менее важное, есть еще одна очень важная тема, касающаяся того, как SBT интегрируется с популярными Java IDE. Удивительно, насколько важны такие интеграции для разработчиков, чтобы быстро начать работу над своими проектами.

SBT очень расширяемый благодаря собственному механизму плагинов. Чаще всего интеграция с большинством популярных Java IDE осуществляется через специальные плагины, например:

  • sbteclipse — плагин для SBT для создания определений проекта Eclipse
  • nbsbt — это плагин для SBT для создания определения проекта Netbeans
  • ensime-sbt — это плагин для SBT для создания определения проекта Ensime.

Добавление плагинов для построения определения так же просто, как добавление пары строк в файл plugin.sbt в подпапке проекта , например, в случае sbteclipse :

1
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")

Следовательно, функциональность плагина доступна через соответствующую задачу (или задачи) из командной строки (или из интерактивной оболочки), например, для sbteclipse это выглядит так:

1
$ sbt "eclipse with-source=true with-javadoc=true"

Стоит отметить, что некоторые IDE, такие как JetBrains IntelliJ IDEA , обеспечивают превосходный SBT через собственную экосистему плагинов.

11. Выводы

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

12. Что дальше

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