Почему Spring получает проблемы с циклической зависимостью на одной машине, а не другой?

У меня возникла проблема с приложением приложения Spring Data для работы в моей среде. Я запускаю Debian, но мои сотрудники либо используют Mac, либо Ubuntu. У меня нет ничего особенного в моих переменных окружения, и я использую ту же самую версию Java, что и другие.

Я видел это в журналах, предлагая, что это круговая справочная проблема, которая приводит к сбою создания:

nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'flyway.CONFIGURATION_PROPERTIES':
Initialization of bean failed;
...
nested exception is
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'flyway': Requested bean is currently in
creation: Is there an unresolvable circular reference?

Таким образом, проблема заключается в том, что пролетная дорога нуждается в некоторых зависимостях, и им нужен пролет.

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

Возможно ли, что autowiring Spring каким-то образом запутался и пытается сделать что-то в неправильном порядке, так что репозиторий имеет значение null, когда он пытается его установить?

Имеет ли Spring плохо реализованная топологическая сортировка для упорядочения зависимостей?

Почему это будет плохо работать в моей среде?

Невозможно ли упорядочить путь класса к его поведению?

======================

Приложение не начнет с этой ошибки:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contentItemRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Repository interface must not be null on initialization!
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:175)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:127)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:251)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1127)

============================

Подпись ContentItemRepository:

@Repository
@Transactional
public interface ContentItemRepository extends JpaRepository<ContentItem, String>, JpaSpecificationExecutor<ContentItem> {

============================

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

"contentItemRepository", который не может быть нулевым, следующий:

@Component
+public class UrlAliasRequestConverter implements Mapper<UrlAliasRequest, UrlAlias> {
+
+    /**
+     * The content item contentItemType repository.
+     */
+    @Autowired
+    private ContentItemRepository contentItemRepository;

Ответ 1

Я не понимаю, почему это произошло, но вот единственное решение, с которым я столкнулся:

Установите Debian 8, и он работает.

Я попробовал это в другой чистой установке Debian 7, и там было меньше ошибок, но у меня все получилось. Чистая установка Debian 8, похоже, работала.

Я могу только заключить, что Java должна вызывать некоторую системную библиотеку, которая каким-то образом влияет на порядок, в котором разрешены зависимости Spring. Эта библиотека должна быть обновлена ​​в Debian 8, что приведет меня в соответствие с установками Ubuntu, которые используют другие разработчики и продукты.

Я не знаю, что это за библиотека... Что-то сканирует файлы в файловой системе, сообщая о них в другом порядке? Что-то, что распаковывает файл .jar, сообщая о его содержимом в другом порядке?

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

Слишком много Spring магии, держащей карточный домик, если вы спросите меня. Мои другие проекты находятся на DropWizard, и там инъекция зависимостей вручную закодирована, поэтому никаких сюрпризов.

=== Обновление

Я обновил окно Debian 7 до 8, и проблема все еще сохраняется. Поэтому моя гипотеза о том, что она является версией библиотеки, неверна. Должно быть, что-то в моей среде.

Я создал нового пользователя на коробке. Проблема все еще существует для этого пользователя. Что-то в этой коробке это действительно не нравится, но я не могу понять, что это такое.

Я хотел бы получить истинную причину и понять ее, но я не думаю, что могу действительно посвятить больше времени, чтобы понять это.

В любом случае, чистая установка Debian 8 решает проблему.

Ответ 2

Это очень вероятно связано с порядком, в котором файлы классов читаются в строке

dir.listFiles() в  PathMatchingResourcePatternResolver.doRetrieveMatchingFiles()

Так как порядок перечисления файлов (файлы классов) зависит от платформы, и сортировка не выполняется в массиве, порядок загрузки классов зависит от того, как платформа возвращает их.

ref: http://forum.spring.io/forum/spring-projects/container/115998-circular-dependency-identification-inconsistent

Ответ 3

У меня такая же проблема на Ubuntu 16.04.

Я обнаружил, что проблема с

@ComponentScan(basePackages = "com.my.app")

В коде работает как минимум 5 разных машин (windows, ubuntu 15.04 и ubuntu 16.04), но не запускает наш CI-сервер (сервер ubuntu 16.04).

После того, как я изменил

@ComponentScan(basePackages = "com.my.app")

к

@ComponentScan(basePackages = {"com.my.app.service", "com.my.app.config", "com.my.app"})

код также запущен на сервере CI.

Я думаю, что это проблема Spring с загрузчиком beans...

UPDATE:

https://github.com/spring-projects/spring-boot/issues/6045

https://jira.spring.io/browse/SPR-14307

Ответ 4

Как расширяется ваш интерфейс репозитория? Вы можете посмотреть исходный код Spring и посмотреть, почему генерируется исключение:

https://github.com/spring-projects/spring-data-commons/blob/master/src/main/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupport.java

@SuppressWarnings("unchecked")
public Class<? extends T> getObjectType() {
    return (Class<? extends T>) (null == repositoryInterface ? Repository.class : repositoryInterface);
}

Вот пример моего репозитория:

@Repository
public interface GameRepository extends JpaRepository<Game, Long> {

Ответ 5

Я не знаю, почему эта конкретная проблема возникла на одном компьютере, а не на другом, но я думаю, что проблема, скорее всего, заключается в том, что оба beans используют инъекцию конструктора для передачи ссылок друг другу, которые создают неразрешимую круговую зависимость - Потребности B для его построения, которые, в свою очередь, нуждаются в A, могут быть созданы без создания другого уже созданного. Если для обоих объектов требуется ссылка на другую, вам нужно вместо этого передать ее через инъекцию установщика после того, как объекты уже созданы. Ответы на этот вопрос имеют отношение к проблеме, с которой вы сталкиваетесь.

Ответ 6

У меня такая же проблема вчера. Я не знаю, почему, но я нашел способ решить проблему: отметьте все beans, участвующие в круговом дереве зависимостей, как lazy-init. Не только прямые, зависящие друг от друга, но и все beans, которые ссылаются на них!

Проект является старым, поэтому он использует только spring 3. Я не уверен, что еще более поздние версии spring получают эту проблему.