Spring Bootでアプリを作ろうとしたところ、デフォルトで作られるSpring BootのメインクラスではHello Worldできるのに、
自分で別に新しく作ったコントローラクラスで、Hello Worldできないという謎事象が発生しました。
結論、初歩的なミスが原因だったのですが、自分の備忘録として置いておきます。
もくじ
・@SpringBootApplicationアノテーション
1.うまくいかなかった現象
@SpringBootApplication
@RestController
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
@RequestMapping(value="/hello", method=RequestMethod.GET)
public String hello() {
return "Hello World!";
}
→ローカルから"/hello"にアクセスすると、問題なく動く。
次に、同様の処理を行うクラス・メソッドを新規に作成(↓)し、挙動確認を行った。
@RestController
public class TestController {
@RequestMapping(value="/hello2", method=RequestMethod.GET)
public String test() {
return "Hello World";
}
→処理は全く同じはず!なのに、"/hello2"にアクセスしても、なぜかHello Worldできない…。
2.うまくいかなかった理由(結論)
対処法を一言でいうと、@SpringBootApplicationアノテーション付与クラスの配置を変更しました。
コンポーネントスキャン対象となるのは、
@SpringBootApplicationアノテーションの付与されたクラスの配下にあるパッケージのみ。
でも私は今回、
・@SpringBootApplicationアノテーション付与クラス(MyAppクラス)のパッケージ:com.app
・自作のコントローラクラス(TestController)のパッケージ:com.test
というパッケージ構成にしていました。
そのため、@SpringBootApplicationアノテーション付与クラスのパッケージ配下以外に配置していたTestControllerはスキャン対象ではなく、
コンテナにBeanとして登録されていませんでした。
@SpringBootApplicationアノテーション付与クラスを、"com"パッケージ配下に配置し直したことで、
com.test.TestControllerもスキャン対象となり、無事にHello Worldできたわけです。
3.詳細
上記、やたらと専門用語を書いてしまったので、自分の整理のためにもさらに詳細を記しておきます。
コンポーネントスキャンとは
Bean定義用のアノテーションが付与されたクラスをDIコンテナに登録すること。
Bean定義用のアノテーションとは、@Component, @Controller, @Serviceといったアノテーションのことです。
コンポーネントスキャンでは、Bean定義用のアノテーションが付与されたクラスを、
文字通り「スキャン」して探し、該当する各クラスをBeanとしてDIコンテナ管理下に置く処理を行います。
Springでは、@ComponentScanアノテーションが付与されているクラスを起点として、
そのクラスのパッケージ配下にある各クラスを「スキャン」する対象とします。
従って、今回私がやらかしたように、
スキャンしたい対象のクラスを、スキャン対象外の場所に配置してしまうと、
そもそもスキャンがなされず、Bean登録も行われなくなってしまいます。
(※ComponentScanの設定はXMLでも可能ですが、ここでは割愛します。)
@SpringBootApplicationアノテーション
既にお気づきかもしれませんが、今回私は、@ComponentScanアノテーションは使用していません。
Spring Bootリファレンスによると、@SpringBootApplicationアノテーションは、
@ComponentScanアノテーションを含む3つのアノテーションの役割を1つで担っています。
なので、@SpringBootApplicationアノテーションを@ComponentScanで置き換えて考えると、
@SpringBootApplicationアノテーションが付与されているクラスを起点として、
そのクラスのパッケージ配下にある各クラスを「スキャン」すると言えます。
4.まとめ(結局、どうすればよかったのか)
今までの話をまとめると、こんな感じです。
この例であれば、MyAppクラスを、comパッケージ直下に置くと、全クラスがスキャンの対象となります。
要は、スキャン起点のクラスよりも下に、
スキャンしたいクラスを配置する構成にしないとだめだった、ということです。
5.おわりに
「何となく」でアノテーションを使っていると、思わぬところでコケてしまうことを身をもって実感しました…。
Java、IT業界で働くことにも少しずつ慣れてきた今、
これまで「分かったつもり」になってきたことを再確認していこうと思います。
※参考図書
株式会社NTTデータ『Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発』