我认为对于开发人员而言,选择正确的构建工具是一个非常重要的选择。 多年来,我一直坚持使用Apache Maven ,老实说,它做得很好,即使到了今天,它还是一个很好的使用工具。 但是我总是觉得可以做得更好……然后Gradle出现了……
尽管我花了很多时间习惯了Gradle的处理方式,但我最终还是放弃了,转而回到Apache Maven 。 原因–我对此不太满意,主要是因为Groovy DSL。 无论如何,我认为Gradle是一款强大,强大且可扩展的构建工具,能够执行构建过程所需的任何任务。
但是随着与Scala的 互动越来越多,我很快发现了sbt 。 尽管sbt是“ 简单构建工具 ”的首字母缩写,但我的第一印象却恰恰相反:我发现它很复杂且难以理解。 出于某些原因,我还是喜欢它,并花了更多时间阅读文档 ( 文档越来越好),进行了许多实验,最终我还是选择了。 在这篇文章中,我想展示sbt可以做的一些很棒的事情, 它们可以简化 Java开发人员的生活(对Scala的一些了解会很方便,但这不是必需的)。
在继续实际示例之前, 请先了解有关sbt的一些事实。 它使用Scala作为构建场景的语言,并且需要可以从此处下载的启动器(我们将使用的版本是0.13.1 )。 有多种方法可以描述sbt中的build ,本文演示的方法是将Build.scala与单个项目一起使用。
我们的示例是一个简单的Spring控制台应用程序,其中包含几个JUnit测试用例:足以了解如何构建具有外部依赖关系的构建并运行测试。 应用程序仅包含两个类:
package com.example;
import org.springframework.stereotype.Service;
@Service
public class SimpleService {
public String getResult() {
return "Result";
}
}
和
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应该位于项目子文件夹中。 另外,应该存在带有所需sbt版本的build.properties文件和带有外部插件的plugins.sbt (我们将使用sbteclipse插件生成Eclipse项目文件)。 我们将从仅包含一行的build.properties开始:
sbt.version=0.13.1
并继续执行plugins.sbt ,在我们的例子中也只是一行:
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.4.0")
最后,让我们从构建的核心开始: Build.scala 。 其中包括两个部分:构建中所有项目的通用设置(对多项目构建有用,但现在只有一个),这是此部分的片段:
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)
)
}
上面的构建看起来很干净并且可以理解: 解析器是Apache Maven存储库的直接类比, EclipseKeys.executionEnvironment是针对生成的Eclipse项目的执行环境(Java SE 7)的定制。 所有这些键都有很好的记录 。
第二部分要小得多,它根据依赖项和主类定义了我们的主项目:
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),并且此设置允许添加默认的import语句,因此我们可以立即使用我们的类。 对junit-interface的依赖使sbt可以运行JUnit测试用例,这是我们要做的第一件事:添加一些测试。 在创建实际测试之前,我们将启动sbt并要求它在每次代码更改时运行测试用例,就像这样:
sbt ~test
在sbt运行时,我们将添加一个测试用例:
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-interface的下一发行版中提供,我们还不能使用@RunWith和@ContextConfiguration批注来运行Spring测试用例。
对于TDD从业者来说,这是一个了不起的功能。 我们要看的下一个很棒的功能是Scala控制台(RELP),它具有在不实际运行应用程序的情况下进行播放的能力。 可以通过键入以下内容来调用它:
sbt console
并在终端中观察类似的内容(如我们所见,会自动包含来自initialCommands的导入):
此时,游乐场已经建立,我们可以做很多非常有趣的事情,例如:创建上下文,获取bean并在其上调用任何方法:
sbt关心classpath,因此可以使用所有类和外部依赖项。 我发现这种方法比使用调试器或其他技术更快地发现事物。
目前,在Eclipse中没有对sbt的良好支持,但是通过使用我们之前接触过的sbteclipse插件来生成Eclipse项目文件非常容易:
sbt eclipse
太棒了! 更不用说这里列出的其他出色的插件,以及使用externalPom()导入Apache Maven POM文件的功能,这实际上简化了迁移。 作为我的结论,如果您正在寻找更好,现代,可扩展的项目构建工具,请查看sbt 。 这是一个很棒的软件,建立在精巧的简明语言之上。
- 完整项目可在GitHub上获得 。