跳到主要内容

高级

上一部分 ,我们探讨了依赖注入概念的一些常见原则和 ActiveJ Inject功能。 在这一部分,我们将进行更高级、更复杂的ActiveJ Inject使用案例。

你可以在以下网站找到完整的例子来源 GitHub

DI 多用途夹子#

Multibinder 如果一个键有两个或两个以上的绑定,则解决绑定冲突。 在下面的例子中,我们将创建一个HTTP服务器,它由两个 AbstractModule。 这两个模块都包括2个相互冲突的键。 在这个例子中,我们将使用不同的方式来提供多重绑定。

在第一个servlet AbstractModule,我们通过覆盖 configure() 方法,为 StringAsyncServlet 的映射提供多重绑定。 我们使用 multibindToMap 方法,该方法为所提供的 冲突的绑定地图返回一个绑定地图。

static class ServletMapsModule extends AbstractModule {  @Override  protected void configure() {    multibindToMap(String.class, AsyncServlet.class);  }
  @Provides  public Map<String, AsyncServlet> firstPage() {    return singletonMap("/first",        request -> HttpResponse.ok200().withPlainText("Hello from first page!"));  }
  @Provides  public Map<String, AsyncServlet> lastPage() {    return singletonMap("/last",        request -> HttpResponse.ok200().withPlainText("Hello from last page!"));  }
  @ProvidesIntoSet  AsyncServlet primary(Map<String, AsyncServlet> initializers) {    RoutingServlet routingServlet = RoutingServlet.create();    initializers.forEach(routingServlet::map);    return routingServlet;  }}

注意, ,主要的 servlet被标记为 @ProvidesIntoSet 注释。 我们以后将使用这个。 在第二个servlet模块中,我们将用内置的 @ProvidesIntoSet 注释来自动设置多重绑定。 该注解以单子集的形式提供结果,然后将其提供给我们的 主要的 AsyncServlet

static class ServletInitializersModule extends AbstractModule {  @ProvidesIntoSet  public Consumer<RoutingServlet> firstPage() {    return routingServlet ->        routingServlet.map("/first",            request -> HttpResponse.ok200().withPlainText("Hello from first page!"));  }
  @ProvidesIntoSet  public Consumer<RoutingServlet> lastPage() {    return routingServlet ->        routingServlet.map("/last",            request -> HttpResponse.ok200().withPlainText("Hello from last page!"));  }
  @ProvidesIntoSet  AsyncServlet primary(Set<Consumer<RoutingServlet>> initializers) {    RoutingServlet routingServlet = RoutingServlet.create();    initializers.forEach(initializer -> initializer.accept(routingServlet));    return routingServlet;  }}

最后,我们可以把所有的模块拉到一起。 还记得我们用 @ProvidesIntoSet 注解标记了 初级 servlets吗? 现在我们可以简单地合并,然后使用 Injector.of()编译它们。

public static void main(String[] args) {  Injector injector = Injector.of(new ServletMapsModule(), new ServletInitializersModule());
  String s = injector.getInstance(new Key<Set<AsyncServlet>>() {}).toString();  System.out.println(s);}

你可以在以下网站上找到实例来源 GitHub

实例注入器#

InstanceInjector 可以将实例注入到 @Inject 一些已经存在的对象的字段和方法。 考虑一下这个简单的例子。

@InjectString message;
@ProvidesString message() {  return "Hello, world!";}
@Overrideprotected void run() {  System.out.println(message);}
public static void main(String[] args) throws Exception {  Launcher launcher = new InstanceInjectorExample();  launcher.launch(args);}

可能会困扰你的问题是-- Launcher ,实际上如何知道消息变量包含 "Hello, world!" 字符串,以便在 run() 方法中显示它? 在这里,在DI的内部工作中, InstanceInjector ,实际上是给发射器提供了帮助。

private void postInjectInstances(String[] args) {  Injector injector = this.createInjector(args);  InstanceInjector<Launcher> instanceInjector = injector.getInstanceInjector(Launcher.class);  instanceInjector.injectInto(this);}
  • createInjector 用给定的参数产生 injector
  • instanceInjectorinjector获得所有需要的数据。
  • injectInto 将数据注入我们的空实例中。 你可以在以下网站上找到实例来源 GitHub

绑定生成器#

让我们考虑一下Cookies的例子吧! 前一部分。 这次我们有相同的POJO成分,但现在我们的cookie是一个通用的 Cookie<T> ,并且有一个字段 Optional<T> pastry

static class Cookie<T> {  private final Optional<T> pastry;
  @Inject  Cookie(Optional<T> pastry) {    this.pastry = pastry;  }
  public Optional<T> getPastry() {    return pastry;  }}

接下来,我们创建一个 AbstractModule cookbook 并重写其 configure() 方法。

AbstractModule cookbook = new AbstractModule() {  @Override  protected void configure() {    // note (1)    generate(Optional.class, (bindings, scope, key) -> {      Binding<Object> binding = bindings.get(key.getTypeParameter(0));      return binding != null ?          binding.mapInstance(Optional::of) :          Binding.toInstance(Optional.empty());    });
    bind(new Key<Cookie<Pastry>>() {});  }

generate() 为本模块添加一个给定类的 BindingGenerator ,本例中是一个 可选的BindingGeneratorInjector 编译最终的绑定图 trie时,试图生成一个缺失的依赖性绑定。 你可以用以下代码代替 generate()

@Provides<T> Optional<T> pastry(@io.activej.di.annotation.Optional T instance) {    return Optional.ofNullable(instance) 。

现在你可以创建 cookbook injector ,并获得一个 Cookie<Pastry>的实例。

Injector injector = Injector.of(cookbook);System.out.println(injector.getInstance(new Key<Cookie<Pastry>>() {}).getPastry().orElseThrow(AssertionError::new).getButter().getName());

你可以在以下网站上找到实例来源 GitHub

暂时性的捆绑#

如果你需要一个非单体对象,以便每个绑定都收到自己的实例,你应该使用 Transient Bindings。 只需添加一个 @Transient 注释。

Injector injector = Injector.of(cookbook);System.out.println(injector.getInstance(new Key<Cookie<Pastry>>() {}).getPastry().orElseThrow(AssertionError::new).getButter().getName());

在创建了一个 Injectorcookbook,每次使用 injector.getInstance,都会得到一个新的 non-singleton Integer instance。

Injector injector = Injector.of(cookbook);Integer someInt = injector.getInstance(Integer.class);Integer otherInt = injector.getInstance(Integer.class);System.out.println("First : " + someInt + ", second  : " + otherInt);

输出将说明所创建的实例是不同的,看起来会是这样的。

第一个:699,第二个:130

你可以在以下网站上找到实例来源 GitHub

ActiveJ专用程序优化#

ActiveJ Specializer 是一个为JVM优化代码的库。 你可以简单地将 它与ActiveJ Inject结合起来,并通过 up to 30% 为了设置ActiveJ Specializer,只需在 Injector.useSpecializer()Injector 实例化之前使用 。 ActiveJ Inject在运行时将捆绑物编译成一个高效的表示。 结合ActiveJ Specializer ,绑定可以变成字节码,其效率不亚于手工编写的代码。 通过这种方式,你可以在现实生活中的项目中获得 ,而不需要维护难以支持的手动代码的最佳性能。

实例提供者#

InstanceProvider 是 `Injector.getInstance()` 的一个版本,有一个出炉的密钥。 它可以通过提供者的方法流畅地请求。

AbstractModule ,我们明确地为 InstanceProvider ,使用 bindInstanceProvider helper方法为 Integer 绑定,并提供 Integer 工厂函数。

AbstractModule cookbook = new AbstractModule() {  @Override  protected void configure() {    bindInstanceProvider(Integer.class);  }
  @Provides  Integer giveMe() {    return random.nextInt(1000);  }};

在创建了一个 Injectorcookbook之后,我们得到了 Key<InstanceProvider<Integer>>的实例。 现在只需使用 provider.get() 来获得一个懒惰的 Integer 实例。

Injector injector = Injector.of(cookbook);InstanceProvider<Integer> provider = injector.getInstance(new Key<InstanceProvider<Integer>>() {});// lazy value get.Integer someInt = provider.get();System.out.println(someInt);

与前面的例子不同,如果你多次调用 provide.get() ,你会收到相同的值。 你可以在以下网站上找到实例来源 GitHub

检查创建的依赖关系图#

ActiveJ Inject为检查创建的实例、作用域和依赖图的可视化提供有效的DSL。 在这个Cookie的例子中,我们像往常一样,创建了 Sugar, Butter, Flour, PastryCookie POJOs, *cookbook*AbstractModule ,有两个作用域( Cookie 的父作用域和 @OrderScope 的成分)和 cookbook injector。 首先,让我们概述一下三个 Injector 方法: peekInstancehasInstancegetInstance。 它们允许检查 创建的实例。

Cookie cookie1 = injector.peekInstance(Cookie.class);System.out.println("Instance is present in injector before 'get' : " + injector.hasInstance(Cookie.class));System.out.println("Instance before get : " + cookie1);
Cookie cookie = injector.getInstance(Cookie.class);
Cookie cookie2 = injector.peekInstance(Cookie.class);System.out.println("Instance is present in injector after 'get' : " + injector.hasInstance(Cookie.class));System.out.println("Instance after get : " + cookie2);System.out.println();    /// created instance check.System.out.println("Instances are same : " + cookie.equals(cookie2));
  • peekInstance - 返回一个实例 ,只有在它已经被 getInstance 调用之前创建的情况下
  • hasInstance - 检查在调用 getInstance 之前,是否已经创建了所提供的 key 的实例。
  • getInstance - 返回一个所提供的 键的实例。 接下来,我们将探讨作用域检查的工具。
final Scope ORDER_SCOPE = Scope.of(OrderScope.class);
System.out.println("Parent injector, before entering scope : " + injector);
Injector subInjector = injector.enterScope(ORDER_SCOPE);System.out.println("Parent injector, after entering scope : " + subInjector.getParent());System.out.println("Parent injector is 'injector' : " + injector.equals(subInjector.getParent()));
System.out.println("Pastry binding check : " + subInjector.getBinding(Pastry.class));
  • getParent - 返回当前作用域的父作用域
  • getBinding - 返回所提供的绑定的依赖性
  • getBindings - 返回所提供范围的依赖性(包括 Injector)。
Utils.printGraphVizGraph(subInjector.getBindingsTrie());

你会收到以下输出。

digraph {    rankdir=BT;    "()->DiDependencyGraphExplore$Flour" [标签="DiDependencyGraphExplore$Flour"];    "()->DiDependencyGraphExplore$Sugar" [标签="DiDependencyGraphExplore$Sugar"]    "()->DiDependencyGraphExplore$Butter" [label="DiDependencyGraphExplore$Butter"];    "()->DiDependencyGraphExplore$Cookie" [label="DiDependencyGraphExplore$Cookie"];    "()->Io.activej.di.core.Injector" [label="Injector"];    "()->DiDependencyGraphExplore$Pastry" [label="DiDependencyGraphExplore$Pastry"];
    { rank=same; "()->DiDependencyGraphExplore$Flour" "()->DiDependencyGraphExplore$Sugar" "()->DiDependencyGraphExplore$Butter" "()->Io.activej.di.core.Injector" }
    "()->DiDependencyGraphExplore$Cookie" -> "()->DiDependencyGraphExplore$Pastry"    "()->DiDependencyGraphExplore$Pastry" -> "()->DiDependencyGraphExplore$Butter"    "()->DiDependencyGraphExplore$Pastry" -> "()->DiDependencyGraphExplore$Flour";    "()->DiDependencyGraphExplore$Pastry" -> "()->DiDependencyGraphExplore$Sugar";}

这可以转化为以下图表。

graph BT DiDependencyGraphExplore$Cookie --> id1(DiDependencyGraphExplore$Pastry) id1 --> DiDependencyGraphExplore$Flour id1 --> DiDependencyGraphExplore$Sugar id1 --> DiDependencyGraphExplore$Butter Injector

你可以在以下网站上找到实例来源 GitHub

可选的发电机模块#

OptionalGeneratorModule 工作方式与之前的生成器模块类似,不同的是 OptionalGeneratorModule ,负责创建 Optional 对象。

  • 在下一个例子中,我们将需要一个 可选<String>的实例。
  • 创作的配方放在 模块内
  • install() 建立 OptionalGeneratorModule 以便进一步自动创建 Optional 对象。
  • 然后我们只需 绑定 字符串 配方,并在下一行指定绑定,为键 可选<String>构建一个实例 。
  • 最终,我们只是创建了一个 模块的注入器,要求它获得 的实例,可选<String> 并接收 "Hello, World"
public class OptionalGeneratorModuleExample {  public static void main(String[] args) {    Injector injector = Injector.of(ModuleBuilder.create()        .install(OptionalGeneratorModule.create())        .bind(String.class).toInstance("Hello, World")        .bind(new Key<Optional<String>>() {})        .build());    Optional<String> instance = injector.getInstance(new Key<Optional<String>>() {});    System.out.println(instance);  }}

你可以在以下网站上找到实例来源 GitHub