Подтвердить что ты не робот

Как получить список экземпляров beans из Spring?

У меня есть несколько beans в моем контексте Spring, у которых есть состояние, поэтому я хотел бы reset, что состояние до/после модульных тестов.

Моя идея заключалась в том, чтобы добавить метод к вспомогательному классу, который просто проходит через все beans в контексте Spring, проверяет методы, которые аннотируются с помощью @Before или @After и вызывают их.

Как получить список beans из ApplicationContext?

Примечание. Решения, которые просто перебирают по всем определенным beans, бесполезны, потому что у меня много ленивых beans, и некоторые из них не должны создаваться, потому что это может быть неудачно для некоторых тестов (т.е. я нуждаюсь в beans a java.sql.DataSource, но тесты работают, потому что они не нуждаются в этом bean).

4b9b3361

Ответ 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);
}