Как получить список экземпляров beans из Spring?
У меня есть несколько beans в моем контексте Spring, у которых есть состояние, поэтому я хотел бы reset, что состояние до/после модульных тестов.
Моя идея заключалась в том, чтобы добавить метод к вспомогательному классу, который просто проходит через все beans в контексте Spring, проверяет методы, которые аннотируются с помощью @Before
или @After
и вызывают их.
Как получить список beans из ApplicationContext
?
Примечание. Решения, которые просто перебирают по всем определенным beans, бесполезны, потому что у меня много ленивых beans, и некоторые из них не должны создаваться, потому что это может быть неудачно для некоторых тестов (т.е. я нуждаюсь в beans a java.sql.DataSource
, но тесты работают, потому что они не нуждаются в этом bean).
Ответы
Ответ 1
Например:
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;
}
Ответ 2
Я не уверен, поможет вам это или нет.
Вам нужно создать собственную аннотацию, например. 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());
}
Таким образом, вы можете получить все beans свои пользовательские аннотации.
Ответ 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);
}
}
}
Ответ 4
Я создал gist ApplicationContextAwareTestBase.
Этот вспомогательный класс выполняет две функции:
-
Он устанавливает для всех внутренних полей значение null. Это позволяет Java освобождать память, которая больше не используется. Это менее полезно с Spring (контекст Spring все еще сохраняет ссылки на все beans).
-
Он пытается найти все методы, аннотированные с помощью @After
во всех beans в контексте и вызывая их после теста.
Таким образом, вы можете легко reset состояние ваших синглтонов /mocks без необходимости уничтожать/обновлять контекст.
Пример: у вас есть макет 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(); }
}
В аннотации убедитесь, что после каждого unit test вызывается reset()
для очистки внутреннего состояния.
Ответ 5
Используя предыдущие ответы, я обновил это, чтобы использовать Java 8 Streams 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);
}