vlambda博客
学习文章列表

读书笔记《building-applications-with-spring-5-and-kotlin》开发实践

Development Practices

在日常开发中,你会接触到不同的实践。其中一些可能不是那么好,因为它们在性能方面不安全或成本高昂。开发人员经常争论这些做法的有效性。在本章中,我们将重点介绍一些最常见和最重要的做法,并对它们进行挑战。

在本章中,我们将了解以下内容:

  • Different development practices
  • Some bad development practices
  • Some good development practices

Challenging development practices

我们可以说 Spring Framework 的开发很简单,而且几乎没有出错的机会。这大多是真的。但是,我们将挑战一些最常用的 Spring Framework 特性并讨论它们的一些缺点。

Reviewing dependency injection

如您所知,@Autowired 是让我们的生活更轻松的神奇词汇。我们为该字段注入适当的实例并使其在我们的代码中可用。现场注入很容易做到并且很受欢迎。我们拥有的代码清晰易读和维护。但是有一些你应该知道的!

让我们考虑通过构造函数注入我们的依赖项。如果我们有一个依赖项,我们将注入具有例如一个参数的构造函数。随着依赖项数量的增加,构造函数参数的数量也会增加。幸运的是,在 Kotlin 中这不是问题,我们有单行构造函数。 在某些时候,您会意识到通过注入如此多的依赖项,您为您的类分配了太多的职责,需要进一步重构。通过使用 @Autowired,您将不会遇到这个问题!所以,要小心并注意注入字段的数量。

通过增加项目的复杂性,所需依赖项的数量也会增加。很快,可能很难区分所有东西是如何绑定在一起的。

Making your classes too open

许多开发人员往往有太多暴露的类,即有一个过度公开可见的代码。使内部结构过于开放会降低代码的灵活性并增加问题的风险。这方面的一个示例可能是跳过整个层并直接访问不应直接访问的类方法和属性。

Mutability

让变量保持可变并不总是一个好主意。例如,一个变量甚至可以在您访问它之前进行修改。只要有可能,您应该配置您的变量,以便它们可以是常量,或者一旦获得它们的值就不能再更改(仅暴露 getter)。

Multithreading

与任何其他软件一样,多线程可能是一个问题。开发人员经常倾向于做错事。调试有问题的多线程应用程序也可能是一个问题。那么,多线程实践存在哪些问题?这里有几个:

  • The first cause of a headache can come from globally visible and modifiable variables. Leaving your variables global opens doors for a lot of issues. Debugging issues that come from this can become a nightmare.
  • The second cause can be the spawning of too many threads. If you do so, this can affect the performance of your application significantly. We will suggest proper practice for this in the next sections.
  • Also, developers tend not to name their threads. Assign meaningful names to your threads so debugging and tracking can be easier!

Not validating data

软件中出现问题的常见原因之一是未验证您处理的数据。有时我们不会验证预期字段、电子邮件地址和其他数据的长度。将无效数据传递给我们系统的组件是故障甚至崩溃的根源。

Tests coverage

另一个不好的做法是不编写单元测试。最糟糕的情况是根本没有!没有用测试覆盖你所有的课程也好不到哪里去。为什么这很重要?这很重要,不仅因为测试证明所有组件都正常运行,而且它们还可以作为我们代码的文档。我们将在下一章更多地讨论单元测试,我们将指导你如何编写它们。

XML configurations

XML 配置是我们用来配置 Spring 应用程序的。通过 XML 配置 Spring 应用程序是许多样板代码的来源。幸运的是,Spring 5 应用程序不需要这种配置方式。我们使用适当的注释来代替,就像我们在本书中的所有示例中所做的那样。

Good ways to develop your code

由于我们刚刚挑战了 Spring Framework 的一些最重要的特性和实践,现在是时候阐明开发代码的好方法了。让我们从依赖注入重新开始。

Don't inject too much

正如我们已经提到的,@Autowired 注释将注入所有必要的依赖项。为了避免我们之前解释的依赖注入问题,请尽量避免将过多的依赖注入到单个类中。通常,如果您的班级需要的 bean 很少,那会很好。保持这种平衡并组织架构取决于您。

Use a closed-visibility approach

为避免因公开公开您的方法和字段而引起的问题,请首先考虑使它们至少在包级别可见。理想情况下,封装在类中的所有字段都应该是私有的或受保护的,或者处于包级别的可见性。仅在需要时公开这些字段或方法。考虑将方法的功能提供给外部世界的后果。

Solving the multithreading issue

没有单一的解决方案可以解决我们之前描述的多线程实践问题。所有解决方案都取决于您遇到的特定问题的性质,以及您软件的现有架构。

例如,使用同步可以防止变量全局可见和可修改的问题的解决方案——但这在性能方面可能代价高昂。所以,再一次,如果你打算在全球范围内公开某些东西,请三思而后行。可以有其他方法来实现相同的目标。

如果您倾向于创建许多新线程来运行相同的功能,更好的解决方案可能是只运行一个线程并在需要时执行相同的操作。例如,如果我们不断地为相同的操作启动一个新线程并且我们经常这样做,我们的性能和内存将被浪费在垃圾收集上。

在处理线程时,我们还建议使用适当的类而不是直接触发裸线程。使用 ExecutorService 抽象,以便更好地控制线程执行。通过这样做,您可以限制同时运行的最大线程数或要放入队列的最大线程数。

Spring data validation

让我们面对我们之前讨论过的问题:不验证数据。 Spring Framework 为我们提供了一组验证器,借助它们我们可以验证我们处理的任何类型的数据。此外,如果需要,可以通过实现以下接口来编写自定义验证器:

org.springframework.validation.Validator

我们将看看最常用的验证器:

  • @Length: To validate a variable for length
  • @Email: To validate an email address
  • @NonNull: To validate not being null
  • @Null: To validate being null
  • @NotBlank: To validate that the annotated string is not null or empty; the difference here to @NotEmpty being that trailing whitespaces are ignored
  • @NotEmpty: To validate that the annotated string is not null or empty
  • @Range: To validate that an annotated element is in the appropriate range
  • @SafeHtml: To validate a rich-text value provided by the user to ensure that it contains no malicious code
  • @URL: To validate a URL

如果前面提到的验证器不能满足您的需求,您可以随时编写自己的验证器实现。让我们举一个自定义验证器的快速示例:

package com.journaler.api.validator 
 
import com.journaler.api.data.NoteDTO 
import org.springframework.stereotype.Component 
import org.springframework.validation.Errors 
import org.springframework.validation.Validator 
 
@Component 
class NoteValidator : Validator { 
 
    override fun validate(target: Any?, errors: Errors?) { 
        if (target is NoteDTO) { 
            when (target) { 
                // Reject if example pattern 'x x x x' occurs. 
                target.title.contains("x x x x") -> errors?.rejectValue("title", "Not allowed!") 
            } 
            return 
        } 
        errors?.rejectValue("title", "Only String titles allowed") 
    } 
 
    override fun supports(clazz: Class<*>?): Boolean { 
        return NoteDTO::class.java == clazz 
    } 
} 

如您所见,您所要做的就是实现一个接口并在 validate 方法实现中应用验证。

Summary

讨论 Spring 中良好的软件和开发实践是一个没完没了的话题。在本章中,我们确定了一些需要考虑的最重要的实践。试着想想我们讨论过的好的和坏的做法,最重要的是,试着练习一段时间。您会注意到您开发的应用程序的质量将显着提高。此外,尝试对其他良好实践建议持开放态度,并考虑如何从中受益。最重要的是,避免不良做法,因为其中许多会导致您误入歧途。

在下一章中,我们将介绍一个特别好的开发实践:我们将为我们的代码编写单元测试。我们将演示如何编写测试并提供如何使用它们的指导。请耐心等待,尝试下面的每个示例,并考虑如何使用它们来测试您自己的一些组件。