【问题标题】:Spring bean creation flow: how to know when it is finished?Spring bean 创建流程:如何知道何时完成?
【发布时间】:2016-12-13 09:55:48
【问题描述】:

我有一个 spring 应用程序,并且有一个预定逻辑以固定速率生成原型 bean。

在我的笔记本电脑上一切正常,但在将其部署到服务器后,我的应用无法启动,原因是:

2016-12-13 04:13:01.885 ERROR 4688 --- [TaskScheduler-1] o.s.s.s.TaskUtils$LoggingErrorHandler    : Unexpected error occurred in scheduled task.
...
BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'metaDataSourceAdvisor': Singleton bean creation not allowed
 while the singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
...

这是第一个预定通话。看起来是时间问题。在我的笔记本电脑上,应用程序的初始化速度要快得多,并且第一次计划调用(生成原型 bean)没有发生任何错误。

有没有办法了解 Spring bean 创建流程的当前状态,以便我可以在预定逻辑中检查它并且在应用程序创建未完成时不产生任何结果?

谢谢!

【问题讨论】:

  • 您需要发布完整的调用堆栈。你能写更多关于你的笔记本电脑环境和服务器环境之间的差异吗?服务器是 servlet/应用程序容器吗?它与用于开发的服务器是否不同?
  • 嗨,克劳斯!我的问题是:我可以注入任何开箱即用的 spring 核心 bean,它会在我的计划逻辑中告诉我 Spring 应用程序初始化的当前状态吗?因为我不想在应用未初始化时生成原型bean
  • 我在 SO 上有 cmets,所以请参阅下面的答案。基本上上下文创建是单线程的。如果您调用 ApplicationContext.getBean() ,请确保执行此操作的线程与 spring 上下文的生命周期同步。如果线程由 servlet-container 或类似的运行时提供,您可以使用 AbstractApplicationContext.isActive() 和 .isRunning(),但与所有并发编程一样,上下文可能在您读取状态后关闭,所以为了安全起见应该只对 spring 管理的线程执行 spring 操作。

标签: java spring spring-bean spring-ioc


【解决方案1】:

查看 Spring 源代码(4.3.3 和 4.2.2),这只发生在一个位置 DefaultSingletonBeanRegistry.getSingleton() 中,并且仅在 spring 上下文的初始化失败或您已明确关闭它时发生。 Spring 使用单个线程(调用线程)来创建您的 bean 并调用启动生命周期方法,因此您发现的异常只有在您违反此原则时才可能出现。

  • 当您说计划任务时,您是指由 Spring 计划(使用@Scheduled),还是您有自己的计划逻辑?
  • 您是否在应用程序中启动任何线程,如果是,拥有该线程的 bean 是否实现 Spring 的 Lifecycle 或 SmartLifecycle 接口?

我见过很多开发人员在 bean 构造函数中启动线程,这是一个非常糟糕的主意。如果你有一个具有生命周期(线程、资源池)的 bean,你应该总是使用 LifeCycle 接口,并在 start() 中创建你的资源,并在 stop() 中清理它。这是一个好主意,因为 start() 在每个急切的单例都被实例化并且上下文完全连接之后被调用,所以除非每个 bean 都可以构造,否则不会创建线程。

【讨论】:

  • 谢谢克劳斯!异常发生在@Scheduled 注释方法中!这个方法会产生一些原型bean(实现Runnable接口)并将它们提交给ExecutorService。我会尽量听从你的建议,实现 LifeCycle 接口!
  • 根据您的信息,我只能在关闭弹簧上下文期间看到这种情况:在创建所有单例之前不应调用@Scheduled,它将由弹簧托管线程调用。您的 ExecutorService 生命周期不是由 spring 管理的,因此它可以在 spring 关闭后调用 beanfactory.getBean() 来获取原型 bean,如果原型 bean 自动装配任何单例 bean,它将失败并出现 BeanCreationNotAllowedException。尝试制作一个拥有 ExecutorService 的 bean,让它实现 SmartLifeCycle。
  • 克劳斯,非常感谢您的快速反应和明确的答复! SmartLifeCycle 正是我想要的!
  • 很高兴为您提供帮助。我认为这是许多开发人员忽略的 Spring 领域(我花了一些时间才找到它),但它使代码更加简洁。我做了很多代码需要在servlet-container上持续部署,而你确实需要一个定义明确的应用生命周期来在一个正在运行的Tomcat上部署新的应用(Tomcat在不运行时会自动停止并取消部署旧版本的应用)使用时间更长)。
猜你喜欢
  • 2016-04-21
  • 2011-11-23
  • 1970-01-01
  • 1970-01-01
  • 2014-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多