¿Cómo puedo encontrar todos los beans con la anotación personalizada @Foo?

Tengo esta configuración de spring:

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

¿Cómo puedo obtener una lista de todos los beans que están anotados con @Foo ?

Nota: @Foo es una anotación personalizada definida por mí. No es una de las anotaciones de spring “oficiales”.

[EDITAR] Siguiendo las sugerencias de Avinash T., escribí este caso de prueba:

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

pero falla con org.junit.ComparisonFailure: expected: but was: . ¿Por qué?

Use el método getBeansWithAnnotation () para obtener beans con anotación.

 Map beans = applicationContext.getBeansWithAnnotation(Foo.class); 

Aquí hay una discusión similar.

Con la ayuda de un par de expertos de Spring, encontré una solución: la propiedad de source de un BeanDefinition puede ser AnnotatedTypeMetadata . Esta interfaz tiene un método getAnnotationAttributes() que puedo usar para obtener las anotaciones de un método de bean:

 public List getBeansWithAnnotation( Class type, Predicate> attributeFilter ) { List 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 attributes = metadata.getAnnotationAttributes( type.getName() ); if( null == attributes ) { continue; } if( attributeFilter.apply( attributes ) ) { result.add( name ); } } } return result; } 

esencia con código completo de clase auxiliar y caso de prueba

Cuento

No es suficiente poner @Foo en el método a a() para hacer que a bean sea anotado con @Foo .

Larga historia

No me había dado cuenta antes de comenzar a depurar el código de Spring, un punto de interrupción en org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(String, Class) me ayudó a entenderlo.

Por supuesto, si moviste tu anotación a la clase Nombrada:

  @Foo public static class Named { ... 

y corrigió algunos detalles menores de su prueba (objective de anotación, etc.), la prueba funciona .

Después de pensarlo dos veces, es bastante natural. Cuando se llama a getBeansWithAnnotation() , la única información que Spring tiene son los beans. Y los frijoles son objetos, los objetos tienen clases. Y Spring no parece necesitar almacenar información adicional, incl. cuál fue el método de fábrica utilizado para crear el bean anotado, etc.

EDITAR Existe un problema por el cual se solicitan preservar las anotaciones para los métodos @Bean : https://jira.springsource.org/browse/SPR-5611

Se ha cerrado como “No se solucionará” con la siguiente solución alternativa:

  • Emplee un BeanPostProcessor
  • Use beanName proporcionado a los métodos de BPP para buscar BeanDefinition asociado desde BeanDefinition BeanFactory
  • Consulte esa BeanDefinition para su factoryBeanName (el bean @Configuration ) y factoryMethodName (el nombre @Bean )
  • utilice la reflexión para obtener el Method del que se originó el grano
  • use la reflexión para interrogar cualquier anotación personalizada de ese método

Si bien la respuesta aceptada y la respuesta de Grzegorz contienen enfoques que funcionarán en todos los casos, encontré uno mucho más simple que funcionó igualmente bien para los casos más comunes.

1) @Foo con @Qualifier :

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

2) Rocíe @Foo en los métodos de fábrica, como se describe en la pregunta:

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

Pero también funcionará en el nivel de tipo:

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

3) Luego, inyecte todas las instancias calificadas por @Foo , independientemente de su tipo y método de creación:

 @Autowired @Foo List fooBeans; 

fooBeans contendrá todas las instancias producidas por un método @Foo -anotado (como se requiere en la pregunta), o creado a partir de una clase anotada @Foo .

La lista también puede ser filtrada por tipo si es necesario:

 @Autowired @Foo List fooBeans; 

La parte buena es que no interferirá con ninguna otra @Qualifier (meta) anotaciones en los métodos, ni @Component y otros en el nivel de tipo. Tampoco impone ningún nombre o tipo particular en los beans objective.