JSR-330 JAVA 依赖注入标准API说明
admin
2024-02-25 16:42:09
0

JSR-330 JAVA 依赖注入标准

文章目录

  • JSR-330 JAVA 依赖注入标准
    • Package javax.inject
    • @Inject注解
    • @Qualifier注解
    • `Interface Provider`
    • @Named注解
    • @Scope注解
    • @Singleton注解
    • 包的下载

JSR-330 JAVA 依赖注入标准,只发布了规范API源码,没有发布规范文档。

Package javax.inject

这个包规定了一种获取对象的方法,与构造函数、工厂和服务定位器(如JNDI)等传统方法相比,它可以最大限度地提高可重用性、可测试性和可维护性。这个过程被称为依赖性注入,对大多数非琐碎的应用都是有益的。

许多类型依赖于其他类型。例如,一个Stopwatch可能依赖于一个TimeSource。一个类型所依赖的类型被称为它的依赖关系在运行时找到一个依赖关系的实例来使用的过程被称为解析依赖关系。如果找不到这样的实例,则表示该依赖关系未被满足,并且应用程序运行失败。

在没有依赖注入的情况下,一个对象可以通过几种方式解决其依赖关系。它可以调用一个构造函数,将一个对象直接与它的依赖关系的实现和生命周期硬连接起来:

 class Stopwatch {final TimeSource timeSource;Stopwatch () {timeSource = new AtomicClock(...); // 通过调用构造函数新建一个依赖对象}void start() { ... }long stop() { ... }}

如果需要更多的弹性,该对象可以调用一个工厂或服务定位器:

  class Stopwatch {final TimeSource timeSource;Stopwatch () {timeSource = DefaultTimeSource.getInstance(); // 通过工厂方式新建一个依赖对象}void start() { ... }long stop() { ... }}

在决定这些传统的依赖解决方法时,程序员必须做出权衡。(1)构造器更简洁,但有限制性。(2)工厂在一定程度上将客户端和实现解耦,但需要模板代码。(3)服务定位器甚至可以进一步解耦,但会降低编译时的类型安全性。这三种方法都抑制了单元测试。例如,如果程序员使用工厂,那么针对依赖工厂的代码的每个测试都必须模拟出工厂,并记得自己清理,否则就会有不利影响。

   void testStopwatch() {TimeSource original = DefaultTimeSource.getInstance();DefaultTimeSource.setInstance(new MockTimeSource());try {// Now, we can actually test Stopwatch.Stopwatch sw = new Stopwatch();...} finally {DefaultTimeSource.setInstance(original);}}

在实践中,模拟工厂的能力会导致更多的模板代码。对多个依赖关系进行模拟和清理的测试很快就会失去控制。更糟糕的是,程序员必须准确地预测未来需要多少灵活性,否则就会承担后果。如果程序员最初选择使用一个构造函数,但后来决定需要更多的灵活性,那么程序员必须替换对构造函数的每一次调用。如果程序员谨慎行事,预先写好工厂,可能会导致大量不必要的模板代码,增加复杂性和易错性。

依赖性注入解决了所有这些问题。程序员不需要调用构造函数或工厂,而是由一个叫做依赖注入器的工具将依赖关系传递给对象。

class Stopwatch {final TimeSource timeSource;@Inject Stopwatch(TimeSource TimeSource) {this.TimeSource = TimeSource;}void start() { ... }long stop() { ... }}

injector(注入器) 进一步将依赖关系传递给其他依赖关系,直到构建出整个对象图。例如,假设程序员要求注入器创建一个StopwatchWidget实例。

   /** GUI for a Stopwatch */class StopwatchWidget {@Inject StopwatchWidget(Stopwatch sw) { ... }...}

injector 可能:

  • 找到一个TimeSource 实例
  • TimeSource实例构建一个Stopwatch实例
  • Stopwatch实例构造一个StopwatchWidget

这使得程序员的代码干净、灵活,并且相对来说没有依赖性相关的基础设施。

在单元测试中,程序员现在可以直接构造对象(没有注入器),并传递模拟依赖。程序员不再需要在每个测试中建立和拆除工厂或服务定位器。这大大简化了我们的单元测试。

void testStopwatch() {Stopwatch sw = new Stopwatch(new MockTimeSource());...}

单元测试复杂性的总减少量与单元测试的数量和依赖关系的数量的乘积成正比。

**这个包提供了依赖注入注解,使可移植类得以实现,但它将外部依赖配置留给注入器实现。**程序员对构造函数、方法和字段进行注解,以表明其可注入性(构造函数注入在上面的例子中得到了证明)。依赖性注入器通过检查这些注释来识别一个类的依赖性,并在运行时注入依赖性。此外,injector 可以在构建时验证所有的依赖关系是否被满足。相比之下,A service locator(服务定位器)在运行时才能发现未满足的依赖关系。

注入器的实现可以采取多种形式。注入器可以使用XML、注解、DSL(特定领域语言),甚至是普通的Java代码来配置自己。一个注入器可以依靠反射或代码生成。一个使用编译时代码生成的注入器甚至可能没有自己的运行时表示。其他注入器可能根本无法生成代码,无论是在编译还是运行时。一个 “容器”,对于某些定义来说,可以是一个注入器,但这个包规范的目的是尽量减少对注入器实现的限制。

@Inject注解

@Inject注解标明可注入的构造函数、方法和字段。可以适用于静态和实例成员。一个可注入的成员可以有任何访问修饰符(private, package-private, protected, public)。构造函数首先被注入,其次是字段,然后是方法。超类中的字段和方法在子类中的字段和方法之前被注入。在同一类中的字段和方法之间的注入顺序没有被指定。

可注入的构造函数用@Inject注解,并接受零个或多个依赖作为参数。一个类中的只支持在一个构造函数上添加注解@Inject

当没有其他构造函数时,@Inject对于公共的、无参数的构造函数是可选的。这使得注入器可以调用默认的构造函数。

什么是可注入的字段:

  • 用@Inject注释的。
  • not final
  • may have any otherwise valid name.可以有任何其他有效的名称。

什么是可注入的方法:

  • 用@Inject注释的。
  • 不是abstract。
  • 不声明它们自己的类型参数。
  • 可以返回一个结果
  • 可以有任何其他有效的名称。
  • 接受零个或多个依赖作为参数。

注入器忽略注入方法的结果,但允许非void的返回类型,以支持在其他情况下使用该方法(例如,构建器式的方法链)。

Examples:

 public class Car {// Injectable constructor@Inject public Car(Engine engine) { ... }// Injectable field@Inject private Provider seatProvider;// Injectable package-private method@Inject void install(Windshield windshield, Trunk trunk) { ... }}

一个用@Inject注解的方法如果覆写了另一个用@Inject注解的方法,每个实例的每个注入请求将只被注入一次。一个没有@Inject注解的方法如果覆写了一个用@Inject注解的方法,将不会被注入。

进行成员注入时必须进行@Inject注解标明。虽然一个可注入的成员可以使用任何可访问性修饰符(包括私有),但平台或注入器的限制(如安全限制或缺乏反射支持)可能会阻止非公共成员的注入。

Qualifiers

一个限定符可以注解一个可注入的字段或参数,并与类型相结合,确定要注入的实现。修饰符是可选的,当在独立于注入器的类中与 @Inject 一起使用时,一个字段或参数不能有超过一个修饰符。在下面的例子中,限定词是@Leather,@Tinted,@Big。

  public class Car {@Inject private @Leather Provider seatProvider;@Inject void install(@Tinted Windshield windshield,@Big Trunk trunk) { ... }}

如果一个可注入方法覆盖另一个方法,覆盖方法的参数不会自动继承被覆盖方法的参数的限定词。

Injectable Values

对于一个给定的类型T和可选的限定符,一个注入器必须能够注入一个用户指定的类,该类。

  1. 与T的赋值兼容,并且
  2. 有一个可注入的构造函数。

例如,用户可以使用外部配置来选择T的实现。除此之外,哪些值被注入取决于注入器的实现和它的配置。

Circular Dependencies

检测和解决循环依赖是留给注入器实现的一个练习。两个构造函数之间的循环依赖是一个明显的问题,但你也可以在可注入的字段或方法之间有一个循环依赖。

  class A {@Inject B b;}class B {@Inject A a;}

当构造一个A的实例时,一个“天真”的注入器实现可能会进入一个无限的循环,构造一个B的实例来设置在A上,第二个A的实例来设置在B上,第二个B的实例来设置在第二个A的实例上,等等。

“保守”的注入器可能会在构建时检测到循环依赖并产生错误,此时程序员可以通过注入ProviderProvider代替A或B来打破循环依赖关系。从它被注入的构造函数或方法中直接调用get(),会破坏提供者打破循环依赖的能力。在方法或字段注入的情况下,对其中一个依赖的范围(例如,单例作用域)也可以实现有效的循环关系。

@Qualifier注解

@Qualifier注解标明限定词注释。任何人都可以定义一个新的限定符。一个限定符的注解:

  • @Qualifier, @Retention(RUNTIME),通常还有@Documented的注解。
  • 可以有属性。
  • 可以是公共API的一部分,与依赖类型很相似,但与实现类型不同,后者不需要是公共API的一部分。
  • 如果用@Target注释,可以限制使用。虽然本规范只涉及将限定符应用于字段和参数,但一些注入器配置可能在其他地方使用限定符注释(例如,在方法或类上)。

For example:

   @java.lang.annotation.Documented@java.lang.annotation.Retention(RUNTIME)@javax.inject.Qualifierpublic @interface Leather {Color color() default Color.TAN;public enum Color { RED, BLACK, TAN }}

Interface Provider

提供T的实例。通常由一个注入器实现。对于任何可以被注入的T类型,你也可以注入Provider。与直接注入T相比,注入Provider可以:

  • 可以返回多个实例。
  • 实例的返回可以延迟化或可选的检索一个实例。
  • 打破循环依赖关系。
  • 抽象作用域,可以在一个已知的作用域中查询一个作用域更小的实例。

For example:

   class Car {@Inject Car(Provider seatProvider) {Seat driver = seatProvider.get();Seat passenger = seatProvider.get();...}}

get()方法—可以返回一个完全构造的类型T的实例。

@Named注解

基于String类型的限定器

Example usage:

   public class Car {@Inject @Named("driver") Seat driverSeat;@Inject @Named("passenger") Seat passengerSeat;...}

@Scope注解

识别范围注解。范围注解适用于包含可注入构造函数的类,并管理注入器如何重复使用该类型的实例。默认情况下,如果没有范围注解,注入器会创建一个实例(通过注入类型的构造函数),将该实例用于一次注入,然后将其遗忘。如果存在范围注解,注入器可以保留该实例,以便在以后的注入中可能被重用。如果多个线程可以访问一个作用域的实例,其实现应该是线程安全的。作用域本身的实现是由注入者决定的。

在下面的例子中,范围注解@Singleton确保我们只有一个Log实例:

  @Singletonclass Log {void log(String message) { ... }}

如果注入器在同一个类上遇到一个以上的范围注解,或者遇到它不支持的范围注解,就会产生一个错误。

一个范围注解:

  • 是用 @Scope@Retention(RUNTIME) 和典型的 @Documented 注解的。
  • 不应该有属性。
  • 通常没有 @Inherited,所以范围是与实现的继承正交的。
  • 如果用@Target来注解,可能会限制使用。虽然本规范只涵盖了对类的作用域的应用,但一些注入器配置可能在其他地方使用作用域注释(例如在工厂方法结果上)。

For example:

   @java.lang.annotation.Documented@java.lang.annotation.Retention(RUNTIME)@javax.inject.Scopepublic @interface RequestScoped {}

用 @Scope 来注解作用域可以帮助注入器检测到这样的情况:程序员在类上使用了作用域注解,但忘记在注入器中配置作用域。一个"保守"的注入器会产生一个错误,而不是不应用一个作用域。

@Singleton注解

标识一个类型,注入器只实例化一次。不继承。

包的下载

最新版查询:https://search.maven.org/artifact/jakarta.inject/jakarta.inject-api/2.0.1.MR/jar

maven配置

jakarta.injectjakarta.inject-api2.0.1.MR

Gradle配置

implementation 'jakarta.inject:jakarta.inject-api:2.0.1.MR'

相关内容

热门资讯

4月份银行理财规模环比增加2.... 钛媒体App 5月16日消息,银行理财市场在4月份迎来规模与收益的双增长。据华源证券廖志明团队发布的...
【光明日报】黑龙江:免签红利释... 5月10日早上7时,一辆国际大巴缓缓停靠在黑龙江绥芬河公路口岸入境大厅前。游客们提着大包小裹,依次走...
又一跨国高端化工合作项目落子乐... 5月15日,福华化学携手瑞士特种化学品企业科莱恩打造的创新型高端磷系无卤阻燃剂项目(以下简称“福华科...
鸿蒙智行:已拥有1951家销售... IT之家 5 月 15 日消息,鸿蒙智行智界 V9 发布会正在进行,官方透露目前已拥有 1951 家...
黄金、白银,直线大跌! 5月15日晚间,贵金属价格突然大跌! 截至记者发稿时,现货黄金跌超2%,暂报4553美元/盎司附近。...
央视《焦点访谈》聚焦!万兴科技... 深圳商报·读创客户端首席记者 谢惠茜 5月14日,中央电视台《焦点访谈》推出专题节目《扩能提质强服务...
东方嘉富人寿董事长履职半年被换... 文|达摩财经 东方嘉富人寿再度进行人事调整。 5月13日,东方嘉富人寿发布公告称,自2026年4月...
重返西决!文班19+6卡斯尔3... 【搜狐体育战报】北京时间5月16日NBA季后赛,客场作战的马刺以139-109击败森林狼,总比分4-...
原创 美... 十万亿美债为什么还没有崩盘?或许答案在于,中国的存在让局势与众不同。现在的美债就像一张看似脆弱的网,...
原创 茅... 最近打开股票软件看白酒板块,是不是心里拔凉拔凉的? 茅台又回到1300元区间了,五粮液跌破90元,洋...
茅台宣布涨价 5月15日深夜,“i茅台”APP发布公告称,按照随行就市、供需适配、量价平衡、相对平稳的原则,贵州茅...
最高涨200元!茅台官宣4款产... 贵州茅台(600519.SH)凌晨宣布涨价几款产品。 茅台数字营销平台“i茅台”今日(5月16日)发...
面向地方国资产融转型全链条,X... 5月15日,XOD创新投融资模式3.0产品发布会在广州举办。该产品主要面向地方国资产融协同创新转型提...
2026Q1:10家上市商超9... 截至4月30日,所有A股上市公司2026年Q1财报全部出炉,传统商超也晒出自己的成绩单。10家披露的...
入主盟科药业失利后,拟借款2.... 来源:时代周报-时代在线 继去年试图通过定增入主盟科药业(688373.SH)失败后,海鲸药业再度出...
同比激增86%、规模突破760... 图片来源:界面图库 界面新闻记者 | 孙艺真 今年以来,证券行业融资补血热潮持续升温。前5个月...
促进青年消费,扶持青年创业,上... 5月14日,上海市政协团青界别、经济界跨界别活动在市政协全过程人民民主实践点举行。 今年初,团市委立...
苹果股价昨日创收盘新高,站上3... IT之家 5 月 16 日消息,苹果公司股价昨日(5 月 15 日)收于 300.23 美元,首次站...
杭州首批配售型保障房正式入市 杭州首批配售型保障房正式入市 价格约为周边商品房5折,18日开始报名 不能入市交易,可由政府指定机构...
“后巴菲特时代”,伯克希尔调仓... 当地时间5月15日,伯克希尔披露了2026年一季度美股持仓报告。这是伯克希尔在巴菲特卸任CEO并由阿...