什么时候注入bean实际初始化?

时间:2014-11-25 17:40:29

标签: java jboss dependency-injection cdi

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

//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的实例何时会通过调用默认构造函数实际初始化注入?当注入它的第一个客户端(如BusinessManager)在应用服务器(在我的情况下为JBoss)启动时被实例化(并且该容器将由容器初始化,因为它是注释为@Stateless)?

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

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

3 个答案:

答案 0 :(得分:5)

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

你需要记住,创建取决于bean的范围,在你的情况下,这是一个无状态bean,如果某个客户端注入它并且没有其他实例可用,则创建bean,如果是singleton则将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启动项目中启动项目,PlayFramework, Net Core,Java EE,Ruby on Rails ....以及其他许多将其作为正常流程并构建中等大小的应用程序,然后尝试不使用DI

答案 1 :(得分:1)

背景:CDI中的所有内容都在Context内运行。那就是说

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

    当容器创建托管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
    2. 相关

答案 2 :(得分:0)

我认为在调用方法之前不会调用它的构造函数,比如helperUtil.dothat(),而不是在实例化其包含bean时。

关于所有注入的字段必须由容器初始化,当托管bean像其他答案一样实例化时,它是真的,但是所有注入的字段都注入代理,因此它们不是真正的对象所以真正的构造函数不叫。

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

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