vlambda博客
学习文章列表

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

Starting with Spring

在上一章中,我们设置了我们的开发环境。我们现在准备创建我们的第一个用 Kotlin 编写的 Spring 代码。为此,我们将熟悉 Spring 背景并解释 Spring 究竟可以做什么。当我们向您介绍Spring背景时,我们将启动该项目!做好准备!

在本章中,我们将学习以下内容:

  • What is Spring?
  • Spring's most important features
  • Kotlin support for Spring
  • Generating projects
  • Creating Spring project in IntelliJ

What is Spring?

Spring Framework 是由 Pivotal 公司开发的框架。该框架为现代企业应用程序提供结束用户的编程和配置模型。 Spring Framework 旨在让开发人员能够专注于应用程序级业务逻辑。如今,几乎所有现代企业应用程序都是使用 Spring Framework 开发的。这使其成为最广泛使用和流行的开发框架之一。

What features does it offer?

Spring Framework 带有许多每个开发人员都需要的现代特性。在这里,我们将重点介绍最重要的部分并对其说几句话。

Dependency injection

说到这里的 Spring Framework,首先要提到的是依赖注入。这是日常开发中所需的有用功能。每个开发人员都试图创建尽可能独立的类。开发人员试图创建一个类之间不知道彼此的系统,也就是说,它们是松散耦合的。依赖注入帮助开发人员将这些类连接在一起,同时保持它们的独立性。

我们如何实现这一目标?

在现代软件开发中,依赖注入是一种技术,其中对象 A 为另一个对象(对象 B)提供依赖关系。因此,依赖关系就是这个提供的对象。注入表示一种依赖关系,它为使用它的依赖对象提供了一种机制。一个好的用例示例可能是为单元测试提供依赖项。

提供体面的一种常见方法是创建一个 dependency 实例或使用 factory 对象为它们创建一个实例。通过使用依赖注入,一个实例被外部传递给 client。过关是别人的问题,不是你的!那么,这个其他人是谁?这个某人是以下之一:

  • An object higher in the dependency graph
  • A dependency injector (framework) that creates our dependency graph

我们现在将向您展示一些依赖注入的示例。我们将向您展示通过构造函数和使用工厂进行注入的示例。在这两种情况下,如果我们计划进行一些认真的测试,事情就会变得复杂。 Spring Framework 让我们轻松搞定!我们将在本书的后续章节中向您展示如何操作。

通过构造函数注入应该是这样的:

class MyExampleClass(val parameter: Any){ 
    private val dependency: Any  
    init { 
        dependency = parameter 
        // Do some work dependency related 
    } 
}  

Injection using the factory would look like this: class MyExampleClass2 { private val dependency = Factory().create() }
class Factory { fun create(): Any { // To some instantiation work return Any() } }

正如我们所提到的,使用依赖注入的一个很好的例子是我们计划进行测试时。依赖注入将使测试变得更加简单和容易!那么,这两个例子会产生什么问题呢?假设您尝试对 MyExampleClass2 进行测试。我们需要一个模拟(或存根)类版本。更大的复杂性是当我们不能轻易影响 Factory 类时!一种更简单的方法是使用构造函数注入。但是,这并不能解决问题。为什么?像这样构造的两个对象之间的循环依赖不再是一种选择。通过 setter 进行依赖注入并非如此。很快,您会注意到与这种设计方法相关的缺点将超过优点!

Inversion of Control (IoC)

负责实现松散耦合的最重要机制是 IoC。这是什么意思?依赖对象提供自己的依赖关系,而不是创建或寻找依赖对象。 IoC 是一种技术,其中对象耦合在运行时由执行对象耦合的汇编程序绑定。 IoC 和依赖注入模式都是负责从代码库中删除依赖项的机制。

让我们提供一些例子来说明这一点。想象一个有音乐播放器的应用程序,您需要为它提供音量控制机制。我们将从这个开始:

class VolumeControl  
class MusicPlayer {  
    val volumeControl = VolumeControl()  
} 

现在,让我们对其进行 IoC:

abstract class VolumeControlAbstract 
class MusicPlayerIOC( private val volumeControl: VolumeControlAbstract )

在第一个代码示例中,我们正在实例化类:

val volumeControl = VolumeControl() 

这意味着 MusicPlayer 类直接依赖于 VolumeControl 类!

第二个例子呢?在第二个示例中,我们正在创建一个抽象。我们在 MusicPlayer 构造函数签名中定义了 VolumeControl 依赖类。在这个例子中,我们没有在类本身中初始化依赖。我们调用依赖并将其传递给 MusicPlayer 类,如下所示:

// Init. dependency. 
val vc = VolumeControlImpl() 
 
// Pass dependency. 
val player = MusicPlayerIOC(vc) 

在此示例中,创建 MusicPlayer 类实例的客户端可以控制您将使用的 VolumeControl 实现。如您所见,我们将依赖项注入到 MusicPlayer 类签名中!

Aspect Oriented Programming (AOP)

Spring 有一个更重要的关键组件:AOP 框架。 AOP 提供了面向对象的编程 API。 AOP 提供了另一种思考程序结构的方式。这是什么意思?面向对象编程中模块化的关键单元是类。 Aspect 表示 AOP 中的模块化单元。依赖注入用于将应用程序对象彼此分离。另一方面,AOP 帮助您将横切关注点与其影响的对象分离。横切关注点可以描述为影响应用程序多个点的任何功能。 security 就是一个很好的例子。为什么?因为应用程序中的许多方法都可以应用安全规则。

多亏了 AOP,每个模块都提供了一个 AOP 实现。在接下来的章节中,我们将在实际代码示例中使用 AOP。

Container

Spring Framework 的一个好处是它创建和管理应用程序对象的生命周期和配置。为此,我们提供了 org.springframework.context.ApplicationContext 接口。该接口负责实例化、配置和组装 bean。使用 Spring Framework,我们有几个开箱即用的 ApplicationContext 实现。

MVC framework

Spring Framework 提供了一个 MVC Web 应用程序框架。这是什么意思? MVC 也可以通过接口进行配置,它支持多种视图技术。 Model-View-Controller (MVC) 让开发变得简单而干净。 MVC 是一种用于在计算机上实现用户界面的软件架构模式。顾名思义,该模式将我们的应用程序分为三个部分:模型、视图和控制器。

Model

这是模式的核心组成部分。模型根据独立于用户界面的问题域来表达应用程序的行为。模型直接影响数据、应用程序逻辑及其规则。

View

这是我们的应用程序正在处理的信息的输出表示。一个很好的例子可以是作为我们 API 调用的最终结果的 HTML 页面。在这里,我们不仅限于单一视图!使用 MVC,同一信息的多个视图是可能的。

Controller

这接受输入数据并将其转换为模型或视图的命令。如您所见,这是 Spring 框架中解耦的又一示例!

Transaction management

对于事务管理,Spring Framework 提供了一个通用的抽象层。事务管理不依赖于 J2EE 环境。与绑定到 JTA 的 EJB CMT 不同,Spring 框架的声明式事务管理适用于任何环境。 Spring Transaction Management 可以使用 JTA 事务,或者,如果需要,可以使用本地事务,使用:

  • JDBC
  • JPA
  • Hibernate
  • JDO

这一切只需调整配置文件!

Misc

在 Spring Framework 中,JDBC 抽象层提供了异常层次结构。这简化了错误处理策略。

Spring Framework 使您能够使用 POJO 开发企业级应用程序。有什么好处?使用 POJO 的好处是您不需要 EJB 容器产品,例如应用程序服务器。开发人员可以选择仅使用健壮的 servlet 容器,例如 Tomcat 或其他一些商业产品。 AWS 可能是一种可能的解决方案。

重要的是要注意 Spring Framework 是以模块化方式组织的,这意味着您必须只考虑您需要的包并忽略其他包。

借助 Spring Framework,您可以使用一些最流行的技术,例如 misc ORM 框架、日志框架、JEE、Quartz 和 JDK 计时器,以及许多其他视图技术。

测试呢?在 Spring Framework 中测试应用程序简单易行。与环境相关的代码被移到这个框架中。通过使用 Java bean 风格的 POJO,可以很容易地使用依赖注入来注入测试数据。

Spring Framework 提供了一个很棒的 AP​​I,可以将特定于技术的异常转换为最终用户的一致、未经检查的异常。

Spring 的 IoC 容器是轻量级的。如果我们将它与 EJB 容器进行比较,差异是显着的。多亏了这一点,我们将花费更少的内存和 CPU 功率。

这些是 Spring 最常被强调的一些特性。在本书的旅程中,你会遇到每一个。此外,您还会发现一些我们没有提到的其他框架,但总的来说,一旦您深入了解 Spring,您将在很长一段时间内不想尝试任何其他框架!可能永远不会!

How well is Kotlin supported?

从版本 5 开始,Spring Framework 正式支持 Kotlin。这是个好消息! Kotlin 的主要优势之一是它与用 Java 编写的库提供了非常好的互操作性。这还不是全部!现在我们可以在开发 Spring 应用程序时编写完全惯用的 Kotlin 代码。借助 Kotlin 和 Spring 5,我们将生产力和灵活性提升到了一个全新的水平!正因为如此,对 Kotlin 的支持终于在 Spring Framework 5 中引入了。

允许 Kotlin 和 Spring 协同工作的关键特性是 Kotlin 扩展。使用 Kotlin 扩展,可以扩展现有的 API。使用最新版本的 Spring Framework,一切都达到了一个新的维度!

需要注意的是,Kotlin 扩展是静态解析的。您必须导入它们。

让我们重点介绍 Kotlin 为 Spring 开发带来的一些好处:

  • Spring now takes advantage of Kotlin null-safety support.
  • Spring Framework 5 comes with a Kotlin-routing DSL.
  • Kotlin reified type parameters provide a workaround for JVM generics type erasure.
  • Kotlin-based Gradle build configuration.
  • Kotlin Script-based templates.
  • Combining Spring Framework with Kotlin allows us to write applications more efficiently, and more simply with expressive, short and readable code. Spring support for Kotlin is an important step in future development for all developers who use Spring!

Creating Spring project

是时候创建我们的第一个 Spring 代码并运行它了。当然,我们将使用 Kotlin 作为我们的主要开发语言。对于项目初始化,我们将使用 Spring Initializr。

What is Spring Initializr?

一句话,Spring Initializr 是一个基于 Web 的 Spring Framework 项目快速启动生成器。您可以通过以下 URL 在线访问它:

https://start.spring.io/

从它的 GitHub 存储库中,https://github.com/spring-io/initializr,因为它是一个开源项目。

Spring Initializr 提供 API 来生成快速启动 Spring Framework 项目。您可以使用托管在 spring.io 域上的默认实例,也可以克隆 GitHub 存储库并托管您自己的实例。

Initializr 提供了一个配置结构。使用配置结构,您可以定义您计划生成的项目的所有方面。例如:

  • A list of dependencies
  • A supported Java version
  • A supported Kotlin version
  • A boot version, and so on

Generating the project

在 Initializr 登录页面上打开浏览器:

https://start.spring.io/

如您所见,配置向导出现,如以下屏幕截图所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

我们将像这样配置我们的项目:

  • Generate a Gradle Project with Kotlin and Spring Boot 2.0.0 M4
  • Set the Group project to com.journaler
  • Set the Artifact project to api

以下屏幕截图显示了项目配置:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

我们将保持我们的第一个配置简单,稍后随着我们的进展和需要更多的依赖项进行扩展。按 生成项目 按钮。系统将要求您保存存档,如以下屏幕截图所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

api.zip 保存到您的首选目录。打开目录并展开它。让我们在下面的截图中看看它的内容:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

这显然是一个标准的 Gradle Kotlin 项目和一个标准的应用程序。我们将详细了解项目包含的最重要的文件。打开你的 build.gradle 配置。它的内容应该与此非常相似:

buildscript {     
    ext {           
        kotlinVersion = '1.1.4-3' 
        springBootVersion = '2.0.0.M4' 
} repositories { mavenCentral() maven { url "https://repo.spring.io/snapshot" } maven { url "https://repo.spring.io/milestone" } } dependencies { classpath("org.springframework.boot:spring-boot-gradle-
plugin:${springBootVersion}") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}") classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}") } } apply plugin: 'kotlin' apply plugin: 'kotlin-spring' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' group = 'com.journaler' version = '0.0.1-SNAPSHOT' ssourceCompatibility = 1.8 compileKotlin { kotlinOptions.jvmTarget = "1.8" } compileTestKotlin { kotlinOptions.jvmTarget = "1.8" } repositories { mavenCentral() maven { url "https://repo.spring.io/snapshot" } maven { url "https://repo.spring.io/milestone" } } dependencies { compile('org.springframework.boot:spring-boot-starter') compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}") compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}") testCompile('org.springframework.boot:spring-boot-starter-test') }

从上到下,脚本为我们的项目执行以下操作:

  • It defines Kotlin and Spring Boot versions
  • It defines build script repositories and classpaths
  • It applies Gradle plugins needed for the application to build and run
  • It defines project group as com.journaler
  • It defines version as 1.0 snapshot
  • It set Kotlin's JVM compatibility to 1.8
  • It defines project dependency repositories
  • It defines Kotlin and Spring dependencies

对我们来说下一个重要方面是 Git 忽略配置。打开 .gitignore 看看:

.gradle 
/build/ 
!gradle/wrapper/gradle-wrapper.jar 
### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### nbproject/private/ build/
nbbuild/ dist/ nbdist/ .nb-gradle/

.gitignore 配置将阻止我们对一些不需要的文件进行版本控制。接下来,打开 src/main.resources 目录下的 application.propeties 文件。该文件将为空。在这里,我们将为我们的应用程序定义特定于环境的参数。

最后,让我们探索代码!由于这是一个 Kotlin 应用程序,因此展开 src/main/kotlin 目录。继续浏览包结构 com/journaler/api 并打开 ApiApplication.kt

package com.journaler.api 
 
import org.springframework.boot.SpringApplication 
import org.springframework.boot.autoconfigure.SpringBootApplication 
@SpringBootApplication class ApiApplication
fun main(args: Array<String>) { SpringApplication.run(ApiApplication::class.java, *args) }

里面的代码不多。代码简单易懂。我们定义了名为 ApiApplication 的应用程序类和分配给 SpringBootApplication 的注解。主要应用程序方法运行 Spring 应用程序。而已!

在我们构建和运行代码之前,还有一件事需要检查。展开目录结构以包含 ApiApplicationTest.kt 测试:

package com.journaler.api 
import org.junit.Test import org.junit.runner.RunWith import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.junit4.SpringRunner
@RunWith(SpringRunner::class) @SpringBootTest class ApiApplicationTests { @Test fun contextLoads() { } }

实际上,这个测试并没有做任何事情。有关 Spring 测试和测试的更多信息可以在本书后面的章节中找到。现在,只需观察代码就足够了。

我们将构建并运行我们的应用程序。打开终端并导航到应用程序根目录。然后执行 Gradle clean 命令:

$ ./gradlew clean 

Gradle 开始下载所需的依赖项:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

如您所见,构建成功完成。要构建应用程序,请执行以下命令:

$ ./gradlew build 

该代码将给出以下输出:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

如您所见,Gradle 任务成功完成。要运行项目,请从终端导航到 build/libs 并执行以下命令:

$ java -jar ./api-0.0.1-SNAPSHOT.jar

代码输出如下所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

该程序将运行并显示一些消息。现在,我们不会详细介绍,因为在我们实际执行第一个 API 调用之前还有很长的路要走。

在本节结束时,在我们从 IntelliJ 从头开始​​创建项目之前,我们将导入该项目。启动 IntelliJ 并选择 Import Project。选择我们提取项目的根目录并单击OK确认。 导入项目 向导出现。 M确保选择了 从现有源创建项目 选项。然后,点击Next,如下图所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

如果您对 项目名称 和以下屏幕截图所示的路径感到满意,请单击下一步:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

点击全部标记,然后点击下一步,如下截图所示:< /跨度>

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

然后点击Finish,如下图所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

然后,选择 Import Gradle project 并在询问时添加 VCS root,如以下屏幕截图所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

请在询问时使用 Gradle 包装器。 点击OK确认导入,如下图所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

一段时间后,项目被导入。 您会注意到 ApiApplication 作为您可以运行的配置可用,如下图所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

如果您的 IDE 没有自动检测到配置,请通过展开项目并找到 ApiApplication.kt 来运行应用程序,然后右键单击它并选择 Run ApiApplication。 kt输出与我们上次执行 JAR 时相同。

将以下行添加到您的 .gitignore 文件中,这样 Gradle 包装器就不会被版本控制。 Initializr 不会生成这些行:

... 
gradlew 
gradlew.bat 
gradle/* 

Creating Spring project with IntelliJ

为了结束本章,我们将演示如何从 IntelliJ Idea 初始化项目。我们将得到与 Initializr 几乎相同的结果。启动 IntelliJ 并选择 创建新项目 选项。出现 New Project 对话框,如下图所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

设置您的项目选项,然后选择 Spring 框架组件,如以下屏幕截图所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

此外,选择 Kotlin/JVM 作为我们将使用的语言,如以下屏幕截图所示:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

按照向导进行操作,直到项目准备就绪。系统将要求您启用注释,如以下屏幕截图所示。请这样做!

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

在下面的屏幕截图中,您可能已经注意到没有 Spring 5 可供选择,因此我们将坚持 Initializr 项目初始化,因为它支持我们想要的一切:

读书笔记《building-applications-with-spring-5-and-kotlin》从春天开始

Summary

在本章中,我们向您介绍了 Spring Framework 及其特性。我们启动了我们的第一个项目并成功地进行了构建和执行。在下一章,我们将继续我们的旅程,做一些更具体的工作。我们将使用控制器、数据类和服务组件创建我们的第一个 RESTful 服务。