読者です 読者をやめる 読者になる 読者になる

kagamihogeの日記

kagamihogeの日記です。

Spring Framework Reference Documentation 4.1.xのIII. Core Technologies 5.9から5.11までをテキトーに訳した

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ をテキトーに訳した。

読んでる途中で4.1.5から4.1.6になったけど、まぁタブン大きくは変わらんでしょう。

5.9 Annotation-based container configuration

Springの設定はXMLよりアノテーションの方が良い?

アノテーション設定の導入は、アノテーションXMLよりも良い(better)かどうか? という質問を提起しました。端的に答えるなら、場合に依ります(it depends)。ちゃんと答えるなら、賛否両論であり、どちらのやり方がよりベターかを決定する開発者次第、といったところです。定義方法によっては、アノテーションは宣言に多くのコンテキストを提供し*1、より短く簡潔な設定となります。しかし、XMLソースコードに触れずにコンポーネントを結びつけたり、再コンパイルする点で優れています。アノテーションを付与するクラスはもはやPOJOではないと主張する開発者がいる一方で、ソースにワイヤリングの情報を持たせるのを好む開発者もおり、こうなると、設定は分散化して制御が困難になっていきます。

どのようなやり方を選ぼうと、Springは両方のスタイルを混ぜて使うことが可能です。It’s worth pointing out that through its JavaConfig option*2、Springは、ターゲットコンポーネントのソースを修正しない方法やツール単位で、非侵襲的(non-invasive)にアノテーションを使うことが可能で、Spring Tool Suiteはすべての設定スタイルをサポートします。

XMLセットアップの代替案としてアノテーション設定を提供します。かぎ括弧宣言*3の代わりにコンポーネントを結びつけるのにバイトコードメタデータを使用します。ビーンの記述にXMLを使う代わりに、開発者は設定をコンポーネントクラスそれ自体に移動し、関連クラス・メソッド・フィールド宣言にアノテーションを使います。the section called “Example: The RequiredAnnotationBeanPostProcessor”で触れたように、BeanPostProcessorアノテーションを組み合わせるのはSpring IoCの拡張としては一般的な方法です。たとえば、Spring 2.0では必須プロパティの強制に@Requiredを導入しました。Spring 2.5では、Springの依存性注入を動作させるのに同一の一般的なアプローチに従わせることが可能になりました*4。基本的には、@AutowiredアノテーションSection 5.4.5, “Autowiring collaborators”で解説したのと同じ機能を提供しますが、より細かい制御が効き、また、適用可能性が広げられています。また、Spring 2.5では@PostConstruct@PreDestroyなどのJSR-250 アノテーションをサポートしました。Spring 3.0では@Inject@Namedなどのjavax.injectパッケージに含まれるJSR-330 (Dependency Injection for Java)アノテーションをサポートしました。これらのアノテーションに関する詳細は関連セクションを参照してください。

アノテーションインジェクションはXMLインジェクションの前に実行され、よって、両方のアプローチでワイヤするプロパティは後の設定が前をオーバーライドします。

これまで述べてきた通り、ビーン定義を一つずつ登録できますが、XMLベースのSpring設定に以下のタグを含めることでそうしたビーンを暗黙的に登録することも可能です(context名前空間を含めていることに注意)。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

(暗黙的に登録されるpost-processorsにはAutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor, 前述したRequiredAnnotationBeanPostProcessorがあります。)

<context:annotation-config/>は、このタグが定義されているのと同一のアプリケーションコンテキスト内のビーンのアノテーションのみ、対象とします。つまり、たとえばDispatcherServlet用のWebApplicationContext<context:annotation-config/>を追加する場合、controllersの@Autowiredビーンのみチェックし、servicesはチェックしません。詳細はSection 17.2, “The DispatcherServlet”を参照してください。

5.9.1 @Required

@Requiredアノテーションは以下の例にようにビーンプロパティのセッターメソッドに適用します。

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

このアノテーションは、ビーン定義のプロパティ値の明示的指定あるいはオートワイヤにより、設定時にこのビーンプロパティの処理が必須である、と単に指定するものです。対象のビーンプロパティが処理されなかった場合にはコンテナは例外をスローします。早期の明示的な失敗*5を可能にし、実際に動かす段になってからのNullPointerExceptionの発生を回避します。たとえば初期化メソッドなど、ビーンクラス自身にアサーションを埋め込むことを推奨します。これによって、コンテナ外でそのクラスを使う場合であっても必須の参照や値を強制することになります。

5.9.2 @Autowired

これまでに見た通り、@Autowiredは"伝統的な"("traditional")セッターメソッドに適用できます。

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

JSR 330の@Injectアノテーションは下記の例のようにSpringの@Autowiredアノテーションが使える場所で使用可能です。

任意の名前および/または複数引数のメソッドアノテーションをつけられます。

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...

}

コンストラクタとフィールドに@Autowiredを適用可能です。

public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...

}

配列型を期待するフィールドやメソッドアノテーションを付与することで、ApplicationContextから特定の型のすべてのビーンを使用可能です。

public class MovieRecommender {

    @Autowired
    private MovieCatalog[] movieCatalogs;

    // ...

}

同じことが型付のコレクションにも適用できます。

public class MovieRecommender {

    private Set<MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...

}

もし配列やリストを指定順序にソートしたい場合、ビーンにorg.springframework.core.Orderedインタフェースもしくは、@Order@Priorityアノテーションのどちらか、を実装します。

同様に、キーの期待型がStringの場合に限り、型付きMapをオートワイヤ可能です。Mapの値は期待型のすべてのビーンを含み、キーはビーン名に相当するものになります。

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...

}

デフォルトでは、オートワイヤは候補として使用可能なビーンが無い場合には失敗します。このデフォルトの振る舞いは、アノテーション付与したメソッドコンストラクタrequired依存性を指定したフィールド、に適用されます。この振る舞いは以下に示すように変更可能です。

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired(required=false)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

クラスに一つのコンストラクタのみrequiredアノテーションを付与可能*6ですが、複数のnon-requiredなコンストラクタアノテーションを付与できます。その場合、候補の判定が行われ、Springは依存性を満たすコンストラクタのうち最長(greediest)のものを使用します。つまり、最も多くの引数を持つコンストラクタを使用します。
@Autowiredrequired属性よりは@Requiredアノテーションを推奨します*7required属性はそのプロパティがオートワイヤでは必須でないと示すもので、もしオートワイヤが出来なくともそのプロパティは無視されます。一方で@Requiredは、コンテナが何らかの手段によってそのプロパティを設定したことを強制する点で、ずっと強力です。もし値が何もインジェクトされなければ、例外が発生します。

@Autowiredはwell-knownな依存性のインタフェースである、BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher, MessageSource、にも使えます。これらのインタフェースとその拡張インタフェース、ConfigurableApplicationContextResourcePatternResolver、は特に設定を必要とせず自動的に解決します。

public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...

}

自前のBeanPostProcessorBeanFactoryPostProcessor内では、SpringのBeanPostProcessor実装が処理する@Autowired, @Inject, @Resource, @Valueアノテーション(がもし有れば)は、適用できません*8。これらのBeanPostProcessorBeanFactoryPostProcessor型ではSpringの@BeanメソッドXMLによる明示的なワイヤリングが必須です。

5.9.3 Fine-tuning annotation-based autowiring with qualifiers

by typeのオートワイヤでは複数の候補が発生する可能性があるので、さらにもうひと手間必要なことがあります。方法の一つにSpringの@Qualifierアノテーションがあります。特定の引数をqualifier値に関連付け、型マッチの組み合わせを狭めることで引数の値に応じて指定のビーンが選ばれます。ごくシンプルなケースとしては、適当な値を指定します。

public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...

}

@Qualifierアノテーションコンストラクタの各引数やメソッド引数にも指定可能です。

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("main")MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...

}

対応するビーン定義は以下のようになります。qualifier値"main"のビーンが、それと同一のqualifier値に設定したコンストラクタ引数に、ワイヤされます。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/>

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/>

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

マッチするビーンが解決出来なかった場合の代替ルートでは、ビーン名にはデフォルトqualifierの値を考慮します。ネストしたqualifier要素ではなくビーンのidに"main"を設定可能で、この場合は同じマッチ結果となります。しかし、by nameで特定のビーンを参照する規約を使用しても、@Autowiredは基本的にはtype-drivenインジェクションで with optional semantic qualifiers*9. つまり、qualifier値は、ビーン名の代替ルートの場合であっても、複数の型マッチからセマンティクスを絞りこむだけです*10。qualifier値は意味的にはユニークなビーンIDへの参照を表現しません。qualifier値の好例としては"main", "EMEA", "persistent"など、前述の例のような匿名ビーン定義の場合に自動生成されるビーンIDからは、独立した特定コンポーネントの特徴を表現するものです。

上述のように、qualifierは型付コレクションにも適用可能です。例としてはSet<MovieCatalog>です。この場合、宣言したqualifierにマッチするすべてのビーンをコレクションにインジェクトします。このことはqualifierはユニークでなくても良いということを暗に示しており、フィルター条件の構成が単純になります。たとえば、同一のqualifier値"action"で複数MovieCatalogビーンを定義可能で、そのすべてが@Qualifier("action")アノテーションを付与したSet<MovieCatalog>にインジェクトされます。

もしby nameでアノテーション駆動インジェクションを表現したいのなら、技術的には@Qualifier値によるビーン名参照と同じことが出来るとしても、@Autowiredをメインには使わないでください。代わりに、JSR-250 @Resourceアノテーションを使い、こちらは、マッチ処理とは無関係に宣言型のユニーク名で特定のターゲットコンポーネントを指定するための定義です。
この意味的な違いから導かれることとして、コレクションやmap型としてビーン自身を定義したものは@Autowiredではインジェクトできません。なぜなら、その場合には型マッチが適合しないためです。ユニーク名でコレクションやマップのビーンを参照するビーンには@Resourceを使います。
@Autowiredは、フィールド・コンストラクタ複数引数のメソッドに適用し、パラメーターレベルにqualifierアノテーションを使い候補を狭めることが可能です。対照的に、@Resourceは、フィールドと単一引数のビーンプロパティーのセッターメソッドのみサポートします。つまり、インジェクション対象がコンストラクタ複数引数のメソッドかどうかでqualifiersは関係(stick with)します。

自前のカスタムqualifierアノテーションを定義可能です。アノテーションを作成して定義内に@Qualifierを入れるだけです。

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {

    String value();
}

オートワイヤするフィールドやパラメータにカスタムqualifierを使用可能です。

public class MovieRecommender {

    @Autowired
    @Genre("Action")
    private MovieCatalog actionCatalog;
    private MovieCatalog comedyCatalog;

    @Autowired
    public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
        this.comedyCatalog = comedyCatalog;
    }

    // ...

}

次に、候補ビーン定義に情報を追加します。<bean/>タグのサブ要素として<qualifier/>タグを追加し、カスタムqualifierアノテーションにマッチするtypeおよびvalueを定義します。typeにはアノテーションの完全修飾クラス名を指定します。もしくは、簡易なやり方として、衝突する恐れが無いのであれば短いクラス名も使用可能です。以下の例は両方のやり方を使用しています。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="Genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        _<qualifier type="example.Genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

Section 5.10, “Classpath scanning and managed components”では、XMLでqualifierメタデータを提供するアノテーションベースのもう一つの方法を紹介します。具体的にはSection 5.10.8, “Providing qualifier metadata with annotations”を参照してください。

場合によっては、値の無いアノテーションで十分な場合があります。これは汎用目的のアノテーションに使用したり、依存性の異なる複数の型に適用可能です。たとえば、利用可能なインターネット接続が無い場合に検索するオフライン(offlive)カタログに使います。まず単純なアノテーションを定義します。

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {

}

次に、オートワイヤするフィールドやプロパティにアノテーションを追加します。

public class MovieRecommender {

    @Autowired
    @Offline
    private MovieCatalog offlineCatalog;

    // ...

}

ビーン定義にはqualifier typeのみ必要です。

<bean class="example.SimpleMovieCatalog">
    <qualifier type="Offline"/>
    <!-- inject any dependencies required by this bean -->
</bean>

名前付きの属性や単純なvalueとは別の属性をカスタムのqualifierアノテーションに定義することも出来ます。複数の定義値がある場合、オートワイヤするフィールドやパラメータに指定し、ビーン定義ではオートワイヤ候補と見なされるすべての属性値にマッチする必要があります。例として以下のアノテーション定義を見てください。

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {

    String genre();

    Format format();

}

このケースではFormatenumです。

public enum Format {
    VHS, DVD, BLURAY
}

オートワイヤするフィールドはカスタムqualifierアノテーションを付与し、genreformat属性の値を含めます。

public class MovieRecommender {

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Action")
    private MovieCatalog actionVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Comedy")
    private MovieCatalog comedyVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.DVD, genre="Action")
    private MovieCatalog actionDvdCatalog;

    @Autowired
    @MovieQualifier(format=Format.BLURAY, genre="Comedy")
    private MovieCatalog comedyBluRayCatalog;

    // ...

}

最後に、qualifierの値に合致するビーン定義を作ります。この例では<qualifier/>サブ要素の代わりにビーンmeta属性も使用しています。<qualifier/>とその属性が優先されますが、有効なqualifierが存在しなければオートワイヤは代替ルートとして<meta/>タグが提供する値を使用し、この例で言えば最後の二つのビーン定義がそれに該当します。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Action"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Comedy"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="DVD"/>
        <meta key="genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="BLURAY"/>
        <meta key="genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

</beans>

5.9.4 Using generics as autowiring qualifiers

@Qualifierアノテーションに加え、qualificationの暗黙的な形式(implicit form )としてJavaジェネリクス型を使用可能です。たとえば、以下の設定があるとします。

@Configuration
public class MyConfiguration {

    @Bean
    public StringStore stringStore() {
        return new StringStore();
    }

    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }

}

上記のビーンはジェネリックなインタフェース、Store<String>Store<Integer>、を実装すると想定すると、Storeインタフェースを@Autowireすることが可能で、qualifierとしてジェネリクスを使えます。

@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean

@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

Lists, Maps, 配列のオートワイヤにもGeneric qualifiersは適用可能です。

// すべての<Integer> genericなStoreビーンをインジェクトする。
// Store<String>ビーンはこのリストには含まれない。
@Autowired
private List<Store<Integer>> s;

5.9.5 CustomAutowireConfigurer

CustomAutowireConfigurerBeanFactoryPostProcessorの一種で、Springの@Qualifierアノテーションを付与していない場合でも、カスタムのqualifierアノテーションを登録可能です。

<bean id="customAutowireConfigurer"
        class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
    <property name="customQualifierTypes">
        <set>
            <value>example.CustomQualifier</value>
        </set>
    </property>
</bean>

AutowireCandidateResolverは以下を使用してオートワイヤ候補を決定します。

  • 各ビーン定義のautowire-candidateの値
  • <beans/>要素で利用可能なdefault-autowire-candidatesのパターン
  • @QualifierアノテーションCustomAutowireConfigurerで登録したカスタムアノテーション

複数のビーンがオートワイヤ候補としてqualifyを用いている場合、第一候補の決定は次の通りです。もし候補のうちただ一つのビーン定義だけがprimary属性にtrueを設定している場合はそれが選ばれます。

5.9.6 @Resource

SpringはフィールドやビーンプロパティのセッターメソッドでJSR-250の@Resourceアノテーションを使用するインジェクションをサポートしています。Java EE 5と6では主要な方法で、たとえばJSF 1.2のマネージドビーンやJAX-WS 2.0エンドポイントなどがあります。Springは同様にSpringマネージドオブジェクトにこのパターンをサポートしています。

@Resourceはname属性を取り、デフォルトではSpringはインジェクトするためのビーン名としてその値を解釈します。つまり、by-nameセマンティクスという意味で、例は以下の通りです。

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder")
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

}

nameを明示的に指定しない場合、デフォルト名はフィールド名かセッターメソッドから取られます。フィールドの場合はフィールド名で、セッターメソッドの場合はビーンプロパティ名になります。よって以下の例は、セッターメソッドにインジェクトされるビーン名は"movieFinder"になります。

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

}

アノテーションに渡す名前はCommonAnnotationBeanPostProcessorの処理中に*11ApplicationContextがビーン名として解決します。この名前は、明示的にSpringのSimpleJndiBeanFactoryを設定する場合、JNDIを通して解決可能です。ただし、推奨はデフォルトの振る舞いを使用し、間接的関係のまま*12にしておくためにSpringのJNDIルックアップ機能をそのまま使用してください。

明示的な名前を指定しない@Resource固有の使用例としては、@Autowiredと似ており、@Resourceは特定のネームドビーンではなくprimary type matchで解決を試行し、また、well-knownで解決可能な依存性である、BeanFactory, ApplicationContext, ResourceLoader, ApplicationEventPublisher, MessageSource、を解決します。

よって以下の例では、customerPreferenceDaoフィールドは、まずcustomerPreferenceDaoという名前のビーンを探し、次に代替ルートとしてCustomerPreferenceDao型のprimary type matchに切り替わります*13。"context"フィールドはApplicationContextのknown解決可能な依存性に基づいてインジェクトが行われます。

public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    @Resource
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...

}

5.9.7 @PostConstruct and @PreDestroy

CommonAnnotationBeanPostProcessor@ResourceアノテーションだけでなくJSR-250 ライフサイクル(lifecycle)アノテーションも認識します。Spring 2.5で導入したこれらのアノテーションサポートは、initialization callbacksdestruction callbacksで解説したものとはまた別の方法を提供します。Spring ApplicationContext内にCommonAnnotationBeanPostProcessorが登録されている場合、これらのアノテーションを付与するメソッドがSpringライフサイクルインタフェースのメソッドもしくは明示的なコールバックメソッド宣言と同等のライフサイクルで呼び出されます。以下の例では、キャッシュを初期化時に事前処理し、破棄時にクリアしています。

public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }

}

各種のライフサイクルメカニズムを組み合わせる影響についてはthe section called “Combining lifecycle mechanisms”を参照してください。

5.10 Classpath scanning and managed components

この章のほとんどの例は設定メタデータを定義するのにXMLを使用しており、これはSpringコンテナ内にBeanDefinitionを生成します。以前の章(Section 5.9, “Annotation-based container configuration”))ではソースレベルのアノテーションで多数の設定メタデータを使用する方法を示しました。しかしながら、そちらの例では、アノテーションはただ単に依存性注入を行うだけで、ベースとなるビーン定義はXMLに明示的に定義しています。この章ではクラスパススキャンによる暗黙的な候補コンポーネント(candidate components)の検出オプションについて説明します。候補コンポーネントとはフィルター条件にマッチするクラスで、そのクラスに対応するビーン定義をコンテナに登録します。これによってビーン登録のためにXMLを使う必要が無くなり、代わりに、アノテーション(@Componentなど)や、AspectJ type expressions、コンテナに登録するクラス用のカスタムフィルター条件、を使用可能です。

Spring 3.0からは、Spring JavaConfigプロジェクトが提供する多数の機能がSpring Frameworkコアの一部となっています。これにより伝統的なXMLの代わりにJavaを使用してビーン定義が可能になりました。それらの新機能の使用例については@Configuration, @Bean, @Import, @DependsOnアノテーションを参照してください。

5.10.1 @Component and further stereotype annotations

@Repositoryアノテーションとは、リポジトリのステレオタイプ(stereotype)(DAOなど)やロールを満たす任意のクラス用のマーカーです。このマーカーの使用に関する共通点はSection 15.2.2, “Exception translation”で解説する例外の自動翻訳(automatic translation of exceptions)です。

Springはいくつかのステレオタイプアノテーションを提供しており、@Component, @Service, @Controllerなどがあります。@Componentは任意のSpringマネージドコンポーネント用の汎用ステレオタイプです。@Repository, @Service, @Controllerはより具体的な使い方のために@Componentを特殊化したもので、例えば、永続化(persistence)・サービス(service)・プレゼンテーション(presentation)レイヤ、などです。よって、自前のコンポーネントクラスに@Componentを設定することは可能ですが、代わりに@Repository, @Service, @Controllerアノテーションを使うことで、アスペクト関連付けやツールがそのクラスをより適切に処理できます。たとえば、これらのステレオタイプアノテーションはポイントカットの理想的なターゲットを作ります*14。また、@Repository, @Service, @ControllerSpring Frameworkの将来リリースにおいて追加のセマンティクスを導入する可能性があります。サービスレイヤーに@Component@Serviceのどちらかを選ぶ場合、@Serviceの方がより適しています。同様に、上述したように、永続化レイヤの自動例外翻訳用のマーカーとして@Repositoryをサポートしています。

5.10.2 Meta-annotations

Springが提供する多くのアノテーションを"メタアノテーション"("meta-annotations")として利用可能です。メタアノテーションは普通のアノテーションで、別のアノテーションに適用可能です。たとえば、上述の@Serviceアノテーション@Componentメタアノテーションが付けられています。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // Springはこのアノテーションを発見すると、@Component同様に@Service処理する
public @interface Service {

    // ....

}

メタアノテーション複合アノテーション(composed annotations)を生成するために組み合わせることも出来ます。たとえば、Spring MVC@RestControllerアノテーション@Controller@ResponseBodyの組み合わせで構成しています。

value()を除き、メタアノテーション型は、ユーザカスタマイズが出来るように元となるアノテーションの属性を再宣言可能です。元となるアノテーションの属性のサブセットのみ公開したい場合に特に役立ちます。たとえば、ここにsessionスコープを定義するカスタム@Scopeアノテーションがあるとして、proxyModeのカスタマイズを加えることが可能です。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope("session")
public @interface SessionScope {

    ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT

}

5.10.3 Automatically detecting classes and registering bean definitions

Springはステレオタイプのクラスの検出とそれに対応するApplicationContextBeanDefinitionsの登録を自動的に行うことが可能です。たとえば、以下の二つのクラスは自動検出の対象になります。

@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

}
@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}

ビーンの登録とクラスの自動検出をするには、@Configurationクラスに@ComponentScanを追加し、そこのbasePackage属性に二つのクラスの共通な親パッケージを指定します(もしくは、各クラスの親パッケージを含む comma/semicolon/space-separated のリストを定義します)。

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    ...
}

簡潔にするために、上記はアノテーションvalue属性を使っても構いません。ComponentScan("org.example")

以下はXMLを使用した場合の例です。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.example"/>

</beans>

<context:component-scan>の使用は暗黙的に<context:annotation-config>の機能を有効化します。通常は、<context:component-scan>を使う場合に<context:annotation-config>を含める必要はありません。

クラスパスパッケージのスキャンはクラスパス上に対応するディレクトリエントリの存在を要求します。ANTでJARをビルドする場合、JARタスクのfiles-onlyスイッチが非有効なことを確認してください。また、クラスパスディレクトリはある種の環境ではセキュリティポリシーに基づき公開されない場合があります。例えば、JDK 1.7.0_45以上(マニフェストTrusted-Libraryのセットアップを要求しますhttp://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresourcesを参照)のスタンドアローンアプリケーションなどです。

また、コンポーネントスキャン要素を使用する場合はAutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorの両方とも暗黙的に含まれます。つまり、この二つのコンポーネントは自動検出され、かつ、XMLのビーン設定メタデータを除くすべてのビーンと関連付けが行われます。

annotation-config属性にfalseを設定することでAutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorを登録しないように設定可能です。

5.10.4 Using filters to customize scanning

デフォルトでは、@Component, @Repository, @Service, @Controllerを付与したクラス、もしくは、自身に@Componentを付与しているカスタムアノテーションのみが候補コンポーネントの検出対象となります。ただし、カスタムフィルターを適用することでこの振る舞いの拡張と変更が可能です。@ComponentScanアノテーションのパラメータincludeFiltersないしexcludeFiltersに追加します(もしくはcomponent-scan要素のinclude-filterないしexclude-filterサブ要素)。各フィルター要素はtypeexpression属性を要求します。以下の表がフィルターオプションの説明です。

Table 5.5. Filter Types

Filter Type Example Expression Description
annotation (default) org.example.SomeAnnotation ターゲットコンポーネントに型レベルで存在しているアノテーション
assignable org.example.SomeClass A class (or interface) that the target components are assignable to (extend/implement).
aspectj org.example..*Service+ ターゲットコンポーネントでマッチさせるためのAspectJの型表現
regex org\.example\.Default.* ターゲットコンポーネントのクラス名でマッチさせるための正規表現
custom org.example.MyTypeFilter org.springframework.core.type .TypeFilterインタフェースのカスタム実装

以下の例はすべての@Repositoryアノテーションを無視して代わりにスタブのリポジトリを使うような設定例です。

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

XMLで同等な設定は以下の通りです。

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex"
                expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

アノテーションuseDefaultFilters=false設定をするか<component-scan/>要素にuse-default-filters="false"属性を追加することで、デフォルトフィルターを無効化できます。この設定は@Component, @Repository, @Service, @Controllerアノテーションを付与したクラスの自動検出を実質的に無効化します。

5.10.5 Defining bean metadata within components

Springコンポーネントはコンテナのビーン定義メタデータにcontribute*15することも出来ます。@Configurationクラス内でビーンメタデータを定義するのに使う@Beanアノテーションと同じことを行います。以下が簡単な例です。

@Component
public class FactoryMethodComponent {

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    public void doWork() {
        // Component method implementation omitted
    }

}

このクラスはSpringコンポーネントでありアプリケーション固有のコードとしてdoWork()メソッドを持ちます。ただし、publicInstance()というファクトリーメソッドを持つビーン定義をcontributeしています。@Beanアノテーションは、ファクトリーメソッド@Qualifierなどビーン定義プロパティによって、識別します。使用可能な他のメソッドレベルアノテーションとしては@Scope, @Lazy, カスタムqualifierアノテーションです。

コンポーネント初期化の役割に加えて、@Lazyアノテーション@Autowired, @Injectでマークしたインジェクションする場所にも付与可能です。このコンテキストでは、遅延解決(lazy-resolution)プロキシのインジェクションを意味します。

以前に解説したオートワイヤフィールドとメソッドには@Beanメソッドのオートワイヤがサポートされています。

@Component
public class FactoryMethodComponent {

    private static int i;

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    // use of a custom qualifier and autowiring of method parameters

    @Bean
    protected TestBean protectedInstance(
            @Qualifier("public") TestBean spouse,
            @Value("#{privateInstance.age}") String country) {
        TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }

    @Bean
    @Scope(BeanDefinition.SCOPE_SINGLETON)
    private TestBean privateInstance() {
        return new TestBean("privateInstance", i++);
    }

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public TestBean requestScopedInstance() {
        return new TestBean("requestScopedInstance", 3);
    }

}

この例は、Stringメソッド引数countryに、ネームドprivateInstanceビーンのAgeプロパティの値をオートワイヤします。Spring Expression Language要素の#{ <expression> }記法でプロパティ値を定義しています。@Valueでは、式リゾルバは式のテキスト解決時にビーン名を参照するための事前設定が行われます。

Springコンポーネント@BeanメソッドはSpringの@Configurationクラス内で用いる場合とは異なる処理が行われます。その違いとは、@Componentクラスはメソッドとフィールド呼び出しのインターセプトにCGLIBによる拡張を行いません。CGLIBプロキシは、協調オブジェクトのビーンメタデータ参照を生成する@Configurationクラスの@Beanメソッド内で、メソッドやフィールド呼び出しを行う手段に使います。そうしたメソッドは通常のJavaセマンティクスでは呼び出されません。対照的に、@Componentクラス内の@Beanメソッドメソッドやフィールド呼び出しは標準的なJavaセマンティクスを持ちます(has standard Java semantics.)。

5.10.6 Naming autodetected components

スキャン処理の過程でコンポーネントを自動検出した場合、そのビーン名は検出したスキャナのBeanNameGeneratorストラテジが生成します。デフォルトでは、nameの値を持つSpringのステレオタイプアノテーション@Component, @Repository, @Service, @Controller)は対応ビーン定義の名前にその値を与えます。

nameを持たないアノテーションやその他の検出コンポーネント(カスタムフィルターが検出したもの)の場合、デフォルトビーン名ジェネレーターはuncapitalized non-qualified class nameを返します。たとえば、以下の二つのコンポーネントを検出すると、その名前はmyMovieListerとmovieFinderImplになります。

@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

デフォルトのビーン命名規則を使いたくない場合、カスタムのビーン命名規則を作成可能です。まず、BeanNameGeneratorインタフェースを実装し、その実装にはデフォルト引数無しコンストラクタを含めます。それから、スキャナ設定時に完全修飾クラス名を指定します。

@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
    ...
}
<beans>
    <context:component-scan base-package="org.example"
        name-generator="org.example.MyNameGenerator" />
</beans>

原則として、あるコンポーネントが対象コンポーネントへ明示的に参照を行う場合には、アノテーションに名前を指定するようにしてください。しかし、コンテナ側にインジェクションの責任がある場合には自動生成名が適しています。

5.10.7 Providing a scope for autodetected components

一般的なSpringマネージドコンポーネントと同様に、デフォルトおよび自動検出コンポーネントの多くで共通なスコープはシングルトンです。ただし、別のスコープを必要する場合もあり、Spring 2.5では新しく@Scopeを導入しています。単純にアノテーションにスコープ名を指定します。

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

アノテーション方式に頼るのではなくスコープ解決規則をカスタムするには、ScopeMetadataResolverインタフェースを実装し、その実装にはデフォルト引数無しコンストラクタを含めておきます。それから、スキャナ設定時に完全修飾クラス名を指定します。

@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
    ...
}
<beans>
    <context:component-scan base-package="org.example"
            scope-resolver="org.example.MyScopeResolver" />
</beans>

非シングルトンスコープを使う場合、スコープオブジェクト用のプロキシを生成する必要があるかもしれません。その理由についてはthe section called “Scoped beans as dependencies”に解説があります。この用途のために、コンポーネントスキャン要素にscoped-proxy属性が利用可能です。三つの値が使用可能で、no, interfaces, targetClassです。たとえば、以下の設定は標準JDK動的プロキシ(standard JDK dynamic proxies)となります。

@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
    ...
}
<beans>
    <context:component-scan base-package="org.example"
        scoped-proxy="interfaces" />
</beans>

5.10.8 Providing qualifier metadata with annotations

@QualifierについてはSection 5.9.3, “Fine-tuning annotation-based autowiring with qualifiers”で解説しました。そのセクションの例ではオートワイヤ候補解決時に細粒度の制御を行うためのカスタムqualifierと@Qualifierを示しました。それらの例はXMLビーン定義だったので、qualifierメタデータXMLbean要素のサブ要素qualifiermetaを使用して候補ビーン定義に設定していました。コンポーネント自動検出クラスパススキャンを使う場合、候補クラスの型レベルアノテーションでqualifierメタデータを付与します。以下の三つの例はそのやり方を使用しています。

@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}
@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}
@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
    // ...
}

他のXML代替のアノテーション方式と同様に、アノテーションメタデータはクラス定義自身に結び付けるものということは覚えておいて下さい。XMLではqualifierメタデータのバリエーションを表現するのに同一型の(of the same type)複数ビーンを定義可能で、これはメタデータがクラスごとではなくインスタンスごとに与えられるものだからです。

5.11 Using JSR 330 Standard Annotations

Spring 3.0以降から始めるのなら、SpringはJSR-330(Dependency Injection)標準アノテーションをサポートしています。これらのアノテーションはSpringアノテーションと同様な方法でスキャンします。なお、クラスパスに関連jarを追加する必要があります。

Mavenを使用する場合、標準Mavenリポジトリjavax.injectアーティファクトを利用可能です。pom.xmlに以下の依存性を追加します。

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

5.11.1 Dependency Injection with @Inject and @Named

@Autowiredの代わりに、@javax.inject.Injectを以下のように使用可能です。

import javax.inject.Inject;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

@Autowired同様に、@Injectはクラス・フィールド・メソッドコンストラクタ引数に使用可能です。依存性注入にqualified nameを使いたい場合は、以下のように@Namedアノテーションを使用します。

import javax.inject.Inject;
import javax.inject.Named;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

5.11.2 @Named: a standard equivalent to the @Component annotation

@Componentの代わりに、@javax.inject.Namedを以下のように使用可能です。

import javax.inject.Inject;
import javax.inject.Named;

@Named("movieListener")
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

@Componentコンポーネント名を指定しないのは良くあることで、@Namedも同様のことが可能です。

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

@Namedを使う場合、Springアノテーションと全く同様な方法でコンポーネントスキャンを使用可能です。

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    ...
}

5.11.3 Limitations of the standard approach

標準アノテーションを使用する場合、以下のテーブルに示すように重要な機能のいくつかが使用不可なことを知っておく必要があります。

Table 5.6. Spring annotations vs. standard annotations

Spring javax.inject.* javax.inject restrictions / comments
@Autowired @Inject @Injectにはrequired属性が無い
@Component @Named -
@Scope("singleton") @Singleton JSR-330のデフォルトスコープはSpringのprototypeと似ています。ただし、Springの一般的なデフォルトとの一貫性を保つため、Springコンテナで宣言したJSR-330ビーンはデフォルトではsingletonになります。singletonでないスコープを使うには、Springの@Scopeアノテーションを使います。なお、javax.inject@Scopeアノテーションを提供していますが、それでも、このアノテーションは自前のアノテーションを生成するのに使うことを指示するためだけに使ってください。*16
@Qualifier @Named -
@Value - なし
@Required - なし
@Lazy - なし

*1:Due to the way they are defined, annotations provide a lot of context in their declarationが原文。直訳しちゃったけど、イマイチ意味がわからん。まぁXMLとの対比だから、宣言的によって簡潔ながらより多様なコンテキストが表現可能になる、ぐらいの意味だとは思うが

*2:訳せない。直訳すれば『JavaConfigオプションを経由することを指摘するのは価値がある』だが、何のことやら…

*3:angle-bracket declarations. かぎ括弧「<..>」つまりXML要素のこと

*4:Spring 2.5 made it possible to follow that same general approach to drive Spring’s dependency injection.が原文。なんか変な訳だ…

*5:eager and explicit failureが原文。コンテナが事前に必須チェックをすることで、そのプロパティが実際に使われる前にエラーを検出できる、くらいの意味。

*6:Only one annotated constructor per-class can be marked as requiredが原文。

*7:@Autowired's required attribute is recommended over the @Required annotationが原文。is recommended overがどうにも…ただ文脈的には@Requiredのほうが意図が明確だし振る舞いもクリアなのでそっちをオススメしてるのだとは思うが

*8:implementations which in turn means that you cannot applyが原文で、which in turn meansがよーわからんので飛ばして訳してる

*9:上手く訳せないからそのままにしたが…このあとを読む限り、qualifierはユニークIDとして使うもんじゃないよーと言いたいのだと思われる

*10:always have narrowing semantics within the set of type matchesが原文。

*11:of which ~ is awareが原文。訳に自信が無い。

*12:to preserve the level of indirectionが原文。Springのデフォルトをオーバーライドして名前をつけるのは推奨されず、勝手に名前がつくのでそちら経由で間接的に使用してね、くらいの意味かと思われる。

*13:この上の段ではprimary type match instead of a specific named beanなのに、ここだと逆になっている気がするのだが…

*14:these stereotype annotations make ideal targets for pointcutsが原文。

*15:貢献する、寄与するとかそんなよーな意味。ニュアンス的には、他のビーンの一部として使用可能な~て感じで、その奉仕してるっぽい感じをcontributeと言ってるのだと思われる

*16:this one is only intended to be used for creating your own annotations.が原文。ちょっと上手く訳せなかった