はじめに
こんにちは!macでSTSの環境構築を行った者です。
今回は「HelloWorldファイルとHelloWorldのテストコードファイルを作成し、jarに保存してmacで実行してみる」ところまで実施しました。
前回同様、Spring徹底入門を参考に進めました。簡単な手順ですが、バージョンのせいかJUnitで実行する辺りやjarにする辺りで思いの外詰まったので記事にしました。
ぜひ俺の屍を越えていってください。
使用環境とバージョン
- macOS Catalina
- jdk14.0.1
- JUnit5
- Maven 3.6.3_1
- STS 4.6.1
- Spring Boot 2.3
Spring Initializrでプロジェクトの雛形を作る
まずSpring Initializrでプロジェクトの雛形を作成します。
Spring InitializrはSpring Bootが提供しているWebサービスです。必要事項をチェックするだけで、Spring Bootプロジェクト作成の基礎となるディレクトリがzip形式でダウンロードできます。また、メインメソッドの雛形があるDemoApplication.javaと、テストコードの雛形があるDemoApplicationTests.javaが、あらかじめ適切なディレクトリ配下に配置されています。
つまり、ダウンロードするだけで今回の作成物の8割は完成します笑
前回Mavenで環境構築を行ったので、ProjectはMavenを選びます。
その他、言語やSpringBootのバージョン、Javaのバージョンなど、自分に合ったものを選択し、GENERATEボタンをクリックすると、demo.zipのダウンロードが始まります。
demo.zipを解凍すると、以下のようなMavenプロジェクトが既に作成されています。すごい!めちゃめちゃ簡単じゃないですか!!
解凍したdemoファイルは、環境構築した際に作成したworkspaceディレクトリ配下に移動させておくと進めやすいです。
STSを開き、File > Import > Existing Maven Projectsで、先ほど解凍したdemoフォルダを選択すれば、インポートを行えます。
自動的にビルドが走るので、Package Explorerが以下画像のようにパッケージ表示になるまでしばし待ちます。自動で以下のようにならなかったら、メニューバーからProject > Cleanを押下してください。
DemoApplication.javaの編集
ダウンロードしたばかりのDemoApplication.javaの中身は以下のようになっているはずです。
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
SpringはMVCモデルのフレームワークです。HelloWorldプロジェクトは最低限の実装で良いので、クライアントからのリクエストを受け取ってレスポンスを返す1、Controller部分のみを実装していきたいと思います。
■DemoApplicationクラスに@Controller
をつける
言わずもがなですが、@Controller
アノテーションをつければ、そのクラスをコントローラーとすることができます。
■@RequestMapping
、@ResponseBody
を付与したメソッドhelloWorld()を作成する
@RequestMapping
と@ResponseBody
、二つのアノテーションを付与することで、戻り値をレスポンスのコンテンツとすることができます。
参考:Spring MVCのコントローラでの戻り値いろいろ
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@SpringBootApplication
@Controller
public class DemoApplication {
@RequestMapping("/")
@ResponseBody
String helloWorld() {
return "Hello World!";
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
保存して、DemoApplicationを右クリック > Run As > Java Applicationと選択してメインメソッドを実行すると、http://localhost:8080 でハロワできるはずです!やったね!
上記以外の書き方でも、コントローラーのアノテーションを@Controller
から@RestController
に変更すれば、helloWorld()のアノテーションは@RequestMapping
のみで動きます。
ただし、アノテーションの組み合わせを間違えるとエラーが発生します(やらかしました)。どうやらHTTPリクエストがアプリケーション側で受け取れていないとこの画面になるようで、DemoApplication.javaをダウンロードしたまんまで実行しても同じ画面になるそうです。
DemoApplicationTests.javaの編集
DemoApplication.javaの実装に合わせて、テストコードも編集していきます。以下がダウンロードしたまんまのテストコードです。これに手を加えていきます。
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoApplicationTests {
@Test
void contextLoads() {
}
}
■@SpringApplicationConfigration
、@WebIntegrationTest
の代わりに@SpringBootTest
を付与する
前者2つのアノテーションはSpring Boot 1.4から非推奨になったため、@SpringBootTest
に置き換えます。
@SpringBootTest
は@SpringBootApplication
がついているクラスをテスト用のコンフィグレーションクラスとして認識します。
■@RunWith(SpringRunner.class)
の代わりに@ExtendWith(SpringExtension.class)
を使用する
SpringRunnerはJUnit4のものなので、JUnit5で使用するとその後のjar作成時にビルドがうまく通らなくなりました…
JUnit5では@RunWith
アノテーション自体が@ExtendWith
に置き換えられていたので、そちらに変更しました。JUnit5 @RunWith
■@LocalServerPort
でポート番号を取得する
Spring Boot 1.3以前では@Value(“${local.server.port}”)
で取得していたポート番号ですが、1.4以降@LocalServerPort
がショートカットとして追加されています。2.3だと@Value
でポート番号を取得しようとすると以下のエラーが発生して、テスト実行ができませんでした。。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.demo.DemoApplicationTests': Unsatisfied dependency expressed through field 'port';
nested exception is org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'int';
nested exception is java.lang.NumberFormatException: For input string: "${local.server.port"
■assertThatメソッドはorg.junit.Assert.assertThatではなくorg.hamcrest.MatcherAssert.assertThatを使用する
JUnitのassertThatは非推奨と出て、deprecatedを許容するアノテーション@SuppressWarnings(“deprecated”)
を付与してもうまくjarのビルドができませんでした…(上記以外が原因かもです)
代わりに、hamcrestのassertThatを使用しました。
最終的なテストコードは以下のようになりました。
package com.example.demo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoApplicationTests {
TestRestTemplate restTemplate = new TestRestTemplate();
@LocalServerPort
int port;
@Test
public void testHello() {
assertThat(restTemplate.getForObject("http://localhost:" + port, String.class), is("Hello World!"));
}
}
DemoApplicationTests.javaを右クリック > Run As > JUnit Testでテストコードが実行できます。JUnitパネルに以下のように表示されれば、テスト成功です!
上記テストコードかDemoApplication.javaのどちらかの"Hello World!"部分を別の言葉に変えて、ちゃんとテストが失敗するかも確認してみてください。
実行可能jarを作成する
ここまでの工程で、既にSpring プロジェクトのHelloWorldは出来上がっています。しかし、実際にjarにしてみると、コンパイルやビルドがうまくいかないことに気づけるので、やっておくと吉です!
jarを作成する手順を説明していきます。
①macのターミナルから、ワークスペース配下のdemoディレクトリまで移動する
cd ~/Users/xxx/workspace/demo
②実行可能jarを作成するMavenコマンドを実行する
./mvnw clean package
STS上ではHelloWorldもテストもうまく行っていたとしても、ここでエラーが出ることもあります(自分です)
自分はここで@RunWith
が使えないことを知りました。
うまくいった場合は、下図のようにBUILD SUCCESS
が表示されます。
③jarを実行する
無事にビルドが成功したら、demo/target 配下に「demo-0.0.1-SNAPSHOT.jar」ができているはずです。末尾にoriginalとついていない方を実行します。
java -jar /Users/xxx/workspace/demo/target/demo-0.0.1-SNAPSHOT.jar
以下のようにSpringが立ち上がれば成功です! http://localhost:8080 で同じようにHello World!が表示できます。
おわりに
冒頭に記した通り、Spring徹底入門を読みながら実施しましたが、ただのHelloWorldを作る際に、同じJavaやIDEでも、バージョンが違うだけでこんなに違うんだなと驚きました…バージョン大事と言われる理由が分かりました…
せっかく雛形を作成したので、次回は簡単にアプリを作成して、テストコードも書いてみたいと思います!
お読みいただきありがとうございました!
ここ違うかもよ〜、という箇所ありましたら、例の如く、そ〜っと教えてください…!
-
Spring MVCの細かな説明は省きますが、正確にはコントローラーの前にあるフロントコントローラーというサーブレットが、リクエストを受け取ってレスポンスを返しています。ここはフレームワークに任せるため、開発者はコントローラーのみ実装します。 ↩