복붙노트

[SPRING] @Foo 커스텀 어노테이션으로 모든 빈을 어떻게 찾을 수 있습니까?

SPRING

@Foo 커스텀 어노테이션으로 모든 빈을 어떻게 찾을 수 있습니까?

나는이 봄 환경을 가지고있다 :

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

@Foo로 주석 처리 된 모든 bean 목록을 얻으려면 어떻게해야합니까?

참고 : @Foo는 저에게 정의 된 맞춤 주석입니다. "공식적인"스프링 주석 중 하나가 아닙니다.

[편집] 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]]>로 실패하지만 <: []]>. 왜?

해결법

  1. ==============================

    1.주석이있는 bean을 가져 오려면 getBeansWithAnnotation () 메소드를 사용하십시오.

    주석이있는 bean을 가져 오려면 getBeansWithAnnotation () 메소드를 사용하십시오.

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

    다음은 비슷한 토론입니다.

  2. ==============================

    2.몇 명의 스프링 전문가의 도움으로 해결책을 찾았습니다. BeanDefinition의 source 속성은 AnnotatedTypeMetadata가 될 수 있습니다. 이 인터페이스에는 getAnnotationAttributes () 메소드가 있는데,이 메소드를 사용하여 Bean 메소드의 어노테이션을 가져올 수 있습니다.

    몇 명의 스프링 전문가의 도움으로 해결책을 찾았습니다. BeanDefinition의 source 속성은 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;
    }
    

    도우미 클래스 및 테스트 케이스의 전체 코드가있는 요점

  3. ==============================

    3.답변과 Grzegorz의 답변에는 모든 경우에 사용할 수있는 접근법이 포함되어 있지만 가장 일반적인 경우에 똑같이 잘 작동하는 훨씬 단순한 방법을 발견했습니다.

    답변과 Grzegorz의 답변에는 모든 경우에 사용할 수있는 접근법이 포함되어 있지만 가장 일반적인 경우에 똑같이 잘 작동하는 훨씬 단순한 방법을 발견했습니다.

    1) Meta-annotate @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 주석이 달린 메소드 (질문에 필요)에 의해 생성되거나 발견 된 @Foo 주석이있는 클래스에서 생성 된 모든 인스턴스를 포함합니다.

    필요한 경우 목록을 추가로 필터링 할 수 있습니다.

    @Autowired
    @Foo
    List<SpecialFooServiceImpl> fooBeans;
    

    좋은 부분은 메소드의 다른 @Qualifier (메타) 주석이나 @Component 및 유형 수준의 다른 주석과 간섭하지 않는다는 것입니다. 또한 대상 빈에 특정 이름이나 유형을 적용하지도 않습니다.

  4. ==============================

    4.@Foo로 주석 처리 된 bean을 만들기 위해 @Foo를 a () 메소드에 두는 것만으로는 충분하지 않습니다.

    @Foo로 주석 처리 된 bean을 만들기 위해 @Foo를 a () 메소드에 두는 것만으로는 충분하지 않습니다.

    Spring 코드 디버깅을 시작하기 전에는 깨닫지 못했습니다. 중단 점은 org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean (String, Class )로 이해할 수있었습니다.

    물론 주석을 Named 클래스로 옮긴 경우 :

      @Foo
      public static class Named {
      ...
    

    테스트의 일부 세부 사항 (주석 대상 등)이 수정되었습니다.

    두 번째 생각을 한 후, 그것은 아주 자연스러운 일입니다. getBeansWithAnnotation ()이 호출되면 Spring에있는 유일한 정보는 빈입니다. 콩은 객체이고, 객체는 클래스입니다. 그리고 Spring은 추가적인 정보를 저장하는 것을 필요로하지 않는다. 어노테이션 된 bean을 만드는 데 사용 된 팩토리 메소드는 무엇입니까?

    편집 @Bean 메서드에 대한 주석을 보존하라는 요청이 있습니다 : https://jira.springsource.org/browse/SPR-5611

    다음 해결 방법으로 "수정되지 않음"으로 종료되었습니다.

  5. from https://stackoverflow.com/questions/14236424/how-can-i-find-all-beans-with-the-custom-annotation-foo by cc-by-sa and MIT license