春の@Lookupアノテーション

春の@Lookupアノテーション

1. 前書き

このクイックチュートリアルでは、@Lookupアノテーションを使用して、Springのメソッドレベルの依存性注入のサポートについて説明します。

2. なぜ@Lookup

@Lookupアノテーションが付けられたメソッドは、メソッドを呼び出すときにメソッドの戻り値の型のインスタンスを返すようにSpringに指示します。

基本的に、Springはアノテーション付きメソッドをオーバーライドし、メソッドの戻り値の型とパラメーターをBeanFactory#getBean.への引数として使用します

@Lookupは次の場合に役立ちます。

  • プロトタイプスコープのBeanをシングルトンBeanに注入する(Providerと同様)

  • 依存関係の手続き的な注入

@Lookupは、JavaでXML要素lookup-methodに相当することにも注意してください。

3. @Lookupの使用

3.1. プロトタイプスコープのBeanをシングルトンBeanに注入する

プロトタイプのSpring Beanを使用することにした場合、シングルトンのSpring BeanがこれらのプロトタイプのSpring Beanにどのようにアクセスするかという問題にほとんどすぐに直面します。

現在、Providerは確かに一方向ですが、@Lookupはいくつかの点でより用途が広いです。

まず、後でシングルトンBeanに注入するプロトタイプBeanを作成しましょう。

@Component
@Scope("prototype")
public class SchoolNotification {
    // ... prototype-scoped state
}

そして、@Lookupを使用するシングルトンBeanを作成する場合:

@Component
public class StudentServices {

    // ... member variables, etc.

    @Lookup
    public SchoolNotification getNotification() {
        return null;
    }

    // ... getters and setters
}

@Lookupを使用すると、シングルトンBeanを介してSchoolNotificationのインスタンスを取得できます。

@Test
public void whenLookupMethodCalled_thenNewInstanceReturned() {
    // ... initialize context
    StudentServices first = this.context.getBean(StudentServices.class);
    StudentServices second = this.context.getBean(StudentServices.class);

    assertEquals(first, second);
    assertNotEquals(first.getNotification(), second.getNotification());
}

StudentServicesでは、getNotificationメソッドをスタブとして残していることに注意してください。

これは、SpringがbeanFactory.getBean(StudentNotification.class)の呼び出しでメソッドをオーバーライドするため、空のままにしておくことができるためです。

3.2. 依存性を手続き的に注入する

ただし、さらに強力なのは、@Lookupを使用すると、依存関係を手続き的に挿入できることです。これは、Providerでは実行できません。

いくつかの状態でStudentNotificationを拡張しましょう:

@Component
@Scope("prototype")
public class SchoolNotification {
    @Autowired Grader grader;

    private String name;
    private Collection marks;

    public SchoolNotification(String name) {
        // ... set fields
    }

    // ... getters and setters

    public String addMark(Integer mark) {
        this.marks.add(mark);
        return this.grader.grade(this.marks);
    }
}

現在、いくつかのSpringコンテキストと、手順で提供する追加のコンテキストに依存しています。

次に、学生データを取得して永続化するメソッドをStudentServicesに追加できます。

public abstract class StudentServices {

    private Map notes = new HashMap<>();

    @Lookup
    protected abstract SchoolNotification getNotification(String name);

    public String appendMark(String name, Integer mark) {
        SchoolNotification notification
          = notes.computeIfAbsent(name, exists -> getNotification(name)));
        return notification.addMark(mark);
    }
}

実行時に、Springは同じ方法でメソッドを実装しますが、いくつかの追加のトリックがあります。

まず、複雑なコンストラクターを呼び出したり、他のSpring Beanを挿入したりできるため、SchoolNotificationをSpring対応のメソッドのように扱うことができます。

これは、beanFactory.getBean(SchoolNotification.class, name)の呼び出しでgetSchoolNotificationを実装することによって行われます。

次に、上記の例のように、@Lookup-annotatedメソッドを抽象化できる場合があります。

abstractの使用はスタブよりも少し見栄えが良いですが、周囲のBeanをdon’tcomponent-scan or @Bean-manageする場合にのみ使用できます。

@Test
public void whenAbstractGetterMethodInjects_thenNewInstanceReturned() {
    // ... initialize context

    StudentServices services = context.getBean(StudentServices.class);
    assertEquals("PASS", services.appendMark("Alex", 89));
    assertEquals("FAIL", services.appendMark("Bethany", 78));
    assertEquals("PASS", services.appendMark("Claire", 96));
}

この設定により、Springの依存関係とメソッドの依存関係をSchoolNotificationに追加できます。

4. 制限事項

@Lookupの汎用性にもかかわらず、いくつかの注目すべき制限があります。

  • Student,のような周囲のクラスがコンポーネントスキャンされる場合、@Lookupのような注釈付きメソッドは具体的でなければなりません。 これは、コンポーネントのスキャンが抽象Beanをスキップするためです。

  • 周囲のクラスが@Beanで管理されている場合、@Lookup-annotatedメソッドはまったく機能しません。

そのような状況で、プロトタイプBeanをシングルトンに注入する必要がある場合は、代わりにProviderを探すことができます。

5. 結論

この簡単な記事では、Springの@Lookupアノテーションをいつどのように使用するかを学びました。これには、プロトタイプスコープのBeanをシングルトンBeanに注入する方法や、依存性を手続き的に注入する方法などが含まれます。

このチュートリアルで使用されるすべてのコードは、over on Githubにあります。