Spring Bootでコンポーネントスキャンできなかった初歩的なミス | 若手エンジニアのブログ

若手エンジニアのブログ

文系出身の若手女子エンジニアによる技術ブログ。
日々の経験や学びをアウトプットするためにブログを書いています。
バックエンド(Java+SpringFramework)を経てインフラエンジニアになりました。
今は育休中につき、本で勉強したことを中心にアウトプットしています。

Spring Bootでアプリを作ろうとしたところ、デフォルトで作られるSpring BootのメインクラスではHello Worldできるのに、

自分で別に新しく作ったコントローラクラスで、Hello Worldできないという謎事象が発生しました。

 

結論、初歩的なミスが原因だったのですが、自分の備忘録として置いておきます。

 

もくじ

1.うまくいかなかった現象

2.うまくいかなかった理由(結論)

3.詳細

  ・コンポーネントスキャンとは

  ・@SpringBootApplicationアノテーション

4.まとめ(結局、どうすればよかったのか)

5.おわりに

 

1.うまくいかなかった現象

Spring Bootアプリケーションを新規に作成。
デフォルトで作られる「@SpringBootApplication」の付与されたクラスをコントローラとし、
Hello Worldの文字列を返すメソッドを作成(↓)。

@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アプリケーション開発