Дублирование одноэлементных компонентов Spring bean на компьютерах с низкой производительностью

#java #spring #javafx

#java #spring #javafx

Вопрос:

Итак, у меня действительно странная и тревожная проблема. На наших компьютерах с низкой производительностью некоторые из наших одноэлементных компонентов дублируются во время инициализации контекста Spring. Это происходит только на компьютерах с низкоэффективным оборудованием и происходит последовательно.

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

У меня есть два журнала: один с компьютера с нормальной производительностью и один с компьютера с низкой производительностью. В журналах показаны разница и проблема. Числа в конце строк — это идентификатор экземпляра из метода System.identityHashCode(object). Формат журнала:

 LOGLEVEL  [Thread ID]  LoggingClass  Message
  

На компьютере с нормальной производительностью происходит следующая распечатка.

 INFO  [JavaFX Application Thread]  MainContentPane      Constructor: 869589588
INFO  [JavaFX Application Thread]  MainContentPane      Getting LockScreen In Spring Init....

INFO  [JavaFX Application Thread]  SessionHandler       Constructor: 939274676
INFO  [JavaFX Application Thread]  SessionHandler       Injected MainContentPane Instance: 869589588
INFO  [JavaFX Application Thread]  UserStateBinder      Constructor: 2010765576
INFO  [JavaFX Application Thread]  UserStateBinder      Injected SessionHandler Instance: 939274676
INFO  [JavaFX Application Thread]  LockScreenLockedController Constructor: 1866179042
INFO  [JavaFX Application Thread]  LockScreenLockedController Injected UserStateBinder Instance: 2010765576
INFO  [JavaFX Application Thread]  LockScreen           Constructor: 204176749
INFO  [JavaFX Application Thread]  LockScreen           Injected LockScreenLockedController Instance: 1866179042
INFO  [JavaFX Application Thread]  LockScreen           This instance: 204176749
INFO  [JavaFX Application Thread]  LockScreen           Bean Factory instance: 1371189401
INFO  [JavaFX Application Thread]  MainContentPane      Injected LockScreen Instance: 204176749
  

Здесь дубликатов нет.

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

 INFO  [JavaFX Application Thread]  MainContentPane      Constructor: 22324067
INFO  [JavaFX Application Thread]  MainContentPane      Getting LockScreen In Spring Init....

INFO  [JavaFX Application Thread]  SessionHandler       Constructor: 32463502
INFO  [JavaFX Application Thread]  SessionHandler       Injected MainContentPane Instance: 22324067
INFO  [JavaFX Application Thread]  UserStateBinder      Constructor: 19793387
INFO  [JavaFX Application Thread]  UserStateBinder      Injected SessionHandler Instance: 32463502
INFO  [JavaFX Application Thread]  LockScreenLockedController Constructor: 29065840
INFO  [JavaFX Application Thread]  LockScreenLockedController Injected UserStateBinder Instance: 19793387
INFO  [JavaFX Application Thread]  LockScreen           Constructor: 12729388
INFO  [JavaFX Application Thread]  LockScreen           Injected LockScreenLockedController Instance: 29065840
INFO  [JavaFX Application Thread]  LockScreen           This instance: 12729388
INFO  [JavaFX Application Thread]  LockScreen           Bean Factory instance: 30716643
INFO  [JavaFX Application Thread]  MainContentPane      Injected LockScreen Instance: 12729388

INFO  [JavaFX Application Thread]  SessionHandler       Constructor: 11043228
INFO  [JavaFX Application Thread]  SessionHandler       Injected MainContentPane Instance: 22324067
INFO  [JavaFX Application Thread]  UserStateBinder      Constructor: 24902967
INFO  [JavaFX Application Thread]  UserStateBinder      Injected SessionHandler Instance: 32463502
INFO  [JavaFX Application Thread]  LockScreenLockedController Constructor: 17521714
INFO  [JavaFX Application Thread]  LockScreenLockedController Injected UserStateBinder Instance: 19793387
INFO  [JavaFX Application Thread]  LockScreen           Constructor: 16791356
INFO  [JavaFX Application Thread]  LockScreen           Injected LockScreenLockedController Instance: 29065840
INFO  [JavaFX Application Thread]  LockScreen           This instance: 16791356
INFO  [JavaFX Application Thread]  LockScreen           Bean Factory instance: 30716643
  

Здесь мы можем видеть, что существует второй набор экземпляров, созданных для всех классов, за исключением класса MainContentPane . Новый набор классов является зависимостью, введенной с предыдущим набором экземпляров (проверьте идентификаторы), а фабрика компонентов — это тот же экземпляр, что и раньше.

Все эти сообщения печатаются в основном потоке (потоке приложения JavaFX), поэтому, похоже, проблемы с параллелизмом также нет.

Проект также включает встроенный http-сервер Jetty. Я не знаю, есть ли что-нибудь в Jetty, что может вызвать эту проблему на компьютере с низкой производительностью.

Версии:

 JRE(incl. JavaFX): 1.8.0.101
Spring: 4.3.3.RELEASE
Jetty   Websocket: 9.3.6.v20151106
  

Я подозревал, что эта проблема, возможно, была решена путем установки параметра контекста Spring setAllowBeanDefinitionOverriding(false). Но это тоже не помогло.

 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.setAllowBeanDefinitionOverriding(false);
        context.register(ClientContext.class,
                MainContext.class,
                CommonContext.class,
                CciContext.class,
                PersistenceContext.class,
                SimulatorContext.class);
        context.refresh();
  

Пожалуйста, скажите мне, есть ли какая-либо дополнительная информация, которая вам нужна, и спасибо за любое время, которое вы потратили на то, чтобы помочь мне.

Редактировать:

Теперь я подтвердил, что вся инициализация и весь доступ к компонентам происходят из одного потока. Я смог найти только два интересных факта в журнале трассировки. Во-первых, кажется, что вся инициализация происходит в том же порядке, даже если это точно такая же версия программного обеспечения (инициализируется ли Spring в paralell?). Во-вторых, компьютеры со средней производительностью имеют дублирование! У них есть только дублирование (или, может быть, это повторная инициализация?) в классе UserStateBinder. Высокопроизводительный компьютер разработки вообще не имеет этой проблемы.

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

Комментарии:

1. Итак, циклическая ссылка на компонент возникает, когда она устанавливает ваш LockScreen в MainContentPane ? Одинаковые ли версии JVM на каждом компьютере?

2. Циклические ссылки: LockScreen -> LockScreenLockedController -> UserStateBinder -> SessionHandler -> MainContentPane -> LockScreen, и это решается путем разрешения MainContentPane получать компонент LockScreen в его методе Spring init вместо его конструктора. Что касается JVM, да, она одинакова на всех компьютерах. JRE поставляется в комплекте с приложением для его обеспечения.

3. Это звучит действительно непонятно. Можете ли вы попробовать debbugger (с eclipse, если возможно) на своих медленных компьютерах?

4. Я думаю, что обвинение в этой проблеме «медленного» компьютера является действительно маловероятной основной причиной. Но, к счастью, нам не нужно гадать: не могли бы вы увеличить свой уровень журнала до TRACE (убедитесь, что org.springframework пакет включен) и загрузить два файла журнала в pastbin или что-то в этом роде? (они будут длинными)

5. Я ожидаю, что при параллельном чтении двух журналов любой может обнаружить проблему. Они будут синхронизированы в течение некоторого времени, затем один из них начнет повторную инициализацию компонентов. Что бы ни произошло непосредственно перед этим, это ваша подсказка. Если вы не можете предоставить доступ к журналам, просто выполните этот анализ самостоятельно.

Ответ №1:

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

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

Проблема возникала только в циклической зависимости LockScreen -> LockScreenLockedController -> UserStateBinder -> SessionHandler -> MainContentPane -> LockScreen. Циклическая зависимость была нарушена путем перемещения зависимости конструктора в MainContentPane в метод инициализации Spring. Код выглядел примерно так.

 @Bean(initMethod = "init")
public MainContentPane mainContentPane() {...}

@Bean
public LockScreen lockScreen() {...}

@Bean
public LockScreenLockedController lockScreenLockedController() {...}

@Bean
public UserStateBinder userStateBinder() {...}

@Bean
public SessionHandler sessionHandler() {...}
  

Решение заключалось в явном объявлении зависимостей в Spring с аннотацией @dependsOn, подобной этой.

 @Bean(initMethod = "init")
public MainContentPane mainContentPane() {...}

@Bean
@DependsOn("lockScreenLockedController")
public LockScreen lockScreen() {...}

@Bean
@DependsOn("userStateBinder")
public LockScreenLockedController lockScreenLockedController() {...}

@Bean
@DependsOn("sessionHandler")
public UserStateBinder userStateBinder() {...}

@Bean
@DependsOn("mainContentPane")
public SessionHandler sessionHandler() {...}
  

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