如何在bean初始化完成后调用一个方法?

273

我有一个使用情况,需要在ApplicationContext加载时仅调用bean中的非静态方法一次。 如果我使用MethodInvokingFactoryBean,这样做可以吗? 或者我们有更好的解决方案吗?

顺便说一下,我在Web应用程序中使用ConfigContextLoaderListener来加载应用程序上下文。 并且希望,如果实例化了bean 'A',则只调用methodA()一次。

如何优雅地完成这个任务?

6个回答

351

对于其他答案中提出的@PostConstruct建议,我认为这确实是最好的解决方案。

  • 它使您的代码与Spring API(@PostConstruct位于javax.*)解耦
  • 它明确地标注了您的初始化方法作为需要调用以初始化bean的内容
  • 您不需要记得在Spring bean定义中添加init-method属性,Spring将自动调用该方法(无论如何,只要在上下文中的其他地方注册了annotation-config选项)。

9
谢谢,这个有效。请注意,如果您想在Spring中使用它,必须包含"<context:annotation-config />"以注册CommonAnnotationBeanPostProcessor bean(如上所述)。 - khylo
2
一个合适的context:component-scan也可以起到作用,如果您的类路径上有大量非Spring库,则可以减少启动时间。 - Donal Fellows
5
JavaDoc中的PostConstruct声明指出每个类只能有一个方法被标注为它:http://docs.oracle.com/javaee/5/api/javax/annotation/PostConstruct.html。 - Andrew Swan
@PostConstruct不能与事务管理器一起使用,请参见:http://forum.spring.io/forum/spring-projects/data/50069-no-transaction-in-transactional-service-called-from-postconstruct/page3 - mjs
3
当你实例化的bean不是你自己的类而是某个第三方类时,@PostConstruct 对你也没什么用处。 - John Rix
@PostConstruct通常非常有用,但在我的情况下,我发现在框架调用postConstruct时,SecurityContext中的Authentication实例不可用。 - demaniak

209

你可以像这样使用:

<beans>
    <bean id="myBean" class="..." init-method="init"/>
</beans>
这将在实例化Bean时调用“init”方法。

15
在大多数情况下,使用postConstruct会更好,因为我们不希望干扰Spring Bean的初始化过程。 - Jackie
4
在这里,“不想搞砸Spring Bean初始化”是什么意思? - Yngve Sneen Lindal
@Mercer Traieste,我应该在这里给什么类属性?我可以在这里给控制器类吗? - KJEjava48

113

有三种不同的方法需要考虑,如文档所述参考

使用 init-method 属性

优点:

  • 不要求 bean 实现接口。

缺点:

  • 源代码中没有立即指示该方法在构造后是需要进行的以确保正确配置 bean 的方法。

实现 InitializingBean 接口

优点:

  • 无需指定 init-method 或打开组件扫描/注释处理。
  • 适用于由库提供的 bean,在这种情况下,我们不希望使用该库的应用程序关注 bean 生命周期。

缺点:

  • 比 init-method 方法更加侵入式。

使用 JSR-250 @PostConstruct 生命周期注解

优点:

  • 在使用组件扫描自动检测 bean 时非常有用。
  • 使得清晰明了地知道哪个特定方法用于初始化,意图更加贴近代码。

缺点:

  • 初始化不再集中指定在配置中。
  • 必须记得打开注释处理(有时可能会被遗忘)

4
我认为使用@PostConstruct是一件好事,因为它是类的一部分,需要在初始化处理结束时调用该方法。 - Donal Fellows
如果那个类确实需要它,而且你不能在构造函数中完成它,那么我认为这是代码异味。 - user482745

41
你试过使用InitializingBean来实现吗?听起来正是你想要的。
不过缺点是你的bean会变得与Spring相关,但在大多数应用中,这并不是什么大问题。

2
你为什么要选择在接口中实现而不是在XML中指定init-method呢? - Mark
4
这是一个品味问题。界面是Spring组件模型的一部分,只起到了这个目的,而对于自定义命名方法,它可能并不明显需要调用它来完成组件生命周期。因此,这主要用于通信。当然,缺点是引入了对Spring框架的依赖。在两者之间的一种好方式是使用@PostConstruct,因为它有清晰的语义,但不会引入依赖关系... - Oliver Drotbohm
8
Oliver给了我一些好的借口,但实际上我只是忘记了init-method :) 另一个原因是类型本身知道在所有属性设置完成后需要“完成” - 它并不是基本上应该在配置中的东西。 - Jon Skeet

8
你可以在应用程序上下文中部署自定义的 BeanPostProcessor来完成此操作。或者,如果你不介意在bean中实现一个Spring接口,你可以使用 InitializingBean接口或"init-method"指令(相同的链接)。

有人了解如何编写BeanPostProcessor的详细信息吗?这似乎正是我所需要的。干杯 :) - peakit
Spring附带许多示例。只需查看BeanPostProcessor的JavaDoc API,您就会找到许多实现类的链接。然后查看它们的源代码。 - Rob H

-9
为了更清楚地澄清两种方法的区别,即使用:
  1. @PostConstruct
  2. init-method="init"
从个人经验来看,我发现仅使用(1)仅适用于Servlet容器,而(2)适用于任何环境,甚至是桌面应用程序。因此,如果您要在独立应用程序中使用Spring,则必须使用(2)来执行“在初始化后调用此方法”操作。

4
在Spring应用程序中使用的@PostConstruct(在技术上)与所属Spring上下文的生命周期相关联。这种上下文可以在各种应用程序中使用。 - Donal Fellows
那是我期望的行为,但对我没有起作用。 - Ayorinde

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接