我的代码库中有以下场景:
//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();
)相比有什么好处?
答案 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)
来添加新的参数ClasssA - > ClasssB - > ClasssC - > ClasssA
当存在这种依赖关系时,你可以开始修改,然后修改使用它的类,依此类推......直到你发现你自己修改了同一个类,所以你开始循环,因为对象之间的通信路径很复杂。 当您使用DI时,可以在早期检测到此循环,因此您可以重新考虑您的体系结构以避免这种生产力黑洞
DI是一个非常强大的工具,可以保持大项目的可维护性,现在存在于很多环境和框架中,因为它非常有用,如果这仍然不能说服你,你可以尝试在Spring启动项目中启动项目,PlayFramework, Net Core,Java EE,Ruby on Rails ....以及其他许多将其作为正常流程并构建中等大小的应用程序,然后尝试不使用DI
答案 1 :(得分:1)
背景:CDI中的所有内容都在Context
内运行。那就是说
当容器创建托管bean,会话bean或支持注入的任何其他Java EE组件类的新实例时,容器必须:
- 初始化所有注入字段的值。容器将每个注入字段的值设置为可注射参考。
这意味着依赖bean的第一次初始化是在它第一次被调用时,也就是初始化父bean时。现在,JSF @ApplicationScoped
具有eager="true"
属性,该属性允许该范围的bean在应用程序的任何其他部分需要之前初始化,在启动时,EJB @Startup
为对EJB的影响相同。但是,CDI bean没有开箱即用的功能。
@Dependent
。这意味着注入的bean继承了注入目标的范围(除非bean有自己的范围;在这种情况下,它自己的范围适用)。在您的情况下,HelperUtil
不会超出EJB的生命周期,有点像@RequestScoped
相关强>
答案 2 :(得分:0)
我认为在调用方法之前不会调用它的构造函数,比如helperUtil.dothat(),而不是在实例化其包含bean时。
关于所有注入的字段必须由容器初始化,当托管bean像其他答案一样实例化时,它是真的,但是所有注入的字段都注入代理,因此它们不是真正的对象所以真正的构造函数不叫。
如果在实例化其包含bean时调用其构造函数,那么例如servlet中的@RequestScoped bean如何。 servlet只实例化一次,但注入的@RequestScoped bean必须多次实例化。如果它是真的那么肯定@RequestScoped注释将不起作用。
因此,通过简单地查看那些CDI注释意图和名称如“request”或“session”,我们知道真正的CDI托管bean对象是独立于包含用户bean实例化而实现/实例化的。