2013-02-12 132 views
17

我的Spring環境中有幾個bean有狀態,所以我想在單元測試之前/之後重置該狀態。如何從Spring獲取實例化bean的列表?

我的想法是添加一個方法到一個助手類,它只是通過Spring上下文中的所有bean,檢查用@Before@After註釋並調用它們的方法。

如何獲得實例化的豆類列表ApplicationContext

注:簡單地遍歷所有定義的bean是無用的,因爲我有很多懶豆,其中一些不能被實例化,因爲這將失敗的一些測試(即我有需要java.sql.DataSource一個咖啡豆,但測試解決方案因爲他們不需要這個bean)。

+1

也許定義 「後」 切入點HTTP:/ /static.springsource.org/spring/docs/3.0.x/reference/aop.html – 2013-02-12 11:52:15

+0

你的單元測試中的@DirtiesContext註釋沒有做到你想要的嗎? – 2013-02-12 12:30:32

+0

@NicolasMommaerts:不。重新設置幾個bean會比從頭創建整個上下文便宜得多。 – 2013-02-12 14:50:01

回答

21

例如:

public static List<Object> getInstantiatedSigletons(ApplicationContext ctx) { 
      List<Object> singletons = new ArrayList<Object>(); 

      String[] all = ctx.getBeanDefinitionNames(); 

      ConfigurableListableBeanFactory clbf = ((AbstractApplicationContext) ctx).getBeanFactory(); 
      for (String name : all) { 
       Object s = clbf.getSingleton(name); 
       if (s != null) 
        singletons.add(s); 
      } 

      return singletons; 

    } 
+0

'getSingleton()'完全適合我。更新了我的要點(請參閱我自己的回答) – 2013-02-12 15:24:31

+0

@AaronDigulla我正在使用'Spring-WebMVC'。我怎樣才能訪問我的'DispatcherServlet'的'WebApplicationContext'?沒有它,我無法迭代instanciated bean。 – smwikipedia 2015-11-03 07:41:47

+1

@AaronDigulla我解決了它。我可以使用'ApplicationContextAware'接口。參考:http://stackoverflow.com/questions/9602664/print-all-the-spring-beans-that-areloaded – smwikipedia 2015-11-03 08:54:39

3

我不確定這是否會對您有所幫助。

您需要創建自己的註釋,例如。 MyAnnot。 並將該註釋放在您想要獲得的類上。 然後使用下面的代碼,你可能會得到實例化的bean。

ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); 
scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnot.class)); 
for (BeanDefinition beanDefinition : scanner.findCandidateComponents("com.xxx.yyy")){ 
    System.out.println(beanDefinition.getBeanClassName()); 
} 

這樣你就可以得到所有具有自定義註釋的bean。

0

我創建了一個gist ApplicationContextAwareTestBase

這個輔助類做了兩兩件事:

  1. 它集所有內部字段設置爲null。這允許Java釋放不再使用的內存。儘管如此,它對Spring的用處不大(Spring上下文仍然保留對所有bean的引用)。

  2. 它試圖在上下文中的所有bean中查找註釋爲@After的所有方法,並在測試後調用它們。

這樣,你可以很容易地重置你的單身/嘲笑的狀態,而不必摧毀/刷新上下文。

示例:您有一個模擬DAO:

public void MockDao implements IDao { 

    private Map<Long, Foo> database = Maps.newHashMap(); 

    @Override 
    public Foo byId(Long id) { return database.get(id)); 

    @Override 
    public void save(Foo foo) { database.put(foo.getId(), foo); } 

    @After 
    public void reset() { database.clear(); } 
} 

註釋將確保reset()將每個單元測試後,被要求清理內部狀態。

3

我必須改善它一點

@Resource 
AbstractApplicationContext context; 

@After 
public void cleanup() { 
    resetAllMocks(); 
} 

private void resetAllMocks() { 
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); 
    for (String name : context.getBeanDefinitionNames()) { 
     Object bean = beanFactory.getSingleton(name); 
     if (Mockito.mockingDetails(bean).isMock()) { 
      Mockito.reset(bean); 
     } 
    } 
} 
0

使用以前的答案,我已經更新了這個用Java 8流API:

@Inject 
private ApplicationContext applicationContext; 

@Before 
public void resetMocks() { 
    ConfigurableListableBeanFactory beanFactory = ((AbstractApplicationContext) applicationContext).getBeanFactory(); 
    Stream.of(applicationContext.getBeanDefinitionNames()) 
      .map(n -> beanFactory.getSingleton(n)) 
      // My ConfigurableListableBeanFactory isn't compiled for 1.8 so can't use method reference. If yours is, you can say 
      // .map(ConfigurableListableBeanFactory::getSingleton) 
      .filter(b -> Mockito.mockingDetails(b).isMock()) 
      .forEach(Mockito::reset); 
}