2013-01-09 13 views
21

mam tej konfiguracji wiosny:Jak mogę znaleźć wszystkie fasolki z niestandardową adnotacją @Foo?

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

Jak mogę uzyskać listę wszystkich ziaren, które są opatrzone @Foo?

Uwaga: @Foo to niestandardowa adnotacja zdefiniowana przeze mnie. To nie jest jedna z "oficjalnych" adnotacji wiosennych.

[EDIT] Po sugestii Avinash T., napisałem ten przypadek testowy:

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

ale nie jest on z org.junit.ComparisonFailure: expected:<[[a]]> but was:<[[]]>. Czemu?

Odpowiedz

16

Z pomocą kilku ekspertów od wiosny, znalazłem rozwiązanie: właściwość source z BeanDefinition może być StandardMethodMetadata. Ta klasa ma metodę getAnnotationAttributes() których można użyć, aby uzyskać adnotacje o sposobie Fasola:

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 StandardMethodMetadata) { 
      StandardMethodMetadata metadata = (StandardMethodMetadata) 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

26

Użyj metody getBeansWithAnnotation(), aby uzyskać fasolę z adnotacją.

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

Here to podobna dyskusja.

+0

Dzięki, zupełnie to przegapiłem.Napisałem test, ale test się nie udał (zobacz moje edytowane pytanie). Każdy pomysł, dlaczego? –

+0

@AaronDigulla ..... twój assertEquals() rzuca ten wyjątek. –

+0

Dzieje się tak, ponieważ 'getBeansWithAnnotation()' zwraca pustą mapę -> nie znajduje żadnych fasoli. Dlaczego? –

6

Krótka historia

Nie wystarczy umieścić @Foo na metodzie a() w celu uczynienia a fasoli uwagami z @Foo.

Długa historia

nie zdawałem sobie sprawy, zanim zacząłem debugowanie kodu wiosna, punkt przerwania na org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(String, Class<A>) pomógł mi zrozumieć.

Oczywiście, jeśli przeniósł swoją adnotację dla podanej klasy:

@Foo 
    public static class Named { 
    ... 

i poprawiono kilka drobnych szczegółów testu (tarcza adnotację, itd.) test działa.

Po chwili zastanowienia jest to całkiem naturalne. Po wywołaniu getBeansWithAnnotation() jedyną informacją, która zawiera Spring, są ziarna. A fasola to przedmioty, obiekty mają klasy. A Spring wydaje się nie przechowywać żadnych dodatkowych informacji, w tym. jaka była fabryczna metoda używana do tworzenia fasoli opisywana przez itp.

EDIT Jest to problem, który prosi o zachowanie adnotacji dla @Bean metod: https://jira.springsource.org/browse/SPR-5611

Został zamknięty jako "Will not fix" z następującym Obejście:

  • zatrudnić BeanPostProcessor
  • Skorzystaj z beanName dostarczonej do metod BPP w celu odszukania powiązanego BeanDefinition z obejmującego BeanFactory
  • Query że BeanDefinition dla jego factoryBeanName (w @Configuration fasoli) i factoryMethodName (nazwa @Bean)
  • użycie odbicie zdobyć na Method fasola pochodzi z
  • użyć refleksji przesłuchiwać wszelkie niestandardowe adnotacje z tej metody
+0

Dzięki. Szkoda, że ​​nie mogę używać adnotacji do tworzenia grup fasoli w mojej konfiguracji Spring bez zmiany typów, ale prawdopodobnie znajdę sposób obejścia tego. –

1

Choć przyjął odpowiedź i Grzegorz's answer zawierać podejścia, które będą pracować w wszystkich przypadkach znalazłem o wiele prostszy, który działał równie dobrze w najczęstszych przypadkach.

1) Meta-annotate @Foo z @Qualifier:

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

2) Posypać @Foo na metodach fabrycznych, jak opisano w pytaniu:

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

ale również pracować od rodzaju poziom:

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

3) Następnie wstrzyknij ll przypadki zakwalifikowane przez @Foo, niezależnie od ich rodzaju i tworzenia sposób:

@Autowired 
@Foo 
List<Object> fooBeans; 

fooBeans następnie zawierać wszystkie przykłady wytwarzanych przez @Foo metodą -annotated (jak określono w kwestii) lub utworzone z odkryto @Foo opatrzona komentarzem.

Listę można dodatkowo filtrować według typu, jeżeli potrzebne:

@Autowired 
@Foo 
List<SpecialFooServiceImpl> fooBeans; 

Dobre jest to, że nie będzie kolidować z innymi @Qualifier (meta) adnotacje na temat metod, ani @Component i innych na danym typie poziom. Nie wymusza również żadnej konkretnej nazwy lub typu na ziarnie docelowym.

Powiązane problemy