Переопределить default-lazy-init = true для Spring bean определений

Я поддерживаю большую систему Java EE. Большая часть бизнес-логики преобразуется из EJB: s в POJO: s настраивается в нескольких файлах конфигурации контекста spring. EJB: s в основном используются как Facades, который ищет бизнес-логику spring beans из контекста, состоящего из всех файлов конфигурации контекста spring, упомянутых ранее. Для этого мы используем AbstractStatelessSessionBean, снабженный инфраструктурой spring.

Все эти файлы конфигурации имеют директиву по умолчанию-lazy-init = true, а это означает, что бизнес-логика beans не создается, пока они фактически не используются системой. Это предпочтительнее в большинстве случаев, поскольку повторная публикация в режиме разработчика становится быстрее.

Но когда выполняются большие слияния, у нас возникают проблемы с поиском всех ошибок конфигурации, таких как отсутствующие зависимости.

Моя идея - написать некоторую форму теста интеграции с целью найти эти ошибки. Это значит, я думаю, что мне нужно найти способ переопределить все объявления lazy-init = true по умолчанию при создании контекста приложения.

Есть ли какой-либо способ сделать это программно или, возможно, с помощью некоторого тестового файла контекста, который включает все фактические файлы контекста?

Ответы

Ответ 1

Скажем, в настоящее время у вас есть один файл applicationContext.xml, содержащий все определения bean:

<beans default-lazy-init="true">

    <!-- all your beans -->

</beans>

Переименуйте его в applicationContext-main.xml или что-нибудь и удалите атрибут default-lazy-init="true". Теперь создайте два applicationContext.xml файла:

<beans default-lazy-init="true">

    <import resource="applicationContext-core.xml"/>

</beans>

и

<beans default-lazy-init="false">

    <import resource="applicationContext-core.xml"/>

</beans>

Как вы видите, единственное различие - это значение default-lazy-init. Во время разработки ваша команда может использовать прежнюю версию applicationContext.xml, которая включает в себя все beans с lazy-init. В средах промежуточной и тестовой среды переключитесь на последнюю, чтобы все beans, включенные в applicationContext-core.xml создавались с нетерпением.

Ответ 2

Я считаю, что лучший способ контролировать ленивый init beans - оставить по умолчанию lazy-init из всех файлов конфигурации, кроме самого верхнего, как предлагает Tomasz Nurkiewicz. Однако в этом случае мне нужно быстрое и грязное исправление, чтобы проверить все определения bean. (Это немного процесс изменения ленивой политики инициализации.)

Я придумал простой BeanFactoryPostProcessor, который, похоже, выполняет эту работу:

public class NonLazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            beanFactory.getBeanDefinition(beanName).setLazyInit(false);
        }
    }
}

Если он включен в файл контекста, он переопределит ленивый флаг инициализации, заданный любыми включенными файлами контекста.

<beans default-lazy-init="false">
    <bean class="example.NonLazyInitBeanFactoryPostProcessor" />
    <import resource="applicationContext-core.xml"/>
</beans>

Если я попытаюсь создать контекст из вышеуказанного xml, ошибки конфигурации, ранее скрытые ленивой инициализацией, немедленно появятся в качестве исключений.

Ответ 3

В этом PostProcessor есть один "но"

for (String beanName : beanFactory.getBeanDefinitionNames()) {
        beanFactory.getBeanDefinition(beanName).setLazyInit(false);
    }

Этот цикл цикла будет перебирать только верхний максимум beans, не включая, например, внутренние (локальные) bean defintions...

Ответ 4

Вы не можете получить доступ к сканеру из контекста - он полностью закрыт, но поскольку вы можете входить в исходный код, вы можете видеть, что нужно для настройки самостоятельно. Я использовал Spring собственный ReflectionTestUtils для установки моего собственного настроенного сканера в контексте:

    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
    BeanDefinitionDefaults defaults = new BeanDefinitionDefaults();
    defaults.setLazyInit(true);
    scanner.setBeanDefinitionDefaults(defaults);
    ReflectionTestUtils.setField(context, "scanner", scanner);
    context.scan("com.some.path");

Вы можете сделать это в любом месте, где есть доступ к Контексту приложения, прежде чем произойдет сканирование компонентов.