Closed22
初めてのエンタープライズJavaアプリケーション開発
Python遣いが急遽Javaアプリケーションを作らないといけなくなったので、環境構築から単体テストまで1週間で対応した話。
書きたいこと
- 自分が開発に至るまでにどういうことを調べて解決していったか。
- 後から振り返って、こうしておけばよかったと思うこと。
- 次回同じような開発が必要になったときに効率的に進めるための方法。
- Javaアプリケーション開発を行うためのガイド作成(自分が参照する用)
- 今回は直面しなかったけど開発する上でのリスクとして考えられるものとその対処法。
- それぞれの作業を進めていく上での手順書。
- 半年後の自分が見たときにいろんな調査をスキップできるような資料にする。
作り方
- 自分が直面した問題をベースに、その中で調査した情報をまとめ直していく。
- 実例をもとに解説を入れていく。
- フローチャートを作成して、必要なものを選択して再利用できるようにする。
- 作業の依存関係を明確にして、飛ばしていいものとダメなものを可視化する。
Javaアプリケーションを開発するために使用した環境やツール、考え方など
- Javaアプリケーションプロジェクトの構成(パッケージ、フォルダ構成、クラス分割)
- ログ出力
- Propertyファイルへの設定の外だし
- データ構造体(setter, getter)
- Gradleプロジェクト(開発中の実行、テスト、ビルド)
- Dockerを使ったSFTP通信の単体テスト
- JUnit5を使ったテスト(アノテーション、アサーション、マッチャー)
- Mockiteによるモック化
- Jacocoを使ったカバレッジレポート出力
- Eclipse
- JDK
- ライブラリ管理(Maven, Gradleリポジトリ)
- 運用を想定した作り込み
1. Javaアプリケーションプロジェクトの構成(パッケージ、フォルダ構成、クラス分割)
- パッケージはクラスファイルの先頭に宣言する
- パッケージの階層はJavaプロジェクトのディレクトリ構成に一致する
- パッケージは他のJavaアプリケーションからimportする際にも指定する。
- 単純な処理であっても、一つのクラスに全ての処理を書くのではなく、複数の適切な単位に分割してクラスを構成していく。
- クラスを分割したら、その役割に応じてディレクトリも分割する。例えば、DAOやログ出力などの役割によってディレクトリを分けてアプリケーションがどんな機能を持っているのかを可視化しておくと保守性が上がる。
- クラスを分割するときにはJUnitの単体テストのやりやすさを考慮したほうがいい。
- クラス設計をするときには、単体テストがやりやすくなるようにしたほうが後から楽になる。例えば、他のクラスをインスタンスかするときはモッククラスを注入できるような作りにしておくと、テストがやりやすい。
- 1から自分で設計するよりも、自分よりもレベルの高いエンジニアが作った構成などを参考に真似しながら作ったほうが短時間でクオリティの高いコードが作成できる。
2. ログ出力
- ログは奥が深く、プログラミングという観点とは少し異なる側面になる。
- ログを設計する際には、それを運用する時のイメージが必要。監視ソフトでも人による監視でもアプリケーションで何が起きたのかを正しく伝えるという要件を満たすことが必要。
- つまり、アプリケーションに何かあったときにそれを見た人が適切な行動を起こして、問題を解決できることが良いログである。
- ログ出力のためのライブラリーはあるので、それを使うのはもちろんのことだが、そこで出力するメッセージをわかりやすくすることもそれ以上に重要。
3. Propertyファイルへの設定の外だし
- 環境依存の設定値は、外出しする
- プロパティファイルを読み込む処理は別クラスとして分けたほうがいい。
- クラスはインスタンス化するのではなく、staticクラスで利用する。
4. データ構造体(setter, getter)
- lombokを使って冗長なコードを排除して、アノテーションを使ってsetter/getterを定義可能
- フィールド追加時のメンテナンスコストも削減
Lombok
5.Gradleプロジェクト(開発中の実行、テスト、ビルド)
- 今回gradleを使ったプロジェクトを作成した
- gradleはjavaプロジェクトの実行やテスト、ビルドを実行するための管理ツール。importするライブラリの管理も出来る。
- junitテストのレポート出力も出来る。
- 設定ファイルによって挙動を変えることが出来るが、それを理解するのが少し大変。
- Mavenリポジトリを利用可能で、Mavenからの以降も用意
- javaアプリケーションのビルドではjarファイルの出力だけではなく、コマンドラインから実行できるコマンドを作成可能。
- Eclipseに依存しない開発ができるので、環境を選ばない開発が出来る。
6. Dockerを使ったSFTP通信の単体テスト
- sftp処理を行う部分も単体テストで確認したかったので、dockerを使ってローカルで出来るようにしました。
- sftpじゃなくても、サーバーとの通信を行う処理の単体テストではこのパターンを使えるはず。
- testcontainerを使った方法も出てきたが、うまく行かなかったので今回は見送りました。
- メモリを結構消費するのでメモリ不足には要注意です。
7. JUnit5を使ったテスト(アノテーション、アサーション、マッチャー)
- テストクラスはクラスごとに作成する。
- 結果が正しいかをアサーションを使って判定する(assertEquals, assertThrows)
- メソッドの出力値と期待値を比較する。void関数の場合は、ログ出力値などを確認する方法もある。
- 各テストで共通する前処理、後処理は@BeforeEach、@AfterEachなどを使ってまとめて記述する。
- Hamcrestのマッチャー関数を使うと、複雑な条件で比較することができる。
- テストを書きながら、ソースコードの不備に気づいて修正することもある。
- 一度テストを書いてしまうとリファクタリングをする際にデグレが起きていないことをすぐに確認できるため安心感につながり、自信を持って次のテストに進める。
8.Mockiteによるモック化
- 単体テストの際に、他のクラスを呼び出す際にモック化して任意の結果を返すことができる。
- ただし、元々のクラスが想定していない結果を返すとエラーになる(元々は文字列を返すクラスなのに、数値を返すようにするなど)
- モック化したクラスを注入する場合には、制約があり、インスタンス化の仕方によると注入できない。
- クラスの設計をする際に、単体テストのことも念頭に置いてインスタンス化などをしたほうが後から楽になる(テストドリブン開発に近い)
9.Jacocoを使ったカバレッジレポート出力
- JUnitを使った単体テストでカバーできていない処理がないかをチェックして、レポートとして出力してくれる
- C0とC1のカバレッジを測定できる。
- 単体テストケースの網羅性に問題がないかどうかを確認する一つの観点となる(カバレッジが100%だからと言ってテストケースが充足していると言えるわけではない)
10.Eclipse
- Java開発を行う時のスタンダードIDE。
- 会社標準になっていたから使ったけど、いまいち使い方がわからないところもあった。
- できればVScodeを使いたかった。
- コンパイルするJavaのバージョンとか、そういうのとかを設定する必要がある。
- Intellijも人気らしいが有料でいつも使えるかどうかは微妙。。。
- JDKバージョンの設定
- コメントの設定
- ビルドの設定
- EclipseでやったことをVScodeでやりたい
11.JDK
- いろんなバージョンがあり、どのバージョンを使っているのかを意識する必要がある。
- OracleJDKやOpenJDKなどリリースしているベンダーによっても異なる。
- JDKによる互換性を確認してみて、違いを調べておいたほうがいいかも。
- JDKの選定を任されたときにどういう観点で調べるか?
- 別のJDKを使う必要がある場合に適切なJDKを選べるか?
12.ライブラリ管理(Maven, Gradleリポジトリ)
- Javaでimportするライブラリのバージョンを管理する。
- 公開されているリポジトリを使う場合と、プライベートで作成するリポジトリを使う場合がある。
- クラウドでもリポジトリを作成・管理するサービスがあった気がする。
- 今まで、どこからimportしているかわからなかったから、図解してまとめておきたい。
gradleを使った実行可能jarファイル作成時のクラスファイルパスを変更したい
gradleを使ってjava applicationを出力する方法(公式マニュアル)
コマンド実行できるようにassemble
するにはThe Application Plugin
を使う。
createStartScriptを使って、アプリケーション名などをカスタマイズ可能
実行可能jarファイルを作成したときに/binフォルダに作成されるスクリプトはStartScript
と呼ぶらしく、build.gradleファイルでプロパティを変更可能。
外部ライブラリjarを使うときの注意点
gradleの日本版マニュアル
13.運用を想定した作り込み
- 作って本番環境で動かす際に必要な機能がある。
- ログ出力や適切な例外処理など。
- いわゆる機能要件を満たすものというよりも、保守性などの非機能要件を満たすために作り込むもの。
- 運用を見越した作り込みをする必要がある。
このスクラップは2023/01/11にクローズされました