[SpringBoot] パッケージが異なるクラスを @Autowired する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

SpringBoot楽しいですよね。
クラスを作って@Componentを書いて、@Autowireでメンバーに注入する。なんて楽にかけるんだ!!と思っていたのですが、少しクラス数も多くなってきたのでパッケージを分けてみたら

org.springframework.beans.factory.NoSuchBeanDefinitionException

とエラーが返ってきたので回避方法を調べました。

環境

Spring Tool Suite 3.8.0

失敗したコード

うまくいかなかったコードがこんな感じです。かなり簡略化はしています。

├ com.example/
│  ├ SpringBootPracticeApplication
│  └ BatchProcessing
└ nextPackage/
   └ Hoge
package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

import nextPackage.Hoge;

@SpringBootApplication
@EnableScheduling
public class SpringBootPracticeApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootPracticeApplication.class, args);
	}
}

@EnableSchedulingと下の@Scheduledは定期実行するために必要なアノテーションになります。興味のある方はこちらをどうぞ!

package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import nextPackage.Hoge;

@Service
public class BatchProcessing {

	@Autowired
	Hoge hoge;
	
	@Scheduled(fixedDelay = 5000)
	public void fixedRate(){
		System.out.println(hoge.getHoge());
	}
}

ちゃんとimportもしているのになんで?

package nextPackage;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

@Component
public class Hoge {

	public String getHoge(){
		return "hoge";
	}
}

上のコードは単純に5秒おきにgetHogeで返される文字列を表示するだけのものです。

回避方法

答えとしては非常に簡単で

@Service
@ComponentScan("nextPackage")
public class BatchProcessing {

	@Autowired
	Hoge hoge;
	
	@Scheduled(fixedDelay = 5000)
	public void fixedRate(){
		System.out.println(hoge.getHoge());
	}
}

パッケージ外はScanしてないから明示的に指定する必要があるようですね。 @ComponentScan("")で指定することによって解決しました。

他にも@Autowiredするクラスを同一パッケージにする方法なんかもあるようです。

最後に

何通りか方法はあるようですが、パッケージ構成を変えてまでやるほどのことでもないので@ComponentScan("")を使うのが現実的ではないかなと思いました。

参考

https://teratail.com/questions/4532