1.間違い:根本的にあまりにも多くの注意
「ここに発明されていない」症候群は、ソフトウェア開発では非常に一般的ですので、私たちは、この一般的な間違いに取り組んでいます。いくつかの一般的な症状が頻繁に書き換えたコードが含まれ、多くの開発者は、この症状を持っています。
特定のライブラリとその実装の内部構造を理解しながら、大幅に優れかつ必要である(それは良い学習プロセスすることができます)が、ソフトウェア・エンジニアとして、同じ基本となる実装の詳細に対処していき個人のキャリア開発は有害です。この抽象化Springフレームワークが理由でありそこにあるように、それは手作業で繰り返しからあなたを解放し、あなたがより高いレベルの内容に集中することができます - ドメインオブジェクトとビジネス・ロジック。
したがって、抽象を受け入れます。あなたが特定の問題に直面して、次回、最初のクイック検索は、この問題を解決するためのライブラリが春に統合されているかどうかを決定するために、今、あなたは、適切な既製のソリューションを見つけるかもしれません。例えば、有用なライブラリーは、この記事の残りの部分では、私が例では、プロジェクトロンボクアノテーションを使用します。ロンボク島は、モデルのコードジェネレータとして使用されている、怠惰な開発者は、このライブラリに慣れの問題を持っている必要はありません。例えば、ロンボクの使用を見て、「標準のJava Beanが」それは次のようになります。
@Getter @Setter @NoArgsConstructor パブリッククラス豆はSerializable {実装 int型firstBeanPropertyを。 文字列secondBeanProperty; }
あなたが想像できるように、以下のように、上記のコードはコンパイルされます。
パブリッククラス豆はSerializable {実装 プライベートint型firstBeanPropertyを。 プライベート文字列secondBeanProperty。 公共INT getFirstBeanProperty(){ this.firstBeanPropertyを返します。 } パブリック文字列getSecondBeanProperty(){ this.secondBeanPropertyを返します。 } 公共ボイドsetFirstBeanProperty(int型firstBeanProperty){ this.firstBeanProperty = firstBeanProperty。 } 公共ボイドsetSecondBeanProperty(文字列secondBeanProperty){ this.secondBeanProperty = secondBeanProperty。 } パブリックビーン(){ } }
しかし、あなたはロンボク島でのIDEを使用する場合、それはここでのIntelliJ IDEAプラグイン見つけることができるプラグインのバージョンをインストールする必要がありますのでご注意ください。
2.エラー2:内部構造は「漏れました」
それは、このように貧弱なコーディング手法に貢献し、サービス設計には柔軟性が発生することはありませんので、あなたの内部構造を開き、それは、良いアイデアになることはありません。内部機構は、APIエンドポイントのデータベース構造の一部の性能からアクセス可能な「漏れ」。例えば、以下のPOJO(「プレーンオールドJavaオブジェクト」)クラスは、データベース内のテーブルを表します。
@Entity @NoArgsConstructor @Getter パブリッククラスTopTalentEntity { @Id @GeneratedValue プライベート整数ID。 @Column プライベート文字列名; 公共TopTalentEntity(文字列名){ this.name =名。 } }
エンドポイントがあると仮定し、彼がアクセスする必要があるTopTalentEntity
データを。戻りTopTalentEntity
魅力的かもしれインスタンスを、より柔軟なソリューションは、APIエンドポイントを表すために、新しいクラスを作成することですTopTalentEntity
データを。
@AllArgsConstructor @NoArgsConstructor @Getter パブリッククラスTopTalentData { プライベート文字列名; }
このように、データベースバックエンドへの変更は、サービス層内の任意の追加の変更を加える必要はありません。検討中の、TopTalentEntity
データベース内のユーザーのパスワードのハッシュ値を格納するための「パスワード」フィールドを追加- noの場合TopTalentData
、接続は同様ではありません、誤って不要な機密情報にさらされる、フロントエンドサービスを変更することを忘れ。
3.エラー3:関心事の分離の欠如
プログラムのサイズの増加に伴い、徐々に、コードの組織はますます重要な課題となっています。特にアプリケーションのアーキテクチャ設計の状況をあまり考慮せず - 皮肉なことに、優れたソフトウェアエンジニアリングの原則のほとんどは規模に崩壊し始めました。間違いは、開発者が最も一般的にコミットしたコードの問題を混同している、それを行うのは非常に簡単です!
一般的には、懸念の分離は、単に既存のクラスに「反転」新機能を壊しています。もちろん、これは(先発のため、それが少ない入力を必要とする)良い短期的な解決策であるが、それは必然的にそれがテスト中であるかどうか、将来的に問題となって、メンテナンス、またはどこかの間になります2間。それは、データベースから返され、次のコントローラを考えてみましょうTopTalentData
。
@RestController パブリッククラスTopTalentController { 民間最終TopTalentRepository topTalentRepository。 @RequestMapping( "/ toptal /取得") 公共の一覧<TopTalentData> getTopTalent(){ 返すtopTalentRepository.findAll() .stream() .MAP(本:: entityToData) .collect(Collectors.toList()); } プライベートTopTalentData entityToData(TopTalentEntity topTalentEntity){ 戻り新しいTopTalentData(topTalentEntity.getName())。 } }
まず、コードは特に問題はないと思われる。それから提供TopTalentEntity
うち検索インスタンスTopTalentData
一覧。しかし、注意深い観察の下で、我々は見ることができTopTalentController
、つまり、それは特定のエンドポイントに要求をマッピングし、データベースからやからデータを取得し、実際にこれで何かをするTopTalentRepository
エンティティが別に受信した1つのフォーマットに変換します。「クリーナー」ソリューションは、自分のクラスにこれらの懸念を分離することです。それは次のようになります。
@RestController @RequestMapping( "/ toptal") @AllArgsConstructor パブリッククラスTopTalentController { 民間最終TopTalentService topTalentService。 @RequestMapping( "/取得") 公共の一覧<TopTalentData> getTopTalent(){ (topTalentService.getTopTalentを返します)。 } } @AllArgsConstructor @Service パブリッククラスTopTalentService { 民間最終TopTalentRepository topTalentRepository。 民間最終TopTalentEntityConverter topTalentEntityConverter。 公共の一覧<TopTalentData> getTopTalent(){ 返すtopTalentRepository.findAll() .stream() .MAP(人材エンティティコンバータ:: .collect(Collectors.toList()); } } @Component パブリッククラスToptalentエンティティコンバータ{ 公共ToptalentデータtoResponse(Toptalentエンティティタレントエンティティ){ 戻り新しいToptalentデータ(topTalentEntity.getName())。 } }
そのような階層の他の利点は、関数がクラス名をチェックして、存在する場所を決定することができることです。必要に応じて加えて、テスト中に、我々は簡単に任意のクラスを置き換えるためにアナログを実装できます。
4.エラー4:不足の例外処理や誤操作
一貫性はユニークですが、それでも春のプロジェクトを扱う際に考慮すべき重要な側面である春(またはJava)のテーマではありません。スタイルが物議を醸すかもしれ(通常は全体の社内チームまたは合意している)が、一般的な標準でコーディングしながら、最終的には大幅に生産性を向上します。これは、複数のチームでは特に顕著であり、一貫性が交換が手に多くのリソースを過ごすには、転送が責任の同じ種類の長い説明を提供していません必要はありませんなくても場所を取ることができます。
春のプロジェクトは、さまざまなコンフィギュレーションファイル、サービス、およびコントローラが含まれて考えてみましょう。命名の意味での一貫性を維持するためには、独自の方法でコードを管理するために、検索簡単に任意の新しい開発者が構造を作成することができ、例えば、コンフィギュレーションConfigクラス、サービスの終了までサービス層だけでなく、制御にサフィックスを追加します使用されるコントローラを終了します。
密接に一貫性のテーマに関連した、サーバー側のエラー処理は、特別な強調に値します。あなたはAPI異常応答の準備不足に対処するために持っていた場合は、おそらくその理由を知っている - 正確に痛みを伴うことになり、異常を解決しますが、これらの異常の原因を決定するために、第1、さらに痛みが発生しました。
API開発者として、理想的には、すべてのユーザー向けエンドを上書きし、一般的なエラー・フォーマットに変換します。これは通常、むしろ問題を解決するためにエスケープよりも、一般的なエラーコードと説明があることを意味します。a)「500内部サーバーエラー」メッセージを返します。ユーザーへのb)に直接リターンスタック例外情報。(取り扱いが難しいクライアントに加えて、それはまたあなたの内部情報を公開しているので実際には、これらは、行くためにすべてのコストで避けるべきです)。
例えば、一般的なエラー応答フォーマットは、長い道のりであってもよいです。
@value パブリッククラスはErrorResponse { プライベート整数のerrorCode。 プライベート文字列にErrorMessage。 }
このようなものは、多くの場合、あなたが簡単かつ体系的に記録することができるので、結果は、多くの場合、非常に優れている、最も人気のあるAPIに遭遇しました。方法を提供することによって、このフォーマットに例外がする@ExceptionHandler
コメント(VI章に見られる場合のメモ)を行うこと。
5.エラー5:マルチスレッド誤操作
デスクトップやWebアプリケーション、いずれかの春またはNo春かどうかは、マルチスレッドを破るのは難しいです。デバッグに不気味ととらえどころのない、としばしば困難なことに起因するプログラムの並列実行によって引き起こされる問題 - 実際には、問題の性質のために、あなたはあなたが問題の並列実装を扱っていることを認識したら、あなたは完全にあきらめする必要がありますあなたは上のエラーの根本的な原因を見つけるまで、デバッガ、および「マニュアル」は、コードを確認してください。残念ながら、これらの問題は、クッキーカッターをソリューションではありません。状況を評価し、その後、あなたは最高の角度だと思う問題を解決するために、シーンに応じました。
もちろん、理想的に、あなたは完全にマルチスレッド化エラーを回避したいです。同様に、1つのサイズの種類は、すべてのアプローチが存在していないが、いくつかのマルチスレッドのデバッグがあると誤った実用的な考慮事項を防ぐフィット:
5.1。グローバルな状態を避けます
まず第一に、心の中で「グローバルな状態」の問題を保ちます。あなたは、マルチスレッドアプリケーションを作成する場合は、可能な場合、あなたは、世界的に変更することができるものに細心の注意を払う必要があり、それらはすべて削除されます。グローバル変数を変更することができた理由のために維持されなければならない場合は、新たなシステムの導入を待って、パフォーマンスが低下する時間がないので、慎重に判断するために、同期、およびパフォーマンスの追跡プログラムを使用してください。
5.2。の変動を避けます
これを直接プログラミングから機能、およびOOPの適用は、クラス宣言は回避し、状態を変更する必要があります。要するに、これは、setterメソッドをあきらめ意味し、すべてのモデルクラスの民間最終フィールドがあります。工事中に発生した時間だけその値が変化します。このように、あなたは常に正しい値を提供するプロパティをobjectに競合の問題、およびアクセスできなくなりますことを確認することができます。
5.3。鍵データを記録します
評価プログラムの例外が発生することができる場所のすべての重要なデータと事前に記録します。エラーが発生した場合、その情報は、要求が受信され、より良い理由を、アプリケーション・エラーを理解するために何を説明することは喜んでいるだろう得ることができます。再度、追加のログ・ファイルの導入は、I / Oを、真剣のでログを乱用しないでください、アプリケーションのパフォーマンスに影響を与えることに注意する必要があります。
5.4。既存の実現を再利用
既存のセキュリティ実装を使用するのではなく、独自のソリューションを再作成するために、:あなたは、(異なるサービスの非同期要求に対して発行など)、独自のスレッドを作成する必要があるとき。これは、主にスレッドを作成しExecutorServicesとJava 8の単純な機能CompletableFuturesを使用することを意味しています。春はまたDeferredResultクラスで非同期リクエスト処理を可能にします。
6.エラー6:アノテーションベースの認証を使用していません
TopTalentの終わりは新しいTopTalentを追加する前に、我々はサービスを必要とします。また、何らかの理由で仮定に基づいて、すべての新しい用語は、長さが10文字を必要とします。次のようにこれを行うための方法は次のようになります。
( "/ PUT")@RequestMapping 公共ボイドaddTopTalent(@RequestBody TopTalentData topTalentData){ boolean型nameNonExistentOrHasInvalidLength = Optional.ofNullable(topTalentData) .MAP(TopTalentData ::のgetName) の.map(名- > name.length()== 10) .orElse(TRUE)。 (nameNonExistentOrInvalidLength){もし //いくつかの例外をスロー } topTalentService.addTopTalent(topTalentData)。 }
しかし、(貧しい建設を除く)は、上記の方法は、真の「クリーン」なソリューションではありません。私たちは、有効性の複数のタイプ(TopTalentDataは空にできませすなわち、TopTalentData.nameの長さは10文字まで空、そしてTopTalentData.nameではない)をチェックし、データが無効である場合に例外をスローしています。
データは、春にHibernateのバリデータを統合することによって、よりきれいであることが確認されています。復興addTopTalent方法の検証をサポートするために、最初にすべての私たちをみましょう:
( "/ PUT")@RequestMapping 公共ボイドaddTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData){ topTalentService.addTopTalent(topTalentData)。 } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) パブリックはErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException){ //ハンドル検証例外 }
加えて、我々はまた、我々はTopTalentDataクラスのどのプロパティを確認したいことを指摘する必要があります。
パブリッククラスTopTalentData { @Length(MIN = 10、最大= 10) @NotNull プライベート文字列名。 }
さて、春には、その要求をインターセプトし、メソッドを呼び出す前に、パラメータを検証する - 追加の手動テストを使用せずに。
同じ機能を実現するもう一つの方法は、私たち自身のノートを作成することです。通常、あなたが使用カスタム注釈を休止制約のビルトインのセットを超えて行く必要があるときにのみが、このケースでは、我々は@Lengthが存在しないことを前提としています。あなたはコメントの検証属性の文字列の長さ、1を検証するために、2つの追加クラスを作成することができます。
@Target({ElementType.METHOD、ElementType.FIELD、ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = {MyAnnotationValidator.class}) パブリック@interfaceのMyAnnotation { 文字列メッセージ()デフォルトの「文字列長「予想と一致しません。 <?>クラス[]グループ()デフォルト{}。 クラス<?ペイロード> []ペイロード()デフォルト{}延びています。 int値(); } @Component パブリッククラスMyAnnotationValidatorはConstraintValidator <MyAnnotation、文字列> {実装 プライベートint型expectedLengthと、 @Override 公共ボイドイニシャライズ(MyAnnotation myAnnotation){ this.expectedLength = myAnnotation.value()。 } @Override パブリックブールのisValid(文字列S、ConstraintValidatorContext constraintValidatorContext){ リターンS ==ヌル|| s.length()== this.expectedLength。 } }
これらのケースでは、ベストプラクティスは、プロパティがnullである必要が関心事の分離は、それを有効な(マークされ、なおisValid
メソッドをs == null
、これは追加の要件属性が使用されている場合、)@NotNull
注釈を。
パブリッククラスTopTalentData { @MyAnnotation(値= 10) @NotNull プライベート文字列名。 }
7.エラー7 :(まだ)は、XMLベースの設定
春の以前のバージョンでは、XMLが必要ですが、今ほとんどの設定は、Javaコードや注釈を介して行うことができますが、単に追加の不要な定型的なコードとしてXMLコンフィギュレーションを。
(とそれに付随するのGitHubリポジトリ)本明細書中にスキャンする最上位のパッケージディレクトリにしているので、春、春の豆が接続されるべきかを知るために設定するには、アノテーションを使用して@SpringBootApplication
文の複合ノートを作った次のように、:
@SpringBootApplication パブリッククラスのアプリケーション{ パブリック静的無効メイン(文字列[] args){ SpringApplication.run(Application.class、引数)。 } }
コンポジットコメント(あなたは春のドキュメントでより多くを学ぶことができます)春ヒントにパッケージ化するだけでは、ビーンを取得するためにスキャンする必要があります。我々の場合には、これはトップレベルのパッケージ(co.kukurin)を取得するために使用されることを意味します。
@Component
(TopTalentConverter
、MyAnnotationValidator
)@RestController
(TopTalentController
)@Repository
(TopTalentRepository
)@Service
(TopTalentService
)クラス
私たちは、追加している場合@Configuration
のようなコメントを、彼らは、Javaベースの構成を確認します。
8.エラー8:プロファイルを無視
サービス側の開発では、頻繁に遭遇する問題には、構成の異なる種類の、通常生産の構成と開発の設定を区別することです。テストからアプリケーションを展開するたびに切り替え、様々な構成が手動で入力する代わりにはなりませんが、より効果的な方法は、プロファイルを使用することです。
このような状況を考えてみましょう:あなたは、地域開発のためのインメモリ・データベースを使用して、本番環境でのMySQLデータベースを使用しています。基本的に、これはあなたが両方にアクセスするために、異なるURL、および(うまくいけば)別の資格情報を使用する必要があることを意味します。あなたはそれ二つの異なるプロファイルを行うことができます方法を見てみましょう:
8.1。APPLICATION.YAMLファイル
'DEV'に#セットのデフォルトのプロファイル spring.profiles.active:DEV #の本番データベースの詳細は spring.datasource.url: 'JDBCます。mysql:// localhostを:3306 / toptal' spring.datasource.username:ルート spring.datasource.passwordを:
8.2。アプリケーションDEV.YAMLファイル
spring.datasource.url: 'JDBC:H2:MEM:' spring.datasource.platform:H2
あなたがDEVにデフォルトの設定ファイルは、理にかなっているように、あなたは、コードを変更したときに誤って本番データベース上の任意の操作を実行したくないと仮定します。その後、サーバー上で、あなたが提供することができます-Dspring.profiles.active=prod
パラメータを上書きするために、手動でJVMの設定ファイルに。また、また、所望のデフォルトのプロファイルに、オペレーティング・システム環境変数。
9.エラーナイン:依存性の注入を受け入れることができません
春の依存性注入のすべてのオブジェクトをスキャンして一緒にすべての必要な構成を接続することができ、適切な手段を用いて、これは、関係をデカップリングするのに便利ですがまたによってクラス間のないタイト、テスト容易になりますこのような何かをするように結合されました:
パブリッククラスToptalentコントローラー{ 民間最終Toptalentサービス人材サービス。 公共Toptalentコントローラ(){ this.topTalentService Toptalent =新しいサービス()。 } }
私たちは私たちの春の接続をしてみましょう:
パブリッククラスToptalentコントローラー{ 民間最終Toptalentサービス人材サービス。 公共Toptalentコントローラ(Toptalentサービス人材サービス){ this.topTalentService =人材サービス。 } }
グーグルのMisko Heveryは、依存性注入の詳細な説明を話し、「なぜ」、それでは、実際にそれを見てみましょう使用する方法です。関心事の分離(よくある間違い#3)では、我々はサービスとコントローラクラスを作成しました。私たちがしたいとしTopTalentService
、正しい行動の前提の下でコントローラをテストします。私たちは、達成する代わりに、実際のサービスクラスの個別の設定を経由してシミュレートされたオブジェクトを挿入することができます。
@Configuration パブリッククラスSampleUnitTestConfig { @Bean 公共TopTalentService topTalentService(){ TopTalentService topTalentService = Mockito.mock(TopTalentService.class)。 。Mockito.when(topTalentService.getTopTalent())thenReturn( 。Stream.of( "メリー"、 "ジョエル")マップ(TopTalentData ::新).collect(Collectors.toList())); topTalentServiceを返します。 } }
その後、私たちは春を使用して伝えることができますSampleUnitTestConfig
モックオブジェクトを注入するために、その構成クラスとして:
@ContextConfiguration(クラス= {SampleUnitTestConfig.class})
その後、私たちは、豆はユニットテストに注入コンテキストコンフィギュレーションを使用することができます。
10.エラー10:テストの欠如、または不適切なテスト
ユニットテストの概念は長い間の周りされているが、多くの開発者は、(それが「必要」の時間でない場合は特に)、それを行うことを「忘れる」するかに見えるか、単に事実の後にそれを追加する。が、これはテストだけではなく、コードの正しさを検証する必要があるため、文書はまた、振る舞い方の異なるシナリオの下でプログラムする必要があり、明らかに望ましくありません。
Webサービスをテストするときの通信は通常、HTTP経由の春を呼び出すために必要とされるため、のみまれ「純粋な」ユニットテストは、DispatcherServlet
実際の受信時、および表示HttpServletRequest
何が起こるかの時間を(それを「統合」テスト、治療を作るために検証、シーケンス、など)。JavaのDSLを簡素化するRESTアシュアード、テストRESTサービスは、MockMVCに、非常にエレガントなソリューションを提供することが示されています。依存性注入と、次のコードスニペットを考えてみましょう:
@RunWith(SpringJUnit4Cla *** unner.class) @ContextConfiguration(クラス= { Application.class、 SampleUnitTestConfig.class }) パブリッククラスRestAssuredTestDemonstration { @Autowired プライベートTopTalentController topTalentController。 @Test 公共ボイドshouldGetMaryAndJoel()は例外をスロー{ //与え MockMvcRequestSpecification givenRestAssuredSpecification = RestAssuredMockMvc.given() .standaloneSetup(topTalentController)。 //とき MockMvcResponse応答= givenRestAssuredSpecification.when()( "/ toptal /取得")を取得。 //その後、 response.then()からstatusCode(200)。 response.then()体( "名前"、hasItems( "メリー"、 "ジョエル"))。 } }
SampleUnitTestConfig
クラスは、TopTalentService
アナログ・インプリメンテーションに接続されTopTalentController
、他のすべてのクラスは、スキャンアプリケーションパッケージ標準下位クラスパッケージディレクトリによって推論されます。RestAssuredMockMvc
ただ、軽量の環境を設定すること、および/toptal/get
エンドポイントの送信GET
要求を。