spring 如何找到带有自定义注释@Foo 的所有 bean?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14236424/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 05:37:39  来源:igfitidea点击:

How can I find all beans with the custom annotation @Foo?

springconfigurationannotations

提问by Aaron Digulla

I have this spring configuration:

我有这个弹簧配置:

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

How can I get a list of all beans that are annotated with @Foo?

如何获得所有用 注释的 bean 的列表@Foo

Note: @Foois a custom annotation defined by me. It's not one of the "official" Spring annotations.

注:@Foo是我定义的自定义注解。它不是“官方”Spring 注释之一。

[EDIT]Following the suggestions of Avinash T., I wrote this test case:

[编辑]按照 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" ); }
    }
}

but it fails with org.junit.ComparisonFailure: expected:<[[a]]> but was:<[[]]>. Why?

但它失败了org.junit.ComparisonFailure: expected:<[[a]]> but was:<[[]]>。为什么?

采纳答案by Aaron Digulla

With the help of a couple of Spring experts, I found a solution: The sourceproperty of a BeanDefinitioncan be AnnotatedTypeMetadata. This interface has a method getAnnotationAttributes()which I can use to get the annotations of a bean method:

在几位 Spring 专家的帮助下,我找到了一个解决方案: a 的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;
}

gist with full code of helper class and test case

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

回答by Avinash T.

Use getBeansWithAnnotation()method to get beans with annotation.

使用getBeansWithAnnotation()方法获取带注释的 bean。

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

Hereis similar discussion.

这里有类似的讨论。

回答by kaqqao

While the accepted answer and Grzegorz's answercontain approaches that will work in allcases, I found a much much simpler one that worked equally well for the most common cases.

虽然接受的答案和Grzegorz 的答案包含适用于所有情况的方法,但我发现了一个简单得多的方法,对于最常见的情况同样有效。

1) Meta-annotate @Foowith @Qualifier:

1)元注释@Foo@Qualifier

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

2) Sprinkle @Fooonto the factory methods, as described in the question:

2)洒@Foo在工厂方法上,如问题中所述:

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

But it will also work on the type level:

但它也适用于类型级别:

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

3) Then, inject all the instances qualified by @Foo, regardless of their type and creation method:

3) 然后,注入所有由 限定的实例@Foo,无论它们的类型和创建方法如何:

@Autowired
@Foo
List<Object> fooBeans; 

fooBeanswill then contain all the instances produced by a @Foo-annotated method (as required in the question), or created from a discovered @Fooannotated class.

fooBeans然后将包含由@Foo-annotated 方法生成的所有实例(如问题中所要求),或从发现的带@Foo注释类创建。

The list can additionally be filtered by type if needed:

如果需要,该列表还可以按类型进行过滤:

@Autowired
@Foo
List<SpecialFooServiceImpl> fooBeans;

The good part is that it will not interfere with any other @Qualifier(meta)annotations on the methods, nor @Componentand others on the type level. Nor does it enforce any particular name or type on the target beans.

好的部分是它不会干扰方法上的任何其他@Qualifier(元)注释,也不会干扰@Component类型级别的其他注释。它也不会在目标 bean 上强制执行任何特定的名称或类型。

回答by Grzegorz Oledzki

Short story

短篇故事

It is not enough to put @Fooon the a()method in order to make the abean annotated with @Foo.

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

Long story

很长的故事

I didn't realize it before I started debugging Spring code, a breakpoint at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(String, Class<A>)helped me understand it.

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

Of course, if you moved your annotation to the Named class:

当然,如果您将注释移至 Named 类:

  @Foo
  public static class Named {
  ...

and fixed some minor details of your test (annotation target, etc.) the test works.

并修复了测试的一些小细节(注释目标等),测试有效

After giving it a second thought, it's quite natural. When getBeansWithAnnotation()is called, the only information Spring has are the beans. And beans are objects, objects have classes. And Spring doesn't seem to need to store any additional information, incl. what was the factory method used to create the bean annotated with, etc.

想了想,还是很自然的。当getBeansWithAnnotation()被调用时,Spring 拥有的唯一信息是 bean。bean是对象,对象有类。而且 Spring 似乎不需要存储任何额外的信息,包括。用于创建带有注释的 bean 的工厂方法是什么,等等。

EDITThere is an issue which requests to preserve annotations for @Beanmethods: https://jira.springsource.org/browse/SPR-5611

编辑有一个问题要求保留@Bean方法的注释:https: //jira.springsource.org/browse/SPR-5611

It has been closed as "Won't fix" with the following workaround:

它已通过以下解决方法关闭为“无法修复”:

  • Employ a BeanPostProcessor
  • Use the beanNameprovided to the BPP methods to look up the associated BeanDefinitionfrom the enclosing BeanFactory
  • Query that BeanDefinitionfor its factoryBeanName(the @Configurationbean) and factoryMethodName(the @Beanname)
  • use reflection to get hold of the Methodthe bean originated from
  • use reflection to interrogate any custom annotations from that method
  • 雇用一个 BeanPostProcessor
  • 使用beanName提供给 BPP 的方法BeanDefinition从封闭的BeanFactory
  • 查询BeanDefinition它的factoryBeanName@Configurationbean)和factoryMethodName@Bean名称)
  • 使用反射来获取Method源自的 bean
  • 使用反射从该方法查询任何自定义注释

回答by user3913029

This is how you can beans which are annotated

这就是您可以注释的豆类的方法

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