java - 低性能计算机上的 Spring bean 单例复制

标签 java spring javafx

所以我遇到了这个非常奇怪且令人不安的问题。在我们的低性能计算机上,一些单例 bean 在 Spring Context 初始化期间被复制。这种情况仅发生在硬件性能较低的计算机上,并且始终会发生。

到目前为止,我可以告诉的是,这似乎发生在一堆处于循环依赖关系中的类上,我怀疑它可能与 bean init 方法有关。通过 init 方法而不是构造函数将 LockScreen 注入(inject)到 MainContentPane 中,解决了循环依赖问题。

我有两份日志,一份来自正常性能计算机,一份来自低性能计算机。日志显示了差异和问题。行末尾的数字是 System.identityHashCode(object) 方法中的实例 ID。日志格式为:

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 类之外,还创建了所有类的第二组实例。新的类集与前一组实例进行依赖注入(inject)(检查 id),并且 bean 工厂与之前的实例相同。

所有这些消息都打印在主线程(JavaFX 应用程序线程)上,因此似乎也不存在并发问题。

该项目还包括一个嵌入式 Jetty http 服务器。我不知道 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();

如果您需要任何其他信息,请告诉我,感谢您花时间帮助我。

编辑:

我现在已经确认所有初始化和所有 bean 访问都是在单个线程中发生的。 我只能在跟踪日志中找到两个有趣的事实。 首先,似乎所有初始化都以相同的顺序发生,即使它是完全相同的软件版本(Spring 是否并行初始化?)。 其次,中等性能的计算机确实存在重复!它们仅在 UserStateBinder 类上有重复(或者可能是重新初始化?)。高性能开发计算机根本不存在这个问题。

从明天开始,我们可能会从我们的项目中删除 spring,因为我们无法找到此问题的解决方案。如果其他人希望我测试任何理论,我仍然可以使用该项目的当前版本。

最佳答案

因此,经过大量进一步的测试,我能够解决问题,但找不到奇怪行为的根本原因。

我怀疑该行为是由 Spring 初始化 bean 引起的,并且具有某种改变行为的时间依赖性。我不能确定这是否是并行性的结果。

该问题仅发生在循环依赖LockScreen -> LockScreenLockedController -> UserStateBinder -> SessionHandler -> MainContentPane -> LockScreen 中。通过将 MainContentPane 中的构造函数依赖项移至 Spring init 方法,打破了循环依赖项。代码看起来像这样。

@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 会根据计算机的性能表现出这种行为。

关于java - 低性能计算机上的 Spring bean 单例复制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39793687/

相关文章:

java - Crashlytics Multiple dex files define 错误

java - Android - 将图像设置为用户个人资料的 imageview

javafx - 使用 JavaFX 3D 创建坐标网格的最实用方法是什么?

JavaFX 调用目标异常

JavaFX 静态与非静态

java - 尝试从 Java Applet 保存网络摄像头图像时出现 NullPointerException

java - 在 JSPF 中获取当前页面的完整 url

java - 在xml中设置bean后,如何实例化对象?

java - 如何在不使用包装对象的情况下反序列化前面带有前导标签的数组?

java - 将 bean 注入(inject)到 Spring 托管上下文之外的类中