アノテーション使用時に SpringFramework がデフォルトで設定するコンポーネント名をカスタマイズする

SpringFramework も、アノテーションを使用してBean定義を行う案件がだいぶ定着してきたように思います。そんな中で、

  • interface を FooBarBaz を定義して、それを implement するクラスを FooBarBazImpl という名前で作りなさい
  • そのとき、コンポーネント名 (Spring流にいうとBean名) は fooBarBaz にしなさい
  • 今回のプロジェクトではアノテーションを使うが、そのときコンポーネント名は @Component("fooBarBaz") のように明示的に名前を指定しなさい

といった規約を採用するパターンをしばしば見かけます。コードで例示すると以下のようになりますね。

@Component("fooBarBaz")
public class FooBarBazImpl implements FooBarBaz {
   ...
}


このようなオートマティックに解決できそうなルールは、そのままコンピューターにやらせちゃった方が良いんじゃないかなーと常々思っています。今回のケースでは AnnotationBeanNameGenerator#buildDefaultBeanName をオーバーライドすれば望むとおりの挙動が得られそうです。

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;

/**
 * 独自定義の {@link BeanNameGenerator} です。
 * 原則として {@link AnnotationBeanNameGenerator} と同じですが、
 * コンポーネントのクラス名に接尾辞 {@code Impl} があるときに限り、
 * それを自動的に除去します。
 */
public class ProjectOriginalAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator {
    private static final String BEAN_NAME_SUFFIX = "Impl";

    @Override
    protected String buildDefaultBeanName(BeanDefinition definition) {
        String defaultBeanName = super.buildDefaultBeanName(definition);
        if (defaultBeanName.endsWith(BEAN_NAME_SUFFIX)) {
            return defaultBeanName.substring(
                    0, defaultBeanName.length() - BEAN_NAME_SUFFIX.length());
        }
        return defaultBeanName;
    }
}


これを applicationContext.xml に既に宣言されている(はず)の タグに新しい属性として追加してやれば、コンテナにコンポーネントが登録されるときにカスタム BeanNameGenerator が使用されます。

<!-- パッケージ名は適当につけています -->
<context:component-scan
        base-package="com.example.component"
        name-generator="com.example.ProjectOriginalAnnotationBeanNameGenerator" />


これで、最初に示した FooBarBazImpl のアノテーションをシンプルに書くことができるようになりました。以下の記述でも、コンポーネント名は fooBarBaz となるはずです。

@Component
public class FooBarBazImpl implements FooBarBaz {
   ...
}


Spring は拡張しやすく作られているので、こういったカスタマイズが簡単にできてうれしいですね。なお、@Component("fooBarBaz") のようにコンポーネント名を明記した場合はそちらの設定が優先されます。必要な状況においては原則を破ることができるわけです。逆に、常にコンポーネント名を明記するスタイルだと、クラス名をリファクタリングで変更したりしたときにコンポーネント名だけ古いまま残って後で見るとよく分からないものになってました、というパターンが発生しそうです。
コンピューター様に任せられるところは、素直に任せた方が良いんじゃないかなぁと。配管コードは極力減らして、本質的なロジックの実装に時間を使いたいところです。