web-dev-qa-db-ja.com

カスタム注釈@Fooを持つすべてのBeanを見つけるにはどうすればよいですか?

私はこのスプリング構成を持っています:

@Lazy
@Configuration
public class MyAppConfig {
    @Foo @Bean
    public IFooService service1() { return new SpecialFooServiceImpl(); }
}

@Fooアノテーションが付けられたすべてのBeanのリストを取得するにはどうすればよいですか?

注:@Fooは、私が定義したカスタムアノテーションです。これは「公式の」Spring注釈の1つではありません。

[EDIT] Avinash T.の提案に従って、このテストケースを作成しました。

import static org.junit.Assert.*;
import Java.lang.annotation.ElementType;
import Java.lang.annotation.RetentionPolicy;
import Java.lang.annotation.Target;

import Java.lang.annotation.Retention;
import Java.lang.reflect.Method;
import Java.util.Map;
import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

public class CustomAnnotationsTest {

    @Test
    public void testFindByAnnotation() throws Exception {

        AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext( CustomAnnotationsSpringCfg.class );

        Method m = CustomAnnotationsSpringCfg.class.getMethod( "a" );
        assertNotNull( m );
        assertNotNull( m.getAnnotation( Foo.class ) );

        BeanDefinition bdf = appContext.getBeanFactory().getBeanDefinition( "a" );
        // Is there a way to list all annotations of bdf?

        Map<String, Object> beans = appContext.getBeansWithAnnotation( Foo.class );
        assertEquals( "[a]", beans.keySet().toString() );
    }


    @Retention( RetentionPolicy.RUNTIME )
    @Target( ElementType.METHOD )
    public static @interface Foo {

    }

    public static class Named {
        private final String name;

        public Named( String name ) {
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    @Lazy
    @Configuration
    public static class CustomAnnotationsSpringCfg {

        @Foo @Bean public Named a() { return new Named( "a" ); }
             @Bean public Named b() { return new Named( "b" ); }
    }
}

しかし、org.junit.ComparisonFailure: expected:<[[a]]> but was:<[[]]>で失敗します。どうして?

28
Aaron Digulla

数人のSpringエキスパートの助けを借りて、解決策を見つけました。sourceBeanDefinitionプロパティはAnnotatedTypeMetadataになります。このインターフェイスにはメソッドgetAnnotationAttributes()があり、これを使用してBeanメソッドの注釈を取得できます。

public List<String> getBeansWithAnnotation( Class<? extends Annotation> type, Predicate<Map<String, Object>> attributeFilter ) {

    List<String> result = Lists.newArrayList();

    ConfigurableListableBeanFactory factory = applicationContext.getBeanFactory();
    for( String name : factory.getBeanDefinitionNames() ) {
        BeanDefinition bd = factory.getBeanDefinition( name );

        if( bd.getSource() instanceof AnnotatedTypeMetadata ) {
            AnnotatedTypeMetadata metadata = (AnnotatedTypeMetadata) bd.getSource();

            Map<String, Object> attributes = metadata.getAnnotationAttributes( type.getName() );
            if( null == attributes ) {
                continue;
            }

            if( attributeFilter.apply( attributes ) ) {
                result.add( name );
            }
        }
    }
    return result;
}

ヘルパークラスとテストケースの完全なコードを含む要点

23
Aaron Digulla

getBeansWithAnnotation() メソッドを使用して、注釈付きのBeanを取得します。

Map<String,Object> beans = applicationContext.getBeansWithAnnotation(Foo.class);

ここ は同様の議論です。

37
Avinash T.

受け入れられた答えと Grzegorzの答え にはallの場合に機能するアプローチが含まれていますが、私ははるかにシンプルな機能を見つけました最も一般的なケースでも同様です。

1)@Fooでメタ注釈@Qualifier

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Foo {
}

2)質問で説明されているように、@Fooをファクトリメソッドに振りかけます。

@Foo @Bean
public IFooService service1() { return new SpecialFooServiceImpl(); }

ただし、型レベルでも機能します。

@Foo
@Component
public class EvenMoreSpecialFooServiceImpl { ... }

3)次に、タイプと作成方法に関係なく、@Fooで修飾されたすべてのインスタンスを挿入します。

@Autowired
@Foo
List<Object> fooBeans; 

fooBeansには、@Foo- annotatedメソッド(質問で必要な場合)によって生成された、または検出された@Foo注釈付きクラスから作成されたすべてのインスタンスが含まれます。

必要に応じて、リストをタイプ別にさらにフィルタリングできます。

@Autowired
@Foo
List<SpecialFooServiceImpl> fooBeans;

良い点は、メソッド上の他の@Qualifier(メタ)アノテーションや、型レベルの@Componentおよびその他のアノテーションと干渉しないことです。また、ターゲットBeanに特定の名前やタイプを強制しません。

21
kaqqao

ショートストーリー

a Beanに_@Foo_アノテーションを付けるには、a()メソッドに_@Foo_を配置するだけでは不十分です。

長い話

Springコードのデバッグを開始する前に気付いていなかったので、org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(String, Class<A>)のブレークポイントがそれを理解するのに役立ちました。

もちろん、注釈をNamedクラスに移動した場合:

_  @Foo
  public static class Named {
  ...
_

また、テストのマイナーな詳細(アノテーションターゲットなど)を修正しましたテストが動作します

考え直した後、それは非常に自然です。 getBeansWithAnnotation()が呼び出されると、Springが持つ唯一の情報はBeanです。 Beanはオブジェクトであり、オブジェクトにはクラスがあります。また、Springは追加情報を保存する必要はないようです。注釈付きのBeanを作成するために使用されたファクトリメソッドなど.

[〜#〜] edit [〜#〜]_@Bean_メソッドの注釈を保存するよう要求する問題があります: https://jira.springsource.org/browse/SPR-5611

次の回避策により、「修正しない」としてクローズされました。

  • BeanPostProcessorを使用する
  • BPPメソッドに提供されるbeanNameを使用して、関連するBeanDefinitionを囲んでいるBeanFactoryから検索します
  • BeanDefinition(_@Configuration_ Bean)およびfactoryBeanName(_@Bean_名)のfactoryMethodNameを照会する
  • リフレクションを使用して、Beanの元のMethodを取得します
  • リフレクションを使用して、そのメソッドからのカスタム注釈を調べます
12