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'

相关内容

热门资讯

韩国股市大跌触发熔断机制 1日,韩国首尔,中区一家银行交易室的电子屏显示韩国综合股价指数(KOSPI)。视觉中国/图 韩国股市...
尿色加深、皮肤泛黄时,你的肝可... 生活中,不少人发现尿色变深、皮肤泛黄时,会误以为是喝水少、上火或肤色问题,简单调整后便不再关注。可这...
30亿元红包!千问宣布 2月2日,千问APP宣布投入30亿元启动“春节请客计划”,以免单形式请全国人民在春节期间吃喝玩乐,感...
周生生足金挂坠被检测出含铁银钯... 编者按:维护消费者权益,守护消费安全。央广网啄木鸟消费者投诉平台,保障消费者合法权益,为新消费时代保...
中国AI应用春节红包大战 阿里... 中国科技巨头为推广旗下人工智能(AI)产品重演11年前的红包大战,继腾讯和百度后,同样在追赶字节跳动...
最新!多家银行,紧急调整! 近期,国际黄金与白银价格在创下历史新高后剧烈波动。2月2日,金银价格显著回调,黄金期货一度跌破每盎司...
米兰冬奥中国代表团成立丨冰雪热... 聚焦健康中国 冬奥会 (第一健康报道北京 实习记者袁正杰) 米兰冬奥会中国体育代表团正式成立,标志着...
i茅台:月活用户超1531万,... 1499元飞天上架已满月,i茅台披露关键数据。 2月2日,据“小茅i茅台”微信公号,i茅台表示,“这...
多重风险高悬 一批*ST公司拉... 伴随2025年度业绩预告密集发布,一批上市公司拉响退市风险“警报”。 据上海证券报初步统计,近期,已...
白癜风医生刘军连:白癜风与饮食... 白癜风患者日常饮食需遵循清淡原则,减少辛辣、油腻、刺激性食物的摄入,避免饮食刺激引发皮肤炎症或免疫波...
备受资金青睐 化工主题ETF总... ◎记者 赵明超 化工主题ETF正在成为资金流入的重要方向。今年以来,化工主题ETF净流入资金逾260...
突然,直线拉升!马斯克,突爆大... 马斯克的人形机器人传来重大预告。 今日,世界首富埃隆·马斯克旗下特斯拉官宣,第三代特斯拉人形机器人O...
【锋行链盟】港股上市公司大宗交... 港股市场的大宗交易通常称为“大手交易”(Block Trade),是指达到一定规模的股票买卖,其操作...
AI春节大战 阿里30亿元跟进... 大厂暗自角力,加速撬动AI产品在C端市场的曝光和渗透。而春节AI大战,只是巨头强化攻势的第一步棋。 ...
近10亿元! 李书福投资的eV... 沃飞长空本轮融资由中信建投领投,联新资本、祥峰投资、光合创投、国策投资等联合投资,公司部分老股东跟投...
多款AI聊天机器人开始引用马斯... ChatGPT并非唯一引用马斯克Grokipedia的聊天机器人 谷歌的Gemini、AI Mode...
杰我睿兑付折上折、云点当遭连续... 深圳市杰我睿珠宝有限公司(以下简称“杰我睿”)出现兑付困难已逾半月之久,2月2日,北京商报记者从多位...
原创 能... 美国抢了委内瑞拉的石油想高价卖给中国,结果被咱们直接拒收,特朗普前脚还在装大爷,后脚就急得求着咱们买...
三区试点,建行支持上海第一批收... 2月2日,中国建设银行支持上海市第一批收购二手住房用于保障性租赁住房项目签约活动在中国建设银行上海市...
淳厚基金左季庆被解聘员工举报 淳厚基金的风波,还没到落幕的时候。 今年 1 月初,上海长宁国资拿下淳厚 58.8% 股权,成为第一...