2013-01-09 34 views
21

我有這個春天的配置:如何使用自定義註釋@Foo查找所有bean?

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

我怎樣才能被標註有@Foo所有Bean的列表?

注:@Foo是由我定義的自定義註釋。這不是「官方」的Spring註釋之一。

[編輯]繼阿維納什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:<[[]]>。爲什麼?

回答

16

在幾位Spring專家的幫助下,我找到了一個解決方案:BeanDefinitionsource屬性可以是StandardMethodMetadata。這個類有一個方法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 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

使用getBeansWithAnnotation()方法來獲取具有註釋的bean。

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

Here是類似的討論。

+0

謝謝,我完全錯過了。我寫了一個測試用例,但測試失敗(請參閱我編輯的問題)。任何想法爲什麼? –

+0

@AaronDigulla .....你的assertEquals()拋出這個異常。 –

+0

這是因爲'getBeansWithAnnotation()'返回一個空映射 - >它沒有找到任何bean。這是爲什麼? –

6

短篇小說

這是不夠的,把@Fooa()方法,以使a豆與@Foo註解。

說來話長

我沒有意識到這一點之前,我開始調試春天代碼,在org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(String, Class<A>)斷點幫助我理解它。

當然,如果你移動你的註釋命名類:

@Foo 
    public static class Named { 
    ... 

和固定測試(註釋目標等)的一些小細節測試工作

經過再次思考之後,這很自然。當調用getBeansWithAnnotation()時,Spring唯一的信息就是bean。而豆是對象,對象有類。而且Spring似乎不需要存儲任何額外的信息,包括。用於創建註釋的bean的工廠方法是什麼,等等。

編輯有哪些請求保留註解@Bean方法的問題:https://jira.springsource.org/browse/SPR-5611

它已被封閉,「不會解決」用以下解決方法:

  • 採用BeanPostProcessor
  • 使用提供給BPP方法的beanName從封閉的BeanFactory
  • 中查找關聯的 BeanDefinition
  • 查詢BeanDefinitionfactoryBeanName(在@Configuration豆)和factoryMethodName(該@Bean名)
  • 使用反射來獲得Method豆源於
  • 使用反射保持從該方法詢問任何自定義註釋
+0

謝謝。很遺憾,我不能在不改變類型的情況下使用註釋在我的Spring配置中創建bean組,但我可能會找到解決方法。 –

1

雖然接受的答案和Grzegorz's answer包含將在所有案件工作方法,我發現這是一個非常簡單的方法,對於最常見的情況也同樣適用。

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)然後,注入LL實例由@Foo合格,而不管它們的類型和創建方法:

@Autowired 
@Foo 
List<Object> fooBeans; 

fooBeans然後將包含由一個@Foo -annotated方法(如在問題必需的)中產生的所有實例,或通過發現的@Foo創建帶註釋的類。

列表可以另外通過型,如果需要進行過濾:

@Autowired 
@Foo 
List<SpecialFooServiceImpl> fooBeans; 

好部分是,它不會與任何其他@Qualifier(甲基)上的方法的說明,也不@Component等人對型干擾水平。它也不強制目標bean上的任何特定名稱或類型。