cdi-api_CDI概述-第1部分

本文介绍了CDI(Contexts and Dependency Injection)API,它是Java EE 6的一部分,旨在成为依赖注入的标准。文章从简介开始,逐步讲解了CDI的基础,如激活、限定符和范围,展示如何在不使用XML配置的情况下实现依赖注入。同时,还讨论了在不同场景下如何使用CDI,包括单元测试和不同范围的bean。
摘要由CSDN通过智能技术生成

cdi-api

介绍

我可能是Spring的忠实拥boy,但我也坚信技术应该包含标准。 尽管Spring现在已经成为事实上的标准,但它正在与其他产品竞争,例如Google Guice。 这使我作为建筑师的工作变得更加艰辛,因为我的工作是设计最长期的解决方案:标准是实现这一目标的盟友。

CDI (以前称为JSR 299)是一种尝试描述依赖注入的真实标准的尝试。 乍一看,CDI的吸引力在于SpringSource和Google都参与了规范团队。 CDI是Java EE 6堆栈的一部分,这意味着在Java EE 6兼容容器中运行的应用程序可以立即利用CDI。

因此,对于Maven应用程序,所需要做的只是添加以下依赖项:

<dependency>
  <groupId> javax.enterprise </groupId>
  <artifactId> cdi-api </artifactId>
  <version> 1.0-SP1 </version>
  <scope> provided </scope>
</dependency>

基础

怎么做? 让我们举一个简单的例子。 我仍然想使用轻量级的服务层:我只需要在servlet中引用此类服务​​即可。 为此,只需添加对服务的引用作为属性并使用@Inject对其进行注释:

publicclassWeldServletextendsHttpServlet{
  @InjectprivateHelloServicehelloService;
  ...
}

对Guice用户的注意事项:请注意,这是相同的注释,仅在标准包( javax.enterprise )中。

就这样! 没有花哨的XML配置,没有要使用的JNDI资源,什么也没有。 唯一要做的就是遵循我所谓的Highlander规则:

只可以有一个人。

该规则强制在扩展HelloService的类路径上必须只有一个类,从而实现了接口协定。 这是常识:注入必须是确定性的,如果要选择的实现不只一个,则不能。

激活

实际上,如果仅使用@Inject批注,则可能会遇到NullPointerException 。 为了激活CDI,您需要在Web应用程序的WEB-INF或jars的META-INF下有一个名为beans.xml的XML配置文件。 该文件可以为空,但必须使用此文件才能引导CDI。

<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

资格赛

但是,大多数情况下这种条件并没有得到满足,因为您可能至少会对单元测试使用另一个模拟实现。 一个地方缺少一个概念:它是限定词。 限定词为注入添加了质量,以便在满足所有注入限定词的所有类之间强制执行Highlander规则。 因此,可以向模拟服务添加一个区分限定符来解决我们的难题。 让我们设计这样的限定词:

  • 被认为是限定词,它使用@Qualifier批注
  • 由于注入是在运行时完成的,因此保留策略必须是运行时
  • 目标将是类型,因为将被注释的是类本身

结果如下:

@Qualifier
@Retention(RUNTIME)
@Target({TYPE})
public@interfaceMock{}

只需使用@Mock和presto注释您的模拟服务,注入将再次成功。 我们还没有看到如何注入模拟服务,但是请耐心等待,稍后再解决。

二传手注射

实际上,这种情况不应该真的发生(至少在使用Maven的情况下),因为标准类路径和测试类路径应该不同。 此外,恕我直言,您的单元测试不应使用注入。 这将略微更改我的servlet,使其在标准上下文和单元测试上下文中都可以使用:我将需要能够在前一种情况下进行注入,而在后一种情况下进行手动设置。 这不是问题,因为CDI也接受setter注入,如以下代码片段所示:

publicclassWeldServletextendsHttpServlet{

  privateHelloServicehelloService;

  @Inject
  publicvoidsetHelloService(HelloServicehelloService){
    this.helloService=helloService;
  }
  ...
}

更多预选赛

如我们所见,上面的限定符用例不是一个很好的例子。 更好的方法是需要servlet报告错误。 报告这种情况的方法有很多:邮件,日志,SMS等。用于报告的服务将依赖于servlet,这意味着所有服务都应在类路径上可用。 现在,我们已经跟以前看到的@Mock ,每个服务将与注释@Mail@Log@SMS等什么,我们没有看到的是如何注入正确的服务。 没有什么比这更容易了,只需通过提供所需的限定符来告诉CDI您需要哪种服务:

publicclassWeldServletextendsHttpServlet{
  @Inject@MailprivateReportServicereportService;
  ...
}

当您不定义任何限定符时,CDI将在@Default的掩护下为您使用一个@Default 。 这就是为什么仅使用@Mock注释模拟服务成功的原因:真实的服务使用@Default隐式注释,这足以满足Higlander规则。

具有属性的预选赛

使用先前的方法可能会导致与Java EE 6的可读性和可维护性目标相抵触的限定符呈指数增长。 CDI仍然可以让您通过注释成员实现这些目标。 现在,这些类是:

publicenumReportType{
  MAIL,SMS,LOG;// Adding one more type here is easy
}

@Qualifier
@Retention(RUNTIME)
@Target({TYPE,FIELD})
public@interfaceReport{
    ReportTypevalue();
}

@Report(MAIL)
publicclassMailReportServiceImplimplementsReportService{...}

publicclassWeldServletextendsHttpServlet{
  @Inject@Report(MAIL)
  privateReportServicereportService;
  ...
}

创建另一个报告服务包括创建服务实现本身并向枚举添加值。

单身人士

传统上,服务是无状态的,因此,对实例化一次以上几乎没有兴趣:使它们成为单例是一种好习惯。 诸如Spring之类的框架将容器管理的bean作为默认实例。 单例创建是CDI的一项功能,但请注意,单例应明确标记为:

@Singleton
publicclassHelloServiceImplimplementsHelloService{
  ...
}

范围豆

单例只是示波器的特殊情况。 在Java EE中,范围是关于Web应用程序定义的:应用程序,会话,请求和页面。 CDI不管理页面范围,而是添加与JSF关联的对话范围。 CDI中的作用域用法与Spring相似:注入的Bean将绑定到已定义的作用域,并且其可见性限于该作用域。

例如,与设置和首选项相关的信息很可能发生在会话范围的bean中。 只需向此bean添加正确的作用域注释:

@SessionScoped
publicclassUserSettingsimplementsSerializable{
  privatestaticfinallongserialVersionUID=1L;
  ...
}

在这一点上,您可能应该能够满足80%的需求。 下一篇文章将讨论生产者和替代者等高级主题。

翻译自: https://blog.frankel.ch/cdi-an-overview/1/

cdi-api

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值