質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Q&A

解決済

1回答

1406閲覧

引数が渡ってきている部分がどこか特定したい

s-lll

総合スコア7

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

0グッド

0クリップ

投稿2020/06/05 08:49

編集2020/06/05 08:57

前提・実現したいこと

SpringBootインストラクション内のConsumingRestApplication.javaのコードで分からない部分があります。runメソッドのreturnの後に記載されている、argsはラムダ式の引数に該当すると思われますが、どこから、代入されているのでしょうか?
アドバイスなど頂けましたら幸いです。

該当のソースコード

package com.example.consumingrest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class ConsumingRestApplication { private static final Logger log = LoggerFactory.getLogger(ConsumingRestApplication.class); public static void main(String[] args) { SpringApplication.run(ConsumingRestApplication.class, args); } @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder.build(); } @Bean public CommandLineRunner run(RestTemplate restTemplate) throws Exception { return args -> { Quote quote = restTemplate.getForObject( "https://gturnquist-quoters.cfapps.io/api/random", Quote.class); log.info(quote.toString()); }; } }

試したこと

デバッガーで処理を追ってみたのですが、// Warning: No line numbers available in class fileと表示され処理が途中から追えませんでした。

補足情報(FW/ツールのバージョンなど)

SpringBoot2.3

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

args配列にはアプリケーション起動時に指定された引数が格納されています。
つまり下記のように引数を与えて実行した場合

java -jar ./path/to/consuming-rest-0.0.1-SNAPSHOT.jar aaa bbb ccc

args配列には3つの文字列が格納されています。

[aaa, bbb, ccc]

試しに以下のようにlogを出力するコードを追加してみると(1),(2)の順番でログに出力されていると思います。

java

1public static void main(String[] args) { 2 // (1) 3 log.info(Arrays.toString(args)); 4 SpringApplication.run(ConsumingRestApplication.class, args); 5}

java

1return args -> { 2 // (2) 3 log.info(Arrays.toString(args)); 4 Quote quote = restTemplate.getForObject( 5 "https://gturnquist-quoters.cfapps.io/api/random", Quote.class); 6 log.info(quote.toString()); 7};

Spring Bootに限らず、Javaのプログラムは引数を与えるとmainメソッドのargsで受け取ります。
その引数をSpring Bootアプリケーション内で利用するために、さらに下記のようにして持ちまわしています。

java

1// (3) アプリケーション実行時の引数を持ちまわす 2SpringApplication.run(ConsumingRestApplication.class, args);

それを確認するには、下記のようにmainメソッド内で別の配列を定義して実行してみます。すると(2)の部分ではこのnewArgs配列の値が出力されたと思います。

java

1String[] newArgs = {"ddd", "eee", "fff"}; 2SpringApplication.run(ConsumingRestApplication.class, newArgs);

argsはラムダ式の引数に該当すると思われますが

蛇足ですが、この部分は従来の書き方をすると以下のようになります。
このCommandLineRunnerインターフェースを実装したインスタンスのrunメソッドには、Spring Bootによって(3)の部分で指定したargsが渡されます。

java

1return new CommandLineRunner() { 2 @Override 3 public void run(String... args) throws Exception { 4 Quote quote = restTemplate.getForObject( 5 "https://gturnquist-quoters.cfapps.io/api/random", Quote.class); 6 log.info(quote.toString()); 7 } 8};

2020/06/06 追記

一点のみどうしても疑問があるのですが、元コードの「run」メソッドの引数「args」はメソッド内で一度も使用されていないにも関わらず、記載されているのは何故

元コードというのは下記のことだと思いますが、端的に言うと引数argsが記載されているのはCommandLineRunnerインターフェースを実装しているからであり、引数を使用していないのはサンプルコード上必要がなかったから、ということではないでしょうか?

java

1return args -> { 2 Quote quote = restTemplate.getForObject( 3 "https://gturnquist-quoters.cfapps.io/api/random", Quote.class); 4 log.info(quote.toString()); 5};

以下はCommandLineRunnerインターフェースのコードです。このインターフェースを実装するということはvoid run(String... args) throws Exceptionをオーバーライドする必要があるということです。

java

1@FunctionalInterface 2public interface CommandLineRunner { 3 4 /** 5 * Callback used to run the bean. 6 * @param args incoming main method arguments 7 * @throws Exception on error 8 */ 9 void run(String... args) throws Exception; 10 11}

このインターフェースを実装したクラスのコードは例えば以下のようになります。このようにrunメソッドをオーバーライドして、Springアプリケーションの起動時に実行したい処理を記述します。

java

1@Component 2public class CustomRunner implements CommandLineRunner { 3 private static final Logger log = LoggerFactory.getLogger(CustomRunner.class); 4 5 private final RestTemplate restTemplate; 6 7 public CustomRunner(RestTemplate restTemplate) { 8 this.restTemplate = restTemplate; 9 } 10 11 @Override 12 public void run(String... args) throws Exception { 13 log.info(Arrays.toString(args)); 14 Quote quote = restTemplate.getForObject( 15 "https://gturnquist-quoters.cfapps.io/api/random", Quote.class); 16 log.info("2nd : {}", quote.toString()); 17 } 18 19}

runメソッドの引数を使う必要がないからといって以下のようにオーバーライドしてもコンパイルエラーになります。なぜならこれではCommandLineRunnerインターフェースを実装していることにならないからです。

java

1@Override 2public void run() throws Exception { 3 4 // 省略 5 6}

ちなみに、引数を使うように実装するのであれば、以下のようにすることもできます。

サンプルコードで使用しているAPIのhttps://gturnquist-quoters.cfapps.io/api/randomは、randomの部分を任意の数値に変えると、それがID指定になります。
たとえば、https://gturnquist-quoters.cfapps.io/api/10でリクエストすると、以下のjsonがレスポンスされます。

json

1{ 2 "type":"success", 3 "value":{ 4 "id":10, 5 "quote":"Really loving Spring Boot, makes stand alone Spring apps easy." 6 } 7}

このように実装すると(引数のチェックをしていないので例は悪いですが)、アプリケーション実行時の引数にIDを指定することができるようになります。

java

1return args -> { 2 String id = args[0]; 3 Quote quote = restTemplate.getForObject( 4 "https://gturnquist-quoters.cfapps.io/api/" + id, Quote.class); 5 log.info(quote.toString()); 6};

2020/06/07 追記

「Quote quote = restTemplate.getForObject(」こちらの行から読み取れるに、「Quote」型の変数を返しているということではないのでしょうか...?

すでに回答に記載していますがCommandLineRunnerインターフェースのrunメソッドのシグネチャは以下の通りです。
このように戻り値の型がvoidとなっているのでrunメソッドは何も返しません。argsという文字列の配列を引数に取り(それを使うかどうかは別として)実装した処理を実行する(Springフレームワークが)だけです。

java

1void run(String... args) throws Exception;

なので

「Quote」型の変数を返している

ということではありません。

以下が参考にされているページのオリジナルコードですが、このrunメソッド内でreturnしているのはラムダ式で書かれたCommandLineRunnerインターフェースを実装したクラス(匿名クラス、無名クラスとも)のインスタンスです。

java

1@Bean 2public CommandLineRunner run(RestTemplate restTemplate) throws Exception { 3 4 return args -> { 5 Quote quote = restTemplate.getForObject( 6 "https://gturnquist-quoters.cfapps.io/api/random", Quote.class); 7 log.info(quote.toString()); 8 }; 9 10}

上記の書き方を少し冗長化すると下記のようになります。returnはCommandLineRunner型の変数runnerを返していることが分かりやすくなったかとおもいます。
ラムダ式について少し触れておくと、args -> { ... }は、argsがメソッドの引数で、-> { ... }がメソッドの本体ということになります。

java

1@Bean 2public CommandLineRunner run(RestTemplate restTemplate) throws Exception { 3 4 CommandLineRunner runner; 5 6 runner = args -> { 7 Quote quote = restTemplate.getForObject( 8 "https://gturnquist-quoters.cfapps.io/api/random", Quote.class); 9 log.info(quote.toString()); 10 }; 11 12 return runner; 13}

上記の書き方をラムダ式を使わずに記述すると下記のようになります。
これはいわゆるラムダ式導入以前の匿名クラス(無名クラス)を利用している書き方です。runner変数には匿名クラスのインスタンスが代入され、それがreturnされています。
さらにオーバーライドしたrunメソッドが何もreturnしていないことがわかると思います。

java

1@Bean 2public CommandLineRunner run(RestTemplate restTemplate) throws Exception { 3 4 CommandLineRunner runner = new CommandLineRunner() { 5 @Override 6 public void run(String... args) throws Exception { 7 Quote quote = restTemplate.getForObject( 8 "https://gturnquist-quoters.cfapps.io/api/random", Quote.class); 9 log.info(quote.toString()); 10 } 11 }; 12 13 return runner; 14}

上記の書き方は匿名クラスを使った例ですが、下記のように書くこともできます。

java

1public class CustomRunner implements CommandLineRunner { 2 private static final Logger log = LoggerFactory.getLogger(CustomRunner.class); 3 4 private final RestTemplate restTemplate; 5 6 public CustomRunner(RestTemplate restTemplate) { 7 this.restTemplate = restTemplate; 8 } 9 10 @Override 11 public void run(String... args) throws Exception { 12 Quote quote = restTemplate.getForObject( 13 "https://gturnquist-quoters.cfapps.io/api/random", Quote.class); 14 log.info(quote.toString()); 15 } 16 17}

これでCommandLineRunnerインターフェースを実装したクラスのインスタンスを返していることがわかるでしょうか?

java

1@Bean 2public CommandLineRunner run(RestTemplate restTemplate) throws Exception { 3 4 CommandLineRunner runner = new CustomRunner(restTemplate); 5 6 return runner; 7}

すでに説明したと思いますが、returnしたインスタンスのrunメソッドは、アプリケーション起動時にSpringフレームワークによって実行されます。

投稿2020/06/05 10:36

編集2020/06/07 12:35
rubytomato

総合スコア1752

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

s-lll

2020/06/06 02:15 編集

ご丁寧にご回答頂きましてありがとうございます。 何故「args」が使いまわせるか、非常に分かりやすく、理解することが出来ました。 しかし、一点のみどうしても疑問があるのですが、元コードの「run」メソッドの引数「args」はメソッド内で一度も使用されていないにも関わらず、記載されているのは何故だとお考えになられますでしょうか? 重ね重ねのご質問となり、恐縮ですが、ご返信頂けましたら幸いです。
rubytomato

2020/06/06 11:42

回答欄に追記しましたのでご確認ください。
s-lll

2020/06/07 01:20 編集

追記頂きありがとうございます。 大変ご丁寧、ご親切にご回答頂いているのに、申し訳ないのですが、あと一歩の所でどうしても分からない部分がございます... 「CommandLineRunner」の「run」メソッドを引数を指定し実装する必要がある、といった部分までは理解することが出来たのですが、そうだとした場合、「 return args -> { 」こちらの部分がなくとも、以下のような形でもインターフェースは実装されるのではないか、と疑問に思ってしまいました... 「  public CommandLineRunner run(RestTemplate restTemplate) throws Exception { Quote quote = restTemplate.getForObject( "https://gturnquist-quoters.cfapps.io/api/random", Quote.class); log.info(quote.toString()) } 」 こちらのような形では、良くありませんでしょうか...? 本当に何度も申し訳ありません。
rubytomato

2020/06/07 12:37 編集

確認したいのですが、以下のConsumingRestApplicationクラスのrunメソッドは、CommandLineRunnerインターフェースのrunメソッドの実装だと認識されているのでしょうか? @Bean public CommandLineRunner run(RestTemplate restTemplate) throws Exception { // 省略 } だとすれば、それは違います。このrunメソッドはCommandLineRunnerインタフェースのrunメソッドの実装ではありません。たまたまrunという名前が付いているだけのメソッドです。(またシグネチャも違っています) 試しにrun以外の名前に変えて実行してみてください。 仮にanythingというメソッド名に変えても実行されます。このメソッドには@Beanアノテーションが付いていますが、このアノテーションが付いているとSpringアプリケーションの起動時に自動的に呼び出され、メソッドの戻り値のインスタンス(この例ではCommandLineRunnerインターフェースを実装した)がDIコンテナに登録されます。 この後は既に説明した通り、DIコンテナに登録されたCommandLineRunnerインタフェースを実装したインスタンス(Bean)は、あるタイミングで順次実行されます。(これもSpringによって) @Bean public CommandLineRunner anything(RestTemplate restTemplate) throws Exception { // 省略 }
s-lll

2020/06/07 08:05 編集

ご返信ありがとうございます。 >CommandLineRunnerインターフェースのrunメソッドの実装だ認識されているのでしょうか? はい。こちら勘違いしてしまっておりました。どうも、ようやくですが、自分が理解出来ていない部分がわかってきた気がいたします... >メソッドの戻り値のインスタンス(この例ではCommandLineRunnerインターフェースを実装した)がDIコンテナに登録されます。 こちらなのですが、「メソッドの戻り値のインスタンス」は「CommandLineRunnerのインスタンス」ではなく、「Quote quote = restTemplate.getForObject(」こちらの行から読み取れるに、「Quote」型の変数を返しているということではないのでしょうか...? 重ね重ね、申し訳ありません。
rubytomato

2020/06/07 12:39

回答欄に追記しましたのでご確認ください。 ちょっと確認したいのですが、ご質問の主旨はSpringフレームワークについてでしょうか?それともラムダ式の書き方についてでしょうか?
s-lll

2020/06/08 14:31 編集

大変ご丁寧に教えて頂きまして、ありがとうございます。 追記の方、読ませて頂きました。 >ご質問の主旨はSpringフレームワークについてでしょうか?それともラムダ式の書き方についてでしょうか? こちらなのですが、最初はSpringフレームワークの処理の流れが分からずご質問させて頂いたのですが、途中から、ラムダ式についても分からなくなってしまいまして... 参考書も読んでみたのですが、どうしても分からない状況に陥ってしまいました... >これはいわゆるラムダ式導入以前の匿名クラス(無名クラス)を利用している書き方です こちらなのですが、匿名クラスにも書き換えてくださいましたおかげで、抽象メソッドのCommandLineRunnerをオーバーライドしているということが理解できました。 そもそも、単純に自分の認識に問題があっただけであるかとは思うのですが、ラムダ式について学習している際に、メソッドの引数に、ラムダ式を組み込み、抽象メソッドを一つだけ持つ「関数型インターフェース」をオーバーライドするといった部分は理解できたのですが、「インスタンスが作成される」といった部分まで、分かっておりませんでした。 質問に記載させて頂きましたコードでは、「 return args -> { 」となっていますが、変数などに、ラムダ式で無名クラスのインスタンスを代入する場合は、「CommandLineRunner a = args->{ }」などと左側の代入先から、ラムダ式のクラスを特定できますが、メソッドの場合、ラムダ式の部分ではなんの無名クラスのインスタンスを返そうとするのか、どのように特定しているのかが分からず、混乱してしまっておりました... 単純に、「public CommandLineRunner run(RestTemplate restTemplate) 」こちらから、「CommandLineRunner 」であると読み取れるということに気がつくのに、時間がかかってしまいました。 本当に、長々とご丁寧にご説明下さり、感謝しかございません。 本当に、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問