我如何才能找到与自定义注解@foo所有豆?(How can I find all beans wit

2019-07-18 01:04发布

我有这个春天的配置:

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

我怎样才能被标注了所有Bean的列表@Foo

注: @Foo是我定义的自定义注释。 这不是“官”春注解之一。

[编辑]继阿维纳什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:<[[]]> 。 为什么?

Answer 1:

使用getBeansWithAnnotation()方法来获取豆注解。

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

下面是类似的讨论。



Answer 2:

一对夫妇春天专家的帮助下,我找到了一个解决方案: source一的财产BeanDefinition可以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;
}

要点用的辅助类和测试用例完整的代码



Answer 3:

虽然接受的答案和格热戈日的回答包含将在所有情况下的工作方式,我发现了一个非常非常简单的一个工作同样适用于最常见的情况。

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等人对类型级别。 它也没有强制任何特定的名称或键入目标豆。



Answer 4:

短篇故事

这是不够的,把@Fooa()方法,以使a带注释的bean @Foo

很长的故事

我没有意识到这一点之前,我开始调试春天代码,在断点处org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(String, Class<A>)帮助我理解它。

当然,如果你移动你的注释命名类:

  @Foo
  public static class Named {
  ...

固定测试的一些小细节(注释目标等) 的测试工作

给它一个第二个想法后,这是很自然的。 当getBeansWithAnnotation()被调用时,只有信息春回大地是豆类。 和豆类是对象,对象有类。 而春天似乎并不需要存储的任何其他信息,含。 什么是用于创建注释豆等工厂方法

编辑有哪些请求保留注解问题@Bean方法: https://jira.springsource.org/browse/SPR-5611

它已经被标记为“不会解决”用以下解决方法:

  • 采用BeanPostProcessor
  • 使用beanName提供给BPP方法来查找相关BeanDefinition从封闭BeanFactory
  • 查询BeanDefinitionfactoryBeanName (在@Configuration豆)和factoryMethodName (该@Bean名)
  • 使用反射来得到的保持Method豆源于
  • 使用反射来从该方法询问任何定制注释


Answer 5:

这是如何可以菜豆,注释

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


文章来源: How can I find all beans with the custom annotation @Foo?