春10の一般的な間違いは、それらの古いプログラマの78%は、ピットを強化しています!

まず、私たちが見て、春の一般的なエラーがものであり、

  1. 下にあまりにも多くの注意
  2. 内部構造は「漏れました」
  3. 関心事の分離の欠如
  4. 不足の例外処理や誤操作
  5. 不適切なマルチスレッディング
  6. 注釈ベースの認証を使用しないでください。
  7. (静止画)XMLベースの構成を使用して
  8. プロフィールを無視
  9. 容認できない依存性注入
  10. テストの欠如、または不適切なテスト
    春10の一般的な間違いは、それらの古いプログラマの78%は、ピットを強化しています!

それから彼は、これらのよくある間違いの1導入
下にあまりにも多くの注意:間違い1.
「ここに発明していない」症候群は、ソフトウェア開発では非常に一般的ですので、私たちはこの共通の間違いに取り組んでいます。いくつかの一般的な症状が頻繁に書き換えたコードが含まれ、多くの開発者は、この症状を持っています。
特定のライブラリとその実装の内部構造を理解しながら、大幅に優れかつ必要である(それは良い学習プロセスすることができます)が、ソフトウェア・エンジニアとして、同じ基本となる実装の詳細に対処していき個人のキャリア開発は有害です。
この抽象化Springフレームワークが理由でありそこにあるように、それは手作業で繰り返しからあなたを解放し、あなたがより高いレベルの内容に集中することができます-ドメインオブジェクトとビジネス・ロジック。
したがって、抽象を受け入れます。あなたが特定の問題に直面して、次回、最初のクイック検索は、この問題を解決するためのライブラリが春に統合されているかどうかを決定するために、今、あなたは、適切な既製のソリューションを見つけるかもしれません。
例えば、有用なライブラリーは、この記事の残りの部分では、私が例では、プロジェクトロンボクアノテーションを使用します。ロンボク島は、モデルのコードジェネレータとして使用されている、怠惰な開発者は、このライブラリに慣れの問題を持っている必要はありません。例えば、ロンボクの使用「標準のJavaビーン」を見て、それがどんなものか:
次のようにあなたが想像できるように、上記のコードはコンパイルされています。
しかし、あなたはロンボク島でIDEを使用する場合、それはプラグインをインストールする必要がありますのでご注意くださいここでは、プラグインののIntelliJ IDEAのバージョンを見つけることができます。

2.エラー2:内部構造は「漏れた」
それは、このように貧弱なコーディング手法に貢献し、サービス設計には柔軟性が発生することはありませんので、あなたの内部構造を開いて、良いアイデアになることはありません。内部機構は、APIエンドポイントのデータベース構造の一部の性能からアクセス可能な「漏れ」。例えば、以下のPOJO(「プレーンオールドJavaオブジェクト」 ) クラスは、データベース内のテーブルを表します。

@Entity
@NoArgsConstructor
@Getter
public class TopTalentEntity {
    @Id
    @GeneratedValue
    private Integer id;
    @Column
    private String name;
    public TopTalentEntity(String name) {
        this.name = name;
    }
}

エンドポイントがあると仮定し、彼はアクセスTopTalentEntityデータを必要とします。戻りTopTalentEntityインスタンスは魅力的かもしれないが、より柔軟な解決策は、APIエンドポイント上TopTalentEntityデータを表現するために、新しいクラスを作成することです。

@AllArgsConstructor
@NoArgsConstructor
@Getter
public class TopTalentData {
    private String name;
}

このように、データベースバックエンドへの変更は、サービス層内の任意の追加の変更を加える必要はありません。検討中の、TopTalentEntityでデータベースユーザーパスワードのハッシュ値を格納するための「パスワード」フィールドを追加-していない場合は、好きなフロントエンドサービスを変更することを忘れ、誤って不要な機密情報にさらされるTopTalentData。
春10の一般的な間違いは、それらの古いプログラマの78%は、ピットを強化しています!
3.エラー3:関心事の分離の欠如
プログラムのサイズの成長には、徐々に、コードの組織はますます重要な課題となっています。特にアプリケーションのアーキテクチャ設計の状況をあまり考慮せず-皮肉なことに、優れたソフトウェアエンジニアリングの原則のほとんどは規模に崩壊し始めました。間違いは、開発者が最も一般的にコミットし、コードの問題を混同することで、それを行うのは非常に簡単です!
一般的には、懸念の分離は、単に既存のクラスに「反転」の新機能を壊しています。もちろん、これは(先発のため、それが少ない入力を必要とする)良い短期的な解決策であるが、それは必然的にそれがテスト中であるかどうか、将来的に問題となって、メンテナンス、またはどこかの間になります2間。それは、データベースからTopTalentDataを返し、次のコントローラを考えてみましょう。

@RestController
public class TopTalentController {
    private final TopTalentRepository topTalentRepository;
    @RequestMapping("/toptal/get")
    public List<TopTalentData> getTopTalent() {
        return topTalentRepository.findAll()
                .stream()
                .map(this::entityToData)
                .collect(Collectors.toList());
    }
    private TopTalentData entityToData(TopTalentEntity topTalentEntity) {
        return new TopTalentData(topTalentEntity.getName());
    }
}

起初,这段代码似乎没什么特别的问题;它提供了一个从 TopTalentEntity 实例检索出来的TopTalentData 的 List。
然而,仔细观察下,我们可以看到 TopTalentController 实际上在此做了些事情;也就是说,它将请求映射到特定端点,从数据库检索数据,并将从 TopTalentRepository 接收的实体转换为另一种格式。一个“更干净” 的解决方案是将这些关注点分离到他们自己的类中。看起来可能是这个样子的:

@RestController
@RequestMapping("/toptal")
@AllArgsConstructor
public class TopTalentController {
    private final TopTalentService topTalentService;
    @RequestMapping("/get")
    public List<TopTalentData> getTopTalent() {
        return topTalentService.getTopTalent();
    }
}
@AllArgsConstructor
@Service
public class TopTalentService {
    private final TopTalentRepository topTalentRepository;
    private final TopTalentEntityConverter topTalentEntityConverter;
    public List<TopTalentData> getTopTalent() {
        return topTalentRepository.findAll()
                .stream()
                .map(topTalentEntityConverter::toResponse)
                .collect(Collectors.toList());
    }
}
@Component
public class TopTalentEntityConverter {
    public TopTalentData toResponse(TopTalentEntity topTalentEntity) {
        return new TopTalentData(topTalentEntity.getName());
    }
}

这种层次结构的另一个优点是,它允许我们通过检查类名来确定将功能驻留在何处。此外,在测试期间,如果需要,我们可以很容易地用模拟实现来替换任何类。

4. 错误四:缺乏异常处理或处理不当
一致性的主题并非是 Spring(或 Java)所独有的,但仍然是处理 Spring 项目时需要考虑的一个重要方面。虽然编码风格可能存在争议(通常团队或整个公司内部已达成一致),但拥有一个共同的标准最终会极大地提高生产力。对多人团队尤为如此;一致性允许交流发生,而不需要花费很多资源在手把手交接上,也不需要就不同类的职责提供冗长的解释。
考虑一个包含各种配置文件、服务和控制器的 Spring 项目。在命名时保持语义上的一致性,可以创建一个易于搜索的结构,任何新的开发人员都可以按照自己的方式管理代码;例如,将 Config 后缀添加到配置类,服务层以 Service 结尾,以及控制器用 Controller 结尾。
与一致性主题密切相关,服务器端的错误处理值得特别强调。如果你曾经不得不处理编写很差的 API 的异常响应,那你可能知道原因 —— 正确解析异常会是一件痛苦的事情,而确定这些异常最初发生的原因则更为痛苦。
作为一名 API 开发者,理想情况下你希望覆盖所有面向用户的端点,并将他们转换为常见的错误格式。这通常意味着有一个通用的错误代码和描述,而不是逃避解决问题:a) 返回一个 “500 Internal Server Error”信息。b) 直接返回异常的堆栈信息给用户。(实际上,这些都应该不惜一切代价地去避免,因为除了客户端难以处理以外,它还暴露了你的内部信息)。
例如,常见错误响应格式可能长这样:

@Value
public class ErrorResponse {
    private Integer errorCode;
    private String errorMessage;
}

与此类似的事情在大多数流行的 API 中也经常遇到,由于可以容易且系统地记录,效果往往很不错。将异常转换为这种格式可以通过向方法提供 @ExceptionHandler 注解来完成(注解案例可见于第六章)。
春10の一般的な間違いは、それらの古いプログラマの78%は、ピットを強化しています!

5. 错误五:多线程处理不当
不管是桌面应用还是 Web 应用,无论是 Spring 还是 No Spring,多线程都是很难破解的。由并行执行程序所引起的问题是令人毛骨悚然且难以捉摸的,而且常常难以调试 —— 实际上,由于问题的本质,一旦你意识到你正在处理一个并行执行问题,你可能就不得不完全放弃调试器了,并 “手动” 检查代码,直到找到根本上的错误原因。
不幸的是,这类问题并没有千篇一律的解决方案;根据具体场景来评估情况,然后从你认为最好的角度来解决问题。
当然,理想情况下,你也希望完全避免多线程错误。同样,不存在那种一刀切的方法,但这有一些调试和防止多线程错误的实际考虑因素:
5.1. 避免全局状态
首先,牢记 “全局状态” 问题。如果你正创建一个多线程应用,那么应该密切关注任何可能全局修改的内容,如果可能的话,将他们全部删掉。如果某个全局变量有必须保持可修改的原因,请仔细使用 synchronization,并对程序性能进行跟踪,以确定没有因为新引入的等待时间而导致系统性能降低。
5.2. 避免可变性
这点直接来自于 函数式编程,并且适用于 OOP,声明应该避免类和状态的改变。简而言之,这意味着放弃 setter 方法,并在所有模型类上拥有私有的 final 字段。它们的值唯一发生变化的时间是在构造期间。这样,你可以确定不会出现争用问题,且访问对象属性将始终提供正确的值。
5.3. 记录关键数据
评估你的程序可能会在何处发生异常,并预先记录所有关键数据。如果发生错误,你将很高兴可以得到信息说明收到了哪些请求,并可更好地了解你的应用程序为什么会出现错误。需要再次注意的是,日志记录引入了额外的文件 I/O,可能会严重影响应用的性能,因此请不要滥用日志。
5.4. 复用现存实现
每当你需要创建自己的线程时(例如:向不同的服务发出异步请求),复用现有的安全实现来代替创建自己的解决方案。这在很大程度上意味着要使用 ExecutorServices 和 Java 8 简洁的函数式 CompletableFutures 来创建线程。Spring 还允许通过 DeferredResult 类来进行异步请求处理。

6. 错误六:不使用基于注解的验证
假设我们之前的 TopTalent 服务需要一个端点来添加新的 TopTalent。此外,假设基于某些原因,每个新名词都需要为 10 个字符长度。执行此操作的一种方法可能如下:

@RequestMapping("/put")
public void addTopTalent(@RequestBody TopTalentData topTalentData) {
    boolean nameNonExistentOrHasInvalidLength =
            Optional.ofNullable(topTalentData)
         .map(TopTalentData::getName)
   .map(name -> name.length() == 10)
   .orElse(true);
    if (nameNonExistentOrInvalidLength) {
        // throw some exception
    }
    topTalentService.addTopTalent(topTalentData);
}

然而,上面的方法(除了构造很差以外)并不是一个真正 “干净” 的解决办法。我们正检查不止一种类型的有效性(即 TopTalentData 不得为空,TopTalentData.name 不得为空,且 TopTalentData.name 为 10 个字符长度),以及在数据无效时抛出异常。
通过在 Spring 中集成 Hibernate validator,数据校验可以更干净地进行。让我们首先重构 addTopTalent 方法来支持验证:

@RequestMapping("/put")
public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) {
    topTalentService.addTopTalent(topTalentData);
}
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) {
    // handle validation exception
}
// 此外,我们还必须指出我们想要在 TopTalentData 类中验证什么属性:
public class TopTalentData {
    @Length(min = 10, max = 10)
    @NotNull
    private String name;
}

现在,Spring 将在调用方法之前拦截其请求并对参数进行验证 —— 无需使用额外的手工测试。
另一种实现相同功能的方法是创建我们自己的注解。虽然你通常只在需要超出 Hibernate的内置约束集 时才使用自定义注解,本例中,我们假设 @Length 不存在。你可以创建两个额外的类来验证字符串长度,一个用于验证,一个用于对属性进行注解:

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = { MyAnnotationValidator.class })
public @interface MyAnnotation {
    String message() default "String length does not match expected";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    int value();
}
@Component
public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, String> {
    private int expectedLength;
    @Override
    public void initialize(MyAnnotation myAnnotation) {
        this.expectedLength = myAnnotation.value();
    }
    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        return s == null || s.length() == this.expectedLength;
    }
}

请注意,这些情况下,关注点分离的最佳实践要求在属性为 null 时,将其标记为有效(isValid 方法中的 s == null),如果这是属性的附加要求,则使用 @NotNull 注解。

public class TopTalentData {
    @MyAnnotation(value = 10)
    @NotNull
    private String name;
}

春10の一般的な間違いは、それらの古いプログラマの78%は、ピットを強化しています!

7. 错误七:(依旧)使用基于xml的配置
虽然之前版本的 Spring 需要 XML,但如今大部分配置均可通过 Java 代码或注解来完成;XML 配置只是作为附加的不必要的样板代码。
本文(及其附带的 GitHub 仓库)均使用注解来配置 Spring,Spring 知道应该连接哪些 Bean,因为待扫描的顶级包目录已在 @SpringBootApplication 复合注解中做了声明,如下所示:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

复合注解(可通过 Spring 文档 了解更多信息)只是向 Spring 提示应该扫描哪些包来检索 Bean。在我们的案例中,这意味着这个顶级包 (co.kukurin)将用于检索:

@Component (TopTalentConverter, MyAnnotationValidator)
@RestController (TopTalentController)
@Repository (TopTalentRepository)
@Service (TopTalentService) 类

如果我们有任何额外的 @Configuration 注解类,它们也会检查基于 Java 的配置。

8. 错误八:忽略 profile
在服务端开发中,经常遇到的一个问题是区分不同的配置类型,通常是生产配置和开发配置。在每次从测试切换到部署应用程序时,不要手动替换各种配置项,更有效的方法是使用 profile。关注Java技术栈微信公众号,在后台回复关键字:boot,可以获取一份栈长整理的 Spring Boot 最新技术干货。
考虑这么一种情况:你正在使用内存数据库进行本地开发,而在生产环境中使用 MySQL 数据库。本质上,这意味着你需要使用不同的 URL 和 (希望如此) 不同的凭证来访问这两者。让我们看看可以如何做到这两个不同的配置文件:
8.1. APPLICATION.YAML 文件

#set default profile to 'dev'
spring.profiles.active: dev
# production database details
spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal'
spring.datasource.username: root
spring.datasource.password:
8.2. APPLICATION-DEV.YAML 文件
spring.datasource.url: 'jdbc:h2:mem:'
spring.datasource.platform: h2

假设你不希望在修改代码时意外地对生产数据库进行任何操作,因此将默认配置文件设为 dev 是很有意义的。
然后,在服务器上,你可以通过提供 -Dspring.profiles.active=prod 参数给 JVM 来手动覆盖配置文件。另外,还可将操作系统的环境变量设置为所需的默认 profile。

9. 错误九:无法接受依赖项注入
正确使用 Spring 的依赖注入意味着允许其通过扫描所有必须的配置类来将所有对象连接在一起;这对于解耦关系非常有用,也使测试变得更为容易,而不是通过类之间的紧耦合来做这样的事情:

public class TopTalentController {
    private final TopTalentService topTalentService;
    public TopTalentController() {
        this.topTalentService = new TopTalentService();
    }
}

我们让 Spring 为我们做连接:
public class TopTalentController {
    private final TopTalentService topTalentService;
    public TopTalentController(TopTalentService topTalentService) {
        this.topTalentService = topTalentService;
    }
}

Misko Hevery 的 Google talk 深入解释了依赖注入的 “为什么”,所以,让我们看看它在实践中是如何使用的。在关注点分离(常见错误 #3)一节中,我们创建了一个服务和控制器类。
假设我们想在 TopTalentService 行为正确的前提下测试控制器。我们可以通过提供一个单独的配置类来插入一个模拟对象来代替实际的服务实现:

@Configuration
public class SampleUnitTestConfig {
    @Bean
    public TopTalentService topTalentService() {
        TopTalentService topTalentService = Mockito.mock(TopTalentService.class);
        Mockito.when(topTalentService.getTopTalent()).thenReturn(
                Stream.of("Mary", "Joel").map(TopTalentData::new).collect(Collectors.toList()));
        return topTalentService;
    }
}

然后,我们可以通过告诉 Spring 使用 SampleUnitTestConfig 作为它的配置类来注入模拟对象:
@ContextConfiguration(classes = { SampleUnitTestConfig.class })

之后,我们就可以使用上下文配置将 Bean 注入到单元测试中。

10.エラー10:テストの欠如、または不適切なテスト
、または(それが「必要」の時間でない場合は特に)、ユニットテストの概念は長い間の周りされているが、多くの開発者がそれを行うことを「忘れる」ためにどちらかのように見えますしかし、後知恵で、それが追加されました。これはテストだけではなく、コードの正しさを検証する必要があるため、文書はまた、振る舞い方の異なるシナリオの下でプログラムする必要があり、明らかに望ましくありません。
Webサービスをテストするときの通信は通常、春のDispatcherServletを呼び出し、そしてあなたがHTTP経由でHttpServletRequestを実際に受信したときに何が起こるか見るために必要とされているので、のみまれ「純粋な」ユニットテストは、(それを「統合」のテストを行います)検証、配列、等を処理します。
JavaのDSLを簡素化するRESTアシュアード、テストRESTサービスは、MockMVCに、非常にエレガントなソリューションを提供することが示されています。依存性注入と、次のコードスニペットを考えてみましょう:

@RunWith(SpringJUnit4Cla***unner.class)
@ContextConfiguration(classes = {
        Application.class,
        SampleUnitTestConfig.class
})
public class RestAssuredTestDemonstration {
    @Autowired
    private TopTalentController topTalentController;
    @Test
    public void shouldGetMaryAndJoel() throws Exception {
        // given
        MockMvcRequestSpecification givenRestAssuredSpecification = RestAssuredMockMvc.given()
                .standaloneSetup(topTalentController);
        // when
        MockMvcResponse response = givenRestAssuredSpecification.when().get("/toptal/get");
        // then
        response.then().statusCode(200);
        response.then().body("name", hasItems("Mary", "Joel"));
    }
}

SampleUnitTestConfigクラスアナログ実装TopTalentControllerに接続TopTalentService、および他のすべてのクラスがスキャンアプリケーションパッケージ標準下位クラスパッケージディレクトリによって推論されます。RestAssuredMockMvcはただ軽量な環境を設定するために、および/ toptal / GETエンドポイントはGETリクエストを送信します。

サポートのおかげで、ヨーヨーの記事を覚えて賞賛のポイントのように、みんなと共有へようこそ!

おすすめ

転載: blog.51cto.com/14442094/2426068