#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 ведет себя таким образом в зависимости от производительности компьютера.