Я думаю, что для разработчиков выбор правильного инструмента сборки — очень важный выбор. В течение многих лет я придерживался Apache Maven и, честно говоря, он выполняет свою работу достаточно хорошо, даже сейчас это хороший инструмент для использования. Но я всегда чувствую, что это можно сделать намного лучше … и тогда появился Градл …
Несмотря на то, что я потратил много часов на то, чтобы привыкнуть к Gradle , я наконец сдался и вернулся к Apache Maven . Причина — я не чувствовал себя комфортно с этим, в основном из-за Groovy DSL. В любом случае, я думаю, что Gradle — это отличный, мощный и расширяемый инструмент для сборки, который способен выполнять любые задачи, которые нужны вашему процессу сборки.
Но все больше и больше занимаясь Scala , я быстро обнаружил, что кто-то другой . Хотя sbt является аббревиатурой от « простого инструмента сборки », мое первое впечатление было совершенно противоположным: я нашел его сложным и трудным для понимания. По некоторым причинам, мне все же понравилось, и, проводя больше времени за чтением документации (которая становится все лучше и лучше), во многих экспериментах, я бы, наконец, сказал, что выбор сделан. В этой статье я хотел бы показать пару замечательных вещей, которые sbt может сделать, чтобы облегчить жизнь Java-разработчику (некоторые знания Scala были бы очень полезны, но это не обязательно).
Прежде чем перейти к реальному примеру, пара фактов о sbt . Он использует Scala в качестве языка для сценария сборки и требует запуска, который можно скачать отсюда (версия, которую мы будем использовать, — 0.13.1 ). Есть несколько способов описать сборку в sbt , один из которых демонстрирует этот пост, это использование Build.scala с одним проектом.
Наш пример — это простое консольное приложение Spring с парой тестовых примеров JUnit : этого достаточно, чтобы увидеть, как строится сборка с внешними зависимостями и выполняются тесты. Приложение содержит только два класса:
01
02
03
04
05
06
07
08
09
10
|
package com.example; import org.springframework.stereotype.Service; @Service public class SimpleService { public String getResult() { return "Result" ; } } |
и
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
package com.example; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.GenericApplicationContext; public class Starter { @Configuration @ComponentScan ( basePackageClasses = SimpleService. class ) public static class AppConfig { } public static void main( String[] args ) { try ( GenericApplicationContext context = new AnnotationConfigApplicationContext( AppConfig. class ) ) { final SimpleService service = context.getBean( SimpleService. class ); System.out.println( service.getResult() ); } } } |
Теперь давайте посмотрим, как выглядит сборка sbt . По договоренности Build.scala должен находиться в подпапке проекта . Кроме того, должен присутствовать файл build.properties с желаемой версией sbt и plugins.sbt с внешними плагинами (мы будем использовать плагин sbteclipse для генерации файлов проекта Eclipse ). Начнем с build.properties, который содержит только одну строку:
1
|
sbt.version= 0.13 . 1 |
и продолжим с plugins.sbt , который в нашем случае тоже всего одна строка:
1
|
addSbtPlugin( "com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.4.0" ) |
Наконец, давайте начнем с сердца нашей сборки: Build.scala . В нем будет две части: общие настройки для всех проектов в нашей сборке (полезно для многопроектных сборок, но сейчас у нас есть только одна), и вот фрагмент этой части:
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
|
import sbt._ import Keys._ import com.typesafe.sbteclipse.core.EclipsePlugin._ object ProjectBuild extends Build { override val settings = super .settings ++ Seq( organization := "com.example" , name := "sbt-java" , version := "0.0.1-SNAPSHOT" , scalaVersion := "2.10.3" , scalacOptions ++= Seq( "-encoding" , "UTF-8" , "-target:jvm-1.7" ), javacOptions ++= Seq( "-encoding" , "UTF-8" , "-source" , "1.7" , "-target" , "1.7" ), outputStrategy := Some( StdoutOutput ), compileOrder := CompileOrder.JavaThenScala, resolvers ++= Seq( Resolver.mavenLocal, Resolver.sonatypeRepo( "releases" ), Resolver.typesafeRepo( "releases" ) ), crossPaths := false , fork in run := true , connectInput in run := true , EclipseKeys.executionEnvironment := Some(EclipseExecutionEnvironment.JavaSE17) ) } |
Вышеприведенная сборка выглядит достаточно чистой и понятной: resolvers — это прямая аналогия репозиториев Apache Maven , EclipseKeys.executionEnvironment — это настройка среды выполнения (Java SE 7) для сгенерированного проекта Eclipse . Все эти ключи очень хорошо документированы .
Вторая часть намного меньше и определяет наш основной проект с точки зрения зависимостей и основного класса:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
lazy val main = Project( id = "sbt-java" , base = file( "." ), settings = Project.defaultSettings ++ Seq( mainClass := Some( "com.example.Starter" ), initialCommands in console += "" " import com.example._ import com.example.Starter._ import org.springframework.context.annotation._ "" ", libraryDependencies ++= Seq( "org.springframework" % "spring-context" % "4.0.0.RELEASE" , "org.springframework" % "spring-beans" % "4.0.0.RELEASE" , "org.springframework" % "spring-test" % "4.0.0.RELEASE" % "test" , "com.novocode" % "junit-interface" % "0.10" % "test" , "junit" % "junit" % "4.11" % "test" ) ) ) |
InitialCommands требует небольшого пояснения: sbt может запускать консоль Scala (REPL), и этот параметр позволяет добавлять стандартные операторы импорта, чтобы мы могли немедленно использовать наши классы. Зависимость от junit-интерфейса позволяет sbt запускать тестовые случаи JUnit, и это первое, что мы сделаем: добавим несколько тестов. Перед созданием реальных тестов мы запустим sbt и попросим его запускать тестовые примеры при каждом изменении кода, вот так:
1
|
sbt ~test |
Пока 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
33
|
package com.example; import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertThat; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.GenericApplicationContext; import com.example.Starter.AppConfig; public class SimpleServiceTestCase { private GenericApplicationContext context; private SimpleService service; @Before public void setUp() { context = new AnnotationConfigApplicationContext( AppConfig. class ); service = context.getBean( SimpleService. class ); } @After public void tearDown() { context.close(); } @Test public void testSampleTest() { assertThat( service.getResult(), equalTo( "Result" ) ); } } |
В консоли мы должны увидеть, что sbt автоматически выбрал изменение и запустил все тестовые случаи. К сожалению, из-за этой проблемы, которая уже исправлена и должна быть доступна в следующей версии junit-интерфейса , мы не можем пока использовать аннотации @RunWith и @ContextConfiguration для запуска тестовых случаев Spring .
Для практиков TDD это потрясающая возможность. Следующая потрясающая функция, которую мы собираемся рассмотреть, это консоль Scala (RELP), которая дает возможность играть с приложением, фактически не запуская его. Это может быть вызвано, набрав:
1
|
sbt console |
и наблюдаем что-то подобное в терминале (как мы видим, импорт из initialCommands автоматически включается):
На данный момент игровая площадка создана, и мы можем сделать много очень интересных вещей, например: создать контекст, получить bean-компоненты и вызвать любые методы для них:
sbt заботится о classpath, поэтому все ваши классы и внешние зависимости доступны для использования. Я нашел этот способ обнаружить вещи намного быстрее, чем с помощью отладчика или других методов.
На данный момент в Eclipse нет хорошей поддержки sbt, но очень просто сгенерировать файлы проекта Eclipse с помощью плагина sbteclipse, к которому мы обращались ранее:
1
|
sbt eclipse |
Потрясающие! Не говоря уже о других замечательных плагинах, которые любезно перечислены здесь, и о возможности импортировать POM-файлы Apache Maven с помощью externalPom (), который действительно упрощает миграцию. В заключение, с моей стороны, если вы ищете лучший, современный, расширяемый инструмент для сборки вашего проекта, пожалуйста, посмотрите на sbt . Это отличный кусок программного обеспечения, созданного на основе удивительного, продуманного языка.
- Полный проект доступен на GitHub .