Springで自動生成されるBean名をFQCNに変更する方法

# はじめに
デフォルトBean名をFQCNにするための方法について解説します。すでに同様の内容を解説したページがいくつかありますが、独自の`BeanNameGenerator`実装クラスを定義する内容のものが多く、標準で用意されている`BeanNameGenerator`実装クラスを利用する例や複数の設定方法を網羅している解説は無かったため、備忘録として残しておきます。

# 概要
SpringでアノテーションベースのBean定義を行う場合、特にBean名を明示しない限り、暗黙的に**クラス名をLower Camel Caseにしたもの**がBean名として与えられます。パッケージ名は無視されてしまうので、状況によってはBean名が重複し`ConflictingBeanDefinitionException`が発生してしまうことがあります。

```java
package com.example.demo.package1
@Service
public class UserService {
	/* Bean名:userService */
}
package com.example.demo.package2
@Service
public class UserService {
	/* Bean名:userService */
}
```

クラス名を変更したり、アノテーションにBean名を渡したりすれば回避できますが、クラス数が多いとその管理も面倒になります。

```java
package com.example.demo.package1
@Service("userService1")
public class UserService {
	/* Bean名:userService1 */
}
package com.example.demo.package2
@Service("userService2")
public class UserService {
	/* Bean名:userService2 */
}
```

デフォルトで与えられるBean名をパッケージ名まで含めたFQCNにすることで、この問題を回避することができます。

# BeanNameGeneratorの指定
Springのコンポーネントスキャン時に使用される`BeanNameGenerator`インターフェースの実装クラスを`FullyQualifiedAnnotationBeanNameGenerator`クラスに変更することで、デフォルトで与えられるBean名をFQCNに変更することができます。
`BeanNameGenerator`インターフェースは、コンポーネントスキャン時にBean名を自動生成するために使われるインターフェースで、Bean名の生成ロジックはその実装クラスに依存します。デフォルトで使用される実装クラスは`AnnotationBeanNameGenerator`クラスです。
コンポーネントスキャン時に使用される`BeanNameGenerator`実装クラスを指定する方法はいくつか存在します。

※`FullyQualifiedAnnotationBeanNameGenerator`クラスは`Spring Framework 5.2.3 (Spring Boot 2.2.3)`で追加されたため、それ以前のバージョンを使用する場合は[こちらの記事](https://qiita.com/yu_bookstore/items/07e905cb5b645781bc43)等を参考に、独自の`BeanNameGenerator`実装クラスを定義して使用してください。

## @&#x200b;SpringBootApplication で指定する
SpringBootを使用している場合には、メインクラスに付与する`@SpringBootApplication`アノテーションの`nameGenerator`パラメータに`BeanNameGenerator`実装クラスを指定することで、コンポーネントスキャン時にその実装クラスが使用されるようになります。

```java
@SpringBootApplication(nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
public class DemoApplication {
	/*...*/
}
```

## @&#x200b;ComponentScan で指定する
SpringBootを使用せず、JavaベースでBean定義を行っている場合は、`@ComponentScan`アノテーションの`nameGenerator`パラメータに`BeanNameGenerator`実装クラスを指定することで、コンポーネントスキャン時にその実装クラスが使用されるようになります。

```java
@Configuration
@ComponentScan(basePackages = "com.example.demo", 
		nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
public class AppConfig {
	/*...*/
}
```

## XMLで指定する
SpringBootを使用せず、XMLベースでBean定義を行っている場合は、`<context:component-scan>`タグの`nameGenerator`属性に`BeanNameGenerator`実装クラスのFQCNを指定することで、コンポーネントスキャン時にその実装クラスが使用されるようになります。

```xml
<context:component-scan
        base-package="com.example.demo"
        name-generator="org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator" />
```

## MyBatis を使用する場合
MyBatisのMapperクラスなどのように、Springのコンポーネントスキャン以外の方法でBean登録されるBeanについては、前述の設定とは別の設定が必要になることがあります。
MyBatisでは、以下の方法でMapperクラスのデフォルトBean名の生成に使用される`BeanNameGenerator`実装クラスを指定することができます。

### @&#x200b;MapperScan で指定する
SpringBootを使用している場合や、JavaベースでBean定義を行っている場合は、`@MapperScan`アノテーションの`nameGenerator`パラメータに`BeanNameGenerator`実装クラスを指定することで、マッパースキャン時にその実装クラスが使用されるようになります。

```java
@SpringBootApplication(nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
@MapperScan(nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
public class DemoApplication {
	/*...*/
}
```

### XMLで指定する
SpringBootを使用せず、XMLベースでBean定義を行っている場合は、`<mybatis:scan>`タグの`nameGenerator`属性に`BeanNameGenerator`実装クラスのFQCNを指定することで、マッパースキャン時にその実装クラスが使用されるようになります。

```xml
<mybatis:scan
        base-package="com.example.demo"
        name-generator="org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator" />
```

# 参考
- https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/FullyQualifiedAnnotationBeanNameGenerator.html
- https://mybatis.org/spring/ja/mappers.html#Mapper_.E3.81.AE.E8.87.AA.E5.8B.95.E6.A4.9C.E5.87.BA
- https://qiita.com/yu_bookstore/items/07e905cb5b645781bc43
- https://ocs.hatenadiary.org/entry/20101129/1291034951

ご意見・ご指摘等あればコメントいただけると幸いです。