Spring Fu 的演进

工程 | Sébastien Deleuze | 2018年10月02日 | ...

借着在 SpringOne platform(在此我做了关于Spring Fu的第一个演讲)和 Kotlinconf 之间的短暂间隙,我想对该项目的演进做一个概述,总结当前状态并分享下一步可能的计划。

六月初,我宣布了一个名为 Spring Fu 的新实验性项目,旨在通过Kotlin DSL和函数式配置来试验一种配置Spring应用程序的新型API。

我必须承认,我没有预料到随之而来的巨大反馈浪潮,我想感谢Spring社区如此热烈的欢迎。此后,我继续致力于该项目,希望能将这个基于原生Spring Framework API的第一个POC转化为Spring新函数式功能的一个孵化器。

Kofu 配置

Kotlin DSL 现在基于Spring Boot基础设施,并称为Kofu(取自Kotlin和functional)。它允许使用Kotlin DSL和lambda而不是注解来配置Spring Boot应用程序,并具有以下特点:

  • 通过Kotlin DSL进行显式配置
  • 基于函数式使用的Spring Boot基础设施
  • 不依赖于类路径检测来启用功能
  • 声明式和命令式兼备
  • 更快的启动和更低的内存消耗
  • 最少地使用反射和注解
  • 纯粹的 Lambda,无 CGLIB 代理

使用Kofu配置的典型Spring Boot应用程序如下所示:

val app = application {
  import(beans)
  listener<ApplicationReadyEvent> {
    ref<UserRepository>().init()
  }
  properties<SampleProperties>("sample")
  server {
    port = if (profiles.contains("test")) 8181 else 8080
    mustache()
    codecs {
      string()
      jackson {
        indentOutput = true
      }
    }
    import(::routes)
  }
  mongodb {
    embedded()
  }
}

val beans = beans {
  bean<UserRepository>()
  bean<UserHandler>()
}

fun routes(userHandler: UserHandler) = router {
  GET("/", userHandler::listView)
  GET("/api/user", userHandler::listApi)
  GET("/conf", userHandler::conf)
}

fun main() = app.run()

为了使用它,您“只需”为Spring Boot 2.1应用程序添加一个org.springframework.fu:spring-boot-kofu依赖项。如当前版本号0.0.2所示,请注意,目前API尚不稳定,不适用于生产环境,且其范围仅限于Spring Boot支持的部分功能。

但请随时尝试,提供反馈,并尝试这种新的Spring Boot应用程序配置方式,它具有函数式特性,可以通过IDE自动补全进行发现,并且有文档说明

Kofu并不比自动配置更好或更差,它只是不同。我相信这可能是Spring Boot触达那些偏好更显式配置模型以及来自Kotlin、Go、Node或Ruby等其他背景的开发者的途径。

Jafu 配置

最初仅限于Kotlin,我收到的一个主要反馈是来自对这种显式DSL方法也感兴趣的Java开发人员,所以我研究了一个Java等价物,并最终得到了这个Jafu(取自Java和functional)DSL。

public class JafuApplication {

  public static SpringApplication app = application(app -> {
    app.beans(beans -> {
      beans.bean(SampleService.class);
      beans.bean(SampleHandler.class);
    });
    app.server(server -> server.router(router -> {
      SampleHandler sampleHandler = app.ref(SampleHandler.class);
      router.GET("/", sampleHandler::hello);
      router.resources("/**", new ClassPathResource("static/"));
    }));
  });

  public static void main (String[] args) {
    app.run(args);
  }
}

目前这只是一个POC,但我计划很快达到与Kofu的功能对等,并并行开发这两个DSL。缺乏类型安全构建器reified类型参数扩展机制将使Jafu比Kofu更冗长且可扩展性更差,但尽管有这些限制,我认为Jafu仍然非常不错且可用。

对于仅对使用函数式Bean注册基础设施带来的性能提升感兴趣的用户,值得注意的是,Dave Syer目前正在试验一些解决方案,这些方案可以使我们能够利用函数式Bean注册的效率,同时仍使用基于注解的常规Spring Boot应用程序。

将Spring应用程序作为原生可执行文件运行

GraalVM是Oracle开发的一个新的虚拟机,它除了其他功能外,还可以通过Substratevm将JVM字节码编译为原生可执行文件。

Spring Framework 5.1为GraalVM原生镜像提供了一些初步支持,但我们仍处于故事的开端。GraalVM团队需要修复Dave提出的各种问题,才能让一切正常工作,而生态系统也需要适应这个拥有不同约束和特性的新平台。

但GraalVM团队正在倾听我们的反馈,Spring应用程序的支持在最近几个月取得了显著进展。现在已经可以编译一个基本的Spring Boot响应式应用程序(使用Kofu配置)作为原生可执行文件,它几乎可以瞬间启动!

协程支持

如前所述,Spring Fu的主要目标是孵化将集成到当前顶级项目(如Spring Framework、Spring Data和Spring Boot)中的功能。

Spring Fu目前正在为Spring WebFlux和Spring Data孵化协程支持,以便能够以更命令式的方式利用Spring响应式堆栈。这主要面向那些希望利用此类堆栈的可伸缩性,而又不需要响应式API全部强大功能的开发者。

class UserRepository(private val mongo: CoroutinesMongoTemplate) {
	suspend fun count(): Long = mongo.count<User>()
	suspend fun findAll(): List<User> = mongo.findAll<User>()
	suspend fun findOne(id: String): User? = mongo.findById<User>(id)
	suspend fun deleteAll() = mongo.remove<User>()
	suspend fun save(user: User): User? = mongo.save(user)
}

请注意,虽然协程在Kotlin 1.3中被认为是最终版本,但在kotlinx-coroutines中仍然缺少一个重要的部分,因为它没有提供任何处理冷流的类型。我们需要这个缺失的抽象才能用协程API暴露我们的响应式基础,更多详情请参阅kotlinx.coroutines#254

请注意,我们应该能够在协程和Reactor类型之间传递上下文,以便支持诸如响应式安全和事务等非常强大的用例。

通过其出色的spring-kotlin-coroutine项目贡献了原始Spring协程支持的Konrad Kaminski将很快加入Spring Fu,与我一起致力于此功能。

结论

我刚刚发布了Spring Fu 0.0.2,它提供了改进的Kofu DSL并引入了函数参数的自动装配。请随时尝试并提供反馈。

即将发布的Spring Fu 0.0.3将提供Kofu和Jafu配置之间的功能对等。

我们已经有10多位社区贡献者向Spring Fu提交了Pull Request,所以如果您有想法,也许下一个就是您 ;-)

期待在即将举行的Spring Fu讲座中与您见面,地点包括JFuture(明斯克)、Spring Fest(东京)和 Devoxx(安特卫普)。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有