はじめに
エキサイト株式会社 21卒 バックエンドエンジニアの山縣です。
Spring Bootでコンポーネント名が重複したときに、ConflictingBeanDefinitionException
が発生してエラーとなってしまいました。
その原因と解決策についてまとめました。
別のパッケージの同一のコンポーネント名
例としてItem
に関するItemService
について考えてみます。
ItemService
はWebとアプリで処理が少し異なるため、それぞれで実装します。
- WebのService
package com.sample.service.item; import org.springframework.stereotype.Service; @Service public class ItemServiceImpl implements ItemService { @Override public Item getItemByID(Long id) { /* 処理 */ } }
- アプリのService
package com.sample.app.service.item; import org.springframework.stereotype.Service; @Service public class ItemServiceImpl implements ItemService { @Override public Item getItemByID(Long id) { /* アプリ固有の処理 */ } }
上記2つは似ていますが、パッケージが異なることに注意が必要です。
通常、同一のクラス名であったとしても、パッケージが異なれば問題なく利用できます。
しかし、ConflictingBeanDefinitionException
が発生しコンパイルできずにエラーとなってしまいました。
ConflictingBeanDefinitionException: Annotation-specified bean name 'itemServiceImpl' for bean class [com.sample.app.service.item.ItemServiceImpl] conflicts with existing, non-compatible bean definition of same name and class [com.sample.service.item.ItemServiceImpl
原因
コンポーネントの場合、同一のクラス名が2つ以上存在するときにコンパイルできずにエラーとなってしまいます。
これは、Spring BootがDIするときに、どちらのBean名もItemServiceImpl
となってしまうからです。
そのため、どちらのSpring BootがどちらのItemServiceImpl
をDIしてよいのか判別できなかったため、ConflictingBeanDefinitionException
が発生してしまいました。
解決策1:コンポーネントのプレフィックスに文字列を付与する
一番単純でわかりやすいのはクラス名の前にApp
やAdmin
などの文字列を付与することです。
これで問題なくコンパイルおよび実行することができます。
package com.sample.app.service.item; import org.springframework.stereotype.Service; @Service public class AppItemServiceImpl implements AppItemService { @Override public void getItemByID(Long id) { /* アプリ固有の処理 */ } }
解決策2:アノテーションの引数に文字列を指定する
@Controller
や@Service
、@Repository
、@Component
などのアノテーションの引数に独自のBean名を定義します。
これにより、同じクラス名のコンポーネントが複数存在しても問題なく実行することができるようになります。
package com.sample.service.item; import org.springframework.stereotype.Service; @Service("AppItemServiceImpl") public class ItemServiceImpl implements ItemService { @Override public void getItemByID(Long id) { /* 処理 */ } }
このとき、アノテーションの引数には変数を渡すことができます。 これで管理するのもよいかもしれません(ただし面倒だとは思います)。
@Service(ComponentName.APP_SERVICE_IMPL)
おわりに
Spring Bootでコンポーネント名が重複してエラーになったときの原因と2つの解決策についてまとめました。
個人的には、命名規則を決めて、管理者用ならAdmin
を、アプリ用ならApp
をプレフィックスに付与したほうがよいのかなと考えています。
例えば、解決策2で運用したときにItemService
とIDEでファイル検索したときに、ファイルパスまで含めて見ないとたどり着けないため、少し不便に感じるからです。
ただ、どちらの実装方法も大きく異なることはないため、最終的には好みになりそうです。