Spring入門ガイド　第3回　RestTemplate で REST API の利用

今回はこれをやっていく。

https://spring.pleiades.io/guides/gs/consuming-rest

>Spring の RestTemplate を使用して、http://localhost:8080/api/random でランダムな Spring Boot の引用を取得するアプリケーションを作成します。

最初何言ってるかわからなかったけどどうやら外部のREST APIを使ったアプリをSpringBootで作るということらしい。

# 必要なもの
・約15分
　→準備している前提。
・任意のテキスト エディターまたは IDE
　→eclipse Version: 2022-12 (4.26.0)を使用します。
・Java 17以降
　→17を使います。
・Gradle 7.5 以降または Maven 3.5 以降
　→Gradle 8.7 を使用します。
・Spring入門ガイドが用意したREST APIの起動まで(作成するプログラムで使うので必要)
　→[GitHubから取得可能。](https://github.com/spring-guides/quoters/tree/master "Spring入門ガイドが用意したREST API")今回はソースを変えたりする気はないのでzip形式でダウンロードしてしまおうと思う。

# Spring入門ガイドが用意したREST APIの起動まで実施
GitHubからダウンロードしたzipを解凍したらeclipseでMavenプロジェクトをインポートする。
インポートできたら以下の手順でjarを作成する。

https://itsakura.com/eclipse-maven-execution#s4

作成したjarを実行すると以下のような出力が出る。
```
C:\Users\socce>java -jar "D:\Desktop\quoters-master\target\quoters-incorporated-0.0.1-SNAPSHOT.jar"

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.7.5)

2024-04-05 15:53:02.756  INFO 19192 --- [           main] o.s.q.QuotersIncorporatedApplication     : Starting QuotersIncorporatedApplication v0.0.1-SNAPSHOT using Java 17.0.10 on DESKTOP-AMS0JGI with PID 19192 (D:\Desktop\quoters-master\target\quoters-incorporated-0.0.1-SNAPSHOT.jar started by socce in C:\Users\socce)
2024-04-05 15:53:02.773  INFO 19192 --- [           main] o.s.q.QuotersIncorporatedApplication     : No active profile set, falling back to 1 default profile: "default"
2024-04-05 15:53:03.920  INFO 19192 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2024-04-05 15:53:04.012  INFO 19192 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 75 ms. Found 1 JPA repository interfaces.
2024-04-05 15:53:05.377  INFO 19192 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2024-04-05 15:53:05.395  INFO 19192 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-04-05 15:53:05.395  INFO 19192 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.68]
2024-04-05 15:53:05.709  INFO 19192 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-04-05 15:53:05.709  INFO 19192 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2839 ms
2024-04-05 15:53:06.008  INFO 19192 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-04-05 15:53:06.351  INFO 19192 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-04-05 15:53:06.442  INFO 19192 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-04-05 15:53:06.534  INFO 19192 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.6.12.Final
2024-04-05 15:53:06.881  INFO 19192 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2024-04-05 15:53:07.123  INFO 19192 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2024-04-05 15:53:08.064  INFO 19192 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2024-04-05 15:53:08.078  INFO 19192 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2024-04-05 15:53:08.642  WARN 19192 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2024-04-05 15:53:09.319  INFO 19192 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2024-04-05 15:53:09.340  INFO 19192 --- [           main] o.s.q.QuotersIncorporatedApplication     : Started QuotersIncorporatedApplication in 7.601 seconds (JVM running for 8.879)
2024-04-05 15:53:36.346  INFO 19192 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-04-05 15:53:36.347  INFO 19192 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-04-05 15:53:36.348  INFO 19192 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
```
この状態で[http://localhost:8080/api/random](http://localhost:8080/api/random)にブラウザでアクセスして以下のようにjsonが返ってくれば外部Rest APIの準備はOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626978/172ba8c6-cf62-65da-4eb4-1d6e9499fdcd.png)

# 上記を呼び出す側のSpringBootプロジェクトを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626978/b5b7b8f0-9612-e81a-941d-a03476a06c79.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626978/1574aa82-e46e-e95b-c675-1d95690ededa.png)

# 手順実施
また分からないことがあったら調べながら進めていく。

### build.gradleのdependenciesに後から追加した場合のeclipseでの変更の取り込み
今回は外部APIから返却されるJSONとJavaのオブジェクトのマッピングをJacksonというライブラリを使って実施する。しかしプロジェクトを作成する際に、依存関係に追加し忘れたので後からbuild.gradleのdependenciesに追加してあげた。ただ追加しただけだとeclipseでJacksonを認識することはなくこちらから再度build.gradleの情報を読み込みなおしてもらう必要があった。
やり方としては、`プロジェクトで右クリック＞Gradle＞Gradleプロジェクトのリフレッシュ`だけで良かった。
JacksonのGradle追加の方法は以下を参考に実施。

https://qiita.com/NagaJun/items/9099e721aeeb7e31e322

### `@Bean`
`@SpringBootApplication(@Configuration`)が付いているクラスで、クラスオブジェクトを生成して返却するようなメソッドに`@Bean`をつけておくと、アプリの起動時に自動でそのメソッドが呼び出され、オブジェクト化されてDIコンテナに追加される。この時オブジェクト生成に必要なオブジェクトすらもDIコンテナから自動で渡される。なので開発者がオブジェクト生成してDIコンテナに入れるような処理を記述する必要がない。
`@Bean`については以下参照。

https://spring.pleiades.io/spring-framework/reference/core/beans/java/bean-annotation.html

### CommandLineRunner
Springアプリを起動してすべてのBeanがDIコンテナに格納された後、DIコンテナにCommandLineRunnerのインスタンスがあれば実行される。複数あった場合は順番も定義できるので定義した順番で実行される。

https://spring.pleiades.io/spring-boot/docs/current/api/org/springframework/boot/SpringApplication.html

今回でいうと以下のラムダ式が実行されるのだが、CommandLineRunnerではなくラムダ式を渡しているのが不思議だったので調査。
```java
	@Bean
	@Profile("!test")
	public CommandLineRunner run(RestTemplate restTemplate) throws Exception {
		return args -> {
			Quote quote = restTemplate.getForObject(
					"http://localhost:8080/api/random", Quote.class);
			log.info(quote.toString());
		};
	}
```

CommandLineRunnerはインターフェースで`@FunctionalInterface`というアノテーションが付いている。

https://spring.pleiades.io/spring-boot/docs/current/api/org/springframework/boot/CommandLineRunner.html

このアノテーションをさらに確認してみると以下のような記述があった。
>Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.

Googleで和訳してみると
>関数インターフェイスのインスタンスは、ラムダ式、メソッド参照、またはコンストラクター参照を使用して作成できることに注意してください。

CommandLineRunnerはラムダ式でもインスタンスの作成ができると記載があるので今回のラムダ式はCommandLineRunnerを作成しているという風にとらえて良さそう。CommandLineRunnerにはrunメソッドしかないのでrunメソッドの型に合わせている。そう解釈すれば矛盾はないのでこれ以上の詮索はしない。

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/FunctionalInterface.html

# eclipseで実行してみる。
`src/main/resources/application.properties`に`server.port=8081`を追記して組み込みサーバの受付ポートを変更しておく。
それかそもそも今回はアプリ起動時に一回自動でCommandLineRunnerが実行されるだけなので組み込みサーバ自体が不要。※ユーザーからアクセスする手段を実装していない為。
その場合は以下のようにSpringApplicationをインスタンス化してWebApplicationTypeをNONEに設定すればよい。

```java
	public static void main(String[] args) {
		//SpringApplication.run(SpringBootGuide03Application.class, args);
		SpringApplication app = new SpringApplication(SpringBootGuide03Application.class);
		app.setWebApplicationType(WebApplicationType.NONE);
		app.run(args);
	}
```
https://tech.asoview.co.jp/entry/2020/03/17/100433

https://spring.pleiades.io/spring-boot/docs/current/reference/html/howto.html#howto.application.non-web-application

どちらのパターンで実行してもちゃんと結果は受け取れる。
eclipseではプロジェクトで右クリックした後、実行＞SpringBootアプリケーションで実行できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2626978/6ac2d160-0067-8946-669d-1b3ff2007777.png)

↓WebApplicationTypeをNONEで実行
```
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
[32m :: Spring Boot :: [39m              [2m (v3.2.4)[0;39m

[2m2024-04-05T21:42:29.569+09:00[0;39m [32m INFO[0;39m [35m20708[0;39m [2m---[0;39m [2m[SpringBootGuide03] [           main][0;39m [2m[0;39m[36mc.e.c.SpringBootGuide03Application      [0;39m [2m:[0;39m Starting SpringBootGuide03Application using Java 17.0.6 with PID 20708 (D:\Spring学習\workspace-sub\SpringBootGuide03\bin\main started by socce in D:\Spring学習\workspace-sub\SpringBootGuide03)
[2m2024-04-05T21:42:29.577+09:00[0;39m [32m INFO[0;39m [35m20708[0;39m [2m---[0;39m [2m[SpringBootGuide03] [           main][0;39m [2m[0;39m[36mc.e.c.SpringBootGuide03Application      [0;39m [2m:[0;39m No active profile set, falling back to 1 default profile: "default"
[2m2024-04-05T21:42:30.223+09:00[0;39m [32m INFO[0;39m [35m20708[0;39m [2m---[0;39m [2m[SpringBootGuide03] [           main][0;39m [2m[0;39m[36mc.e.c.SpringBootGuide03Application      [0;39m [2m:[0;39m Started SpringBootGuide03Application in 1.019 seconds (process running for 1.592)
[2m2024-04-05T21:42:32.061+09:00[0;39m [32m INFO[0;39m [35m20708[0;39m [2m---[0;39m [2m[SpringBootGuide03] [           main][0;39m [2m[0;39m[36m                                        [0;39m [2m:[0;39m Quote[type=success, value=Value[id=12, quote=@springboot with @springframework is pure productivity! Who said in #java one has to write double the code than in other langs? #newFavLib]]
```

# 感想
時間がかかった。わからないことが多いのでこのペースで行くと全部やりきるのに暇でも半年かかりそう。
すごくためにはなるけど画面操作でCRUDできるようになるのが最低限必要だと思うのでもう少し直接関係ありそうなところから広げていこうと思う。
外部のAPIを使う際にラッピングしてくれるRestTemplateはもしかしたら使うかもしれない。
CommandLineRunnerはアプリの初期処理として使うかもしれない。
けど後回しで良い機能ではあると思った。