Works by

Ren's blog

@rennnosuke_rk 技術ブログです

【Spring】Spring BootでのデフォルトのConponent Scan

f:id:rennnosukesann:20181220183020p:plain:w300

Spring Initializrで生成したばかりのSpring Bootプロジェクトからアプリを実行すると、Component Scanの対象となるパッケージがデフォルトでトップレベルから実行されていることがわかります。

これは、下記の様な @SpringBootApplication アノテーションが付与されたクラスがトップレベルのパッケージに配置されているためです。

// PlaygroundApplication.kt

@SpringBootApplication
class PlaygroundApplication

fun main(args: Array<String>) {
    runApplication<PlaygroundApplication>(*args)
}

@SpringBootApplication アノテーションには @ComponentScan アノテーションが付与されており、その引数には basePackages 指定がありません。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),  // basePackage指定なし
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
@ConfigurationPropertiesScan
public @interface SpringBootApplication {
...
}

basePackages 指定のない @ComponentScan は、アノテーションを付与したクラスが属するパッケージ階層以下をスキャン対象とします。
そのため、例えば PlaygroundApplication クラス(kotlinであればこのクラスを含む.ktファイル)が下図のように配置されている場合、そのComponent Scan対象は PlaygroundApplication が属するパッケージ階層以下となります。

com
└── springapp
    ├── PlaygroundApplication.kt // PlaygroundApplicationクラスを定義
    ├── ServletInitializer.kt
    ├── application
    │   └── config
    │       └── AppConfig.kt
    └── domain
        ├── model
...

この理由から、 PlaygroundApplication.kt を別のパッケージ階層に移動すると、そのパッケージ配下のコンポーネントだけがスキャン対象となり、上位のパッケージ及び別階層のパッケージのスキャンは実施されません。

com
└── springapp
    ├── ServletInitializer.kt
    ├── app
    │   └── PlaygroundApplication.kt
    ├── application
    │   └── config
    │       └── AppConfig.kt // スキャンされない!
    └── domain
        ├── model
***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.springapp.domain.service.UserService required a bean of type 'com.springapp.domain.repository.UserRepository' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)
...

なお @SpringBootApplication で明示的にスキャン対象を指定する場合、 scanBasePackages プロパティを使用します。

@SpringBootApplication(scanBasePackages={"com"})
class PlaygroundApplication

fun main(args: Array<String>) {
    runApplication<PlaygroundApplication>(*args)
}

参考文献

docs.spring.io