sourcetip

사용자 지정 주석 @Foo가 있는 모든 콩을 어떻게 찾을 수 있습니까?

fileupload 2023. 8. 26. 12:10
반응형

사용자 지정 주석 @Foo가 있는 모든 콩을 어떻게 찾을 수 있습니까?

스프링 구성은 다음과 같습니다.

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

주석이 달린 모든 콩의 목록을 가져오려면 어떻게 해야 합니까?@Foo?

참고:@Foo사용자 정의 주석입니다.공식적인 봄 주석 중 하나가 아닙니다.

[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:<[[]]>.왜요?

getBeans 사용주석() 메서드를 사용하여 주석이 있는 콩을 가져옵니다.

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

여기 비슷한 논의가 있습니다.

업데이트: Spring 5.2는 다음의 동작을 변경했습니다.context.getBeansWithAnnotation(...)그리고 이제 공장에서 생산된 콩을 올바르게 처리합니다.간단하게 사용해 보세요.

원답


승인된 답변과 Grzegorz의 답변에는 모든 경우에 효과적인 접근법이 포함되어 있지만, 저는 가장 일반적인 경우에도 똑같이 잘 작동하는 훨씬 간단한 접근법을 발견했습니다.

  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 { ... }
  1. 그런 다음 인증된 모든 인스턴스를 주입합니다.@Foo유형 및 작성 방법에 관계없이 다음을 수행할 수 있습니다.

    @Autowired
    @Foo
    List<Object> fooBeans; 
    

fooBeans그러면 a에 의해 생성된 모든 인스턴스가 포함됩니다.@Foo- 질문에 필요한 방법 또는 발견된 방법으로 생성됨@Foo주석이 달린 수업

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

@Autowired
@Foo
List<SpecialFooServiceImpl> fooBeans;

좋은 점은 다른 어떤 것에도 방해가 되지 않는다는 것입니다.@Qualifier(viii) 방법에 대한 설명, 또는@Component유형 수준의 기타 항목.또한 대상 콩에 특정 이름이나 유형을 적용하지 않습니다.

몇 명의 Spring 전문가들의 도움으로, 저는 해결책을 찾았습니다:source의 특성BeanDefinition수 있습니다.AnnotatedTypeMetadata이 인터페이스에 메서드가 있습니다.getAnnotationAttributes()콩 방법의 주석을 얻는 데 사용할 수 있습니다.

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;
}

도우미 클래스 및 테스트 사례의 전체 코드가 포함된 요지

단편소설

그것으로는 충분하지 않습니다.@Foo에서a()만들기 위한 방법a주석이 달린@Foo.

장황한 이야기

스프링 코드 디버깅을 시작하기 전까지는 몰랐습니다.org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(String, Class<A>)이해하는 데 도움이 됐어요

물론 주석을 명명된 클래스로 이동한 경우:

  @Foo
  public static class Named {
  ...

테스트의 일부 세부 사항(해석 대상 등)을 수정합니다.

다시 한 번 생각해 본 결과, 그것은 아주 자연스러운 일입니다.언제getBeansWithAnnotation()라고 불리는데, 봄이 가지고 있는 유일한 정보는 콩입니다.그리고 콩은 물체이고, 물체는 계급을 가지고 있습니다.그리고 Spring은 다음과 같은 추가 정보를 저장할 필요가 없어 보입니다.주석이 달린 콩을 만드는 데 사용된 공장 방법 등은 무엇이었습니까?

EDIT 다음에 대한 주석 보존을 요청하는 문제가 있습니다.@Bean방법: https://jira.springsource.org/browse/SPR-5611

다음 해결 방법으로 "수정하지 않음"으로 닫혔습니다.

  • 용기하를 합니다.BeanPostProcessor
  • 을 합니다.beanName된 BPP를 됩니다.BeanDefinition한 동한것부터로에서.BeanFactory
  • 쿼리대 쿼리BeanDefinition그것 때문에factoryBeanName (그)@Configuration과 콩) 및factoryMethodName (그)@Bean으)로 표시)
  • 반사를 이용하여 그것을 잡습니다.Method에서 유래한 것입니다.
  • 반사를 사용하여 해당 방법의 사용자 정의 주석을 조회합니다.

주석이 달린 모든 콩을 가져오는 방법

context.getBeansWithAnnotation(Foo.class)

이는 다음을 반환합니다.Map<String, Object>여기서 키는 콩 이름입니다.

주석 클래스를 가져오는 방법

context.findAnnotationOnBean(beanName, Foo.class);

값이 때 이 될 수 .@Foo(weight=100)).

이것이 주석이 달린 콩을 얻는 방법입니다.

@Autowired
private ApplicationContext ctx;

public void processAnnotation() {
    // Getting annotated beans with names
    Map<String, Object> allBeansWithNames = ctx.getBeansWithAnnotation(TestDetails.class);
    //If you want the annotated data
    allBeansWithNames.forEach((beanName, bean) -> {
        TestDetails testDetails = (TestDetails) ctx.findAnnotationOnBean(beanName, TestDetails.class);
        LOGGER.info("testDetails: {}", testDetails);
    });
}

경우에는 경에는우내는▁in.getBeansWithAnnotation빈 목록을 반환했습니다.제 실수는 사용자 지정 주석에 보존 및 대상을 추가하지 않은 것입니다.

이 선들을 추가하는 것은 제 주석의 맨 위에 그것을 수정하지 않았습니다.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)

언급URL : https://stackoverflow.com/questions/14236424/how-can-i-find-all-beans-with-the-custom-annotation-foo

반응형