【问题标题】:When is injected bean actually initialized?注入的bean何时实际初始化?
【发布时间】:2015-01-23 20:11:26
【问题描述】:

我的代码库中有以下场景:

//this bean, which will be injected,
//is not annotated
public class HelperUtil {
   //only default constructor with no args

   public void doThis(String _in) {
      //...
   }

   public void doThat() {
      //...
   }
}

在下面的类中我们进行注入:

@Named
@Stateless
public class BusinessManager {

    @PersistenceContext(unitName = "default")
    private EntityManager em;

    @Inject
    private HelperUtil helperUtil ;

    //...
}

Q1:什么时候通过调用默认构造函数来真正初始化要注入的HelperUtil实例?当应用程序服务器(在我的情况下为JBoss)启动时实例化第一个注入它的客户端(如BusinessManager)时是否完成(并且该客户端将由容器初始化,因为它被注释为@987654326 @)?

Q2:在上面的演示中,只要容器以外的客户端通过直接调用构造函数而不是通过获取实例来请求实例,HelperUtil 将保持为singleton DI?

Q3:在这种情况下使用 DI 和 @Inject 与直接调用构造函数 (HelperUtil helper = new HelperUtil();) 相比有什么优势?

【问题讨论】:

  • 如何做到这一点取决于框架。在某些框架中,例如iPOJO,注入的字段可能在第一次使用之前不会创建。
  • 您知道在JBoss 上运行的JSF 框架是如何完成的吗?

标签: java jboss dependency-injection cdi


【解决方案1】:

这取决于,但你可以控制这些事件来执行一些代码,例如: 如果您需要在应用程序启动时执行您的 bean,您需要将 @Startup 注释添加到您的 bean。 如果您需要在不访问其他注入资源的情况下初始化 bean,您可以使用普通的构造函数。 如果您需要在初始化 bean 时执行某些方法,请在方法中使用 @PostConstruct 注释。

您需要记住,创建取决于 bean 的范围,在您的情况下,它是一个无状态 bean,如果某些客户端注入它并且没有其他可用实例,则将创建 bean,如果是单例则bean 将只创建一次,通常会在需要时创建 bean(单例 bean 会初始化,直到第一个客户端使用它,或者在启动时使用注解)

编辑: 对于第三个问题,优点是如果您在 HelperUtil 中使用资源或其他 bean,它们将使用正确的值初始化,例如,如果您使用实体管理器或帮助程序中的其他 bean。如果您的助手只处理静态方法或其他简单实用程序之类的事情,那么您是对的,优势是没有的,您可以像静态助手类一样简单地管理,但是如果您需要 EE 资源,则需要按顺序管理 bean加载所有注入和资源

编辑 2: 在 Java 和 C# Core 中编程和使用依赖注入几年后,我可以补充:问题 3 非常开放,使用 DI 将允许您的代码:

  • 减少耦合,如果您更改构造函数,则必须搜索所有 new ObjectModified(oldParams) 以添加新参数
  • 更容易测试,因为你可以注入“假对象”作为依赖,避免加载所有系统和准备测试的状态,例如,如果你想检查一些依赖于当前的代码小时,您可以在测试模式下连接一个虚假的提供者,以始终给出相同的小时或一些序列
  • 避免循环依赖,其中类 A 依赖于 B,B 依赖于 A,通常这更复杂,例如

ClasssA -> ClasssB -> ClasssC -> ClasssA

当存在此依赖项时,您可以开始修改,然后修改使用它的类,依此类推...直到不知何故您发现自己正在修改与以前相同的类!所以您开始循环,因为对象之间的通信路径很复杂。 当您使用 DI 时,可以及早检测到这种循环,因此您可以重新考虑您的架构以避免这种生产力黑洞

DI 是一个非常强大的工具来保持大型项目的可维护性,现在存在于很多环境和框架中,因为它非常有用,如果这仍然不能说服你,你可以尝试在 Spring boot 中启动一个项目,PlayFramework, Net Core、Java EE、Ruby on Rails.... 以及许多其他已将此作为正常流程并构建中型应用程序的人,然后尝试不使用 DI

【讨论】:

  • 谢谢我刚刚在 OP 中添加了第三个问题,如果你不介意看一下
【解决方案2】:

背景:CDI 中的所有内容都在Context 中运行。话虽如此

  1. 应请求实例化托管 bean 的字段。来自the spec

当容器创建托管 bean、会话 bean 或任何其他支持注入的 Java EE 组件类的新实例时,容器必须:

  • 初始化所有注入字段的值。容器将每个注入字段的值设置为可注入引用。

这意味着依赖 bean 的第一次初始化是在它第一次被调用时,也就是父 bean 被初始化的时候。现在,JSF @ApplicationScoped 具有eager="true" 属性,它允许该范围的bean 在应用程序的任何其他部分需要它们之前进行初始化,在启动时,EJB @Startup 对EJB 具有相同的效果。但是,CDI bean 没有开箱即用的功能。

  1. CDI bean 的默认范围是@Dependent。这意味着注入的 bean 继承了注入目标的范围(除非 bean 有自己的范围;在这种情况下,它自己的范围适用)。在您的情况下,HelperUtil 不会超过该 EJB 的生命周期,类似于@RequestScoped

相关

【讨论】:

    【解决方案3】:

    我认为它的构造函数只有在方法像 helperUtil.dothat() 一样被调用时才会被调用,而不是在其包含的 bean 被实例化时。

    当托管bean像其他答案一样被实例化时,容器必须初始化所有注入的字段,这是真的,但是所有注入的字段都注入了代理,所以它们不是真实的对象,所以真正的构造函数没有被调用。

    如果在实例化其包含的 bean 时调用其构造函数,那么例如在 servlet 中的 @RequestScoped bean 怎么样。 servlet 只实例化一次,但注入的 @RequestScoped bean 必须实例化多次。如果它是真的,那么@RequestScoped 注释肯定不起作用。

    因此,通过简单地查看那些 CDI 注释意图和名称,如“请求”或“会话”,我们知道真正的 CDI 托管 bean 对象是独立于包含用户 bean 实例化的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-05
      • 1970-01-01
      • 2016-07-22
      • 2013-07-08
      • 1970-01-01
      • 2012-02-08
      • 2013-03-05
      相关资源
      最近更新 更多