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
How can I find all beans with the custom annotation @Foo?
提问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;
}
回答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.
这是不够的,放@Foo的a()方法,以使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 associatedBeanDefinitionfrom the enclosingBeanFactory - Query that
BeanDefinitionfor itsfactoryBeanName(the@Configurationbean) andfactoryMethodName(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);
});
}

