欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Google Guice 一个轻量级的依赖注入框架

程序员文章站 2022-04-17 17:33:11
...

1.美图

Google Guice 一个轻量级的依赖注入框架

2.概述

2.1 背景

在做项目的时候,看见有段代码直接是使用Google Guice 注入了avaitor表达式。

2.1 官网

Github 主页:https://github.com/google/guice
API:http://google.github.io/guice/api-docs/4.0/javadoc/

Guice (pronounced 'juice') is a lightweight dependency injection framework for 
Java 6 and above, brought to you by Google. 
一个轻量级的依赖注入框架。

关于 Spring 的依赖注入,请参见 Spring 依赖注入 DI 的方式

3.案例

一个 Google Guice 示例参见 Guide to Google Guice
例如我们有一个 Communication 类,它实际上是利用 Communicator 来真正的发送消息。
添加 Maven 的依赖:

<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>4.0</version>
</dependency>

我们首先定义 Communicator 接口,和它的一个实现类 DefaultCommunicatorImpl:

package com.google.guice.demo;

public interface Communicator {
    boolean sendMessage(String message);
}

package com.google.guice.demo;

public class DefaultCommunicatorImpl implements Communicator {
    public boolean sendMessage(String message) {
        System.out.println("Sending Message + " + message);
        return true;
    }
}

随后我们通过 @Inject 注解来在 Communication 类中注入 Communicator 类的依赖:

package com.google.guice.demo;

import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

import java.util.logging.Logger;

public class Communication {
    @Inject
    private Communicator communicator;

    public Communication(Boolean keepRecords) {
        if (keepRecords) {
            System.out.println("Message logging enabled");
        }
    }

    public boolean sendMessage(String message) {
        communicator.sendMessage(message);
        return true;
    }


}

那么 BasicModule 类又是怎么样的呢?

The Module is the basic unit of definition of bindings. 定义依赖绑定的基本单元。

  1. 它需要继承 AbstractModule 类
  2. 它将 Communication 绑定了到一个实例 Instance,传入参数 true 到构造方法
  3. 它将 Communicator 绑定了到一个具体的实现 DefaultCommunicatorImpl
package com.google.guice.demo;

import com.google.inject.AbstractModule;

public class BasicModule extends AbstractModule {

    @Override
    protected void configure() {
        // 表明:当需要 Communicator 这个变量时,我们注入 DefaultCommunicatorImpl 的实例作为依赖
        bind(Communicator.class).to(DefaultCommunicatorImpl.class);

        bind(Communication.class)
                .toInstance(new Communication(true));
    }
}

测试类以及输出结果

package com.google.guice.demo;

import com.google.inject.Guice;
import com.google.inject.Injector;

import static org.junit.Assert.*;

public class CommunicationTest {


    /**
     * 输出如下
     * Message logging enabled
     * Sending Message + hello world
     */
    @org.junit.Test
    public  void sendMessage() {
        Injector injector = Guice.createInjector(new BasicModule());

        Communication comms = injector.getInstance(Communication.class);

        comms.sendMessage("hello world");
    }
}

在 sendMessage() 中,可以看到我们通过 Injector 得到了一个 Communication 实例,随后调用了 sendMessage() 方法。

可以看到,Guice 通过代码的形式来注入并管理依赖,而不是通过 XML 配置文件的形式,这个与 Spring 不太一样。

3.1 注解方法

我们也可通过 @Provides 注解来在 BasicModule 中定义依赖:

package com.google.guice.demo;

import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;

public class BasicModule extends AbstractModule {

    @Override
    protected void configure() {
        // 表明:当需要 Communicator 这个变量时,我们注入 DefaultCommunicatorImpl 的实例作为依赖
        bind(Communicator.class).to(DefaultCommunicatorImpl.class);

        bind(Communication.class)
                .toInstance(new Communication(true));
    }


    @Provides
    @Singleton
    public Communicator getCommunicator() {
        return new DefaultCommunicatorImpl();
    }
}

其中 @Singleton 注解表明这个依赖的 Scope 是单例,它是延时加载的 lazily initiated。

如果我们对一个依赖进行了多次绑定,例如:

@Provides
@Singleton
public Communicator getCommunicator() {
    return new DefaultCommunicatorImpl();
}

@Provides
@Singleton
public Communicator getCommunicatorOneMoreTime() {
    return new DefaultCommunicatorImpl();
}

运行时会抛出如下的异常:

1) A binding to demo.guice.Communicator was already configured at demo.guice.BasicModule.getCommunicator().
  at demo.guice.BasicModule.getCommunicator(BasicModule.java:17)

1 error
    at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:466)
    at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:155)
    at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:107)
    at com.google.inject.Guice.createInjector(Guice.java:96)
    at com.google.inject.Guice.createInjector(Guice.java:73)
    at com.google.inject.Guice.createInjector(Guice.java:62)

假如我们现在有了 Communicator 接口的另外一种实现 AnotherCommunicatorImpl:

public class AnotherCommunicatorImpl implements Communicator {
    public boolean sendMessage(String message) {
        System.out.println("Another Sending Message + " + message);
        return true;
    }
}

同时我们在 Communication 类中需要同时依赖于原有的 DefaultCommunicatorImpl 和新定义的 AnotherCommunicatorImpl,例如:

public class Communication {

    @Inject
    private Communicator communicator;

    @Inject
    private Communicator anotherCommunicator;

    public Communication(Boolean keepRecords) {
        if (keepRecords) {
            System.out.println("Message logging enabled");
        }
    }

    public boolean sendMessage(String message) {
        communicator.sendMessage(message);

        anotherCommunicator.sendMessage(message);

        return true;
    }

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new BasicModule());

        Communication comms = injector.getInstance(Communication.class);

        comms.sendMessage("hello world");
    }
}

那么我们在 BasicModule 应该怎么定义这种绑定呢?
如果我们尝试添加另外一个 @Provides 方法,返回 AnotherCommunicatorImpl,例如:

@Provides
@Singleton
public Communicator getCommunicator() {
    return new DefaultCommunicatorImpl();
}

@Provides
@Singleton
public Communicator getAnotherCommunicator() {
    return new AnotherCommunicatorImpl();
}

则会有如下的异常:

Exception in thread "main" com.google.inject.CreationException: Unable to create injector, see the following errors:

1) A binding to demo.guice.Communicator was already configured at demo.guice.BasicModule.getCommunicator().
  at demo.guice.BasicModule.getAnotherCommunicator(BasicModule.java:23)

这里我们需要通过 @Named 注解提供为属性赋值的功能。
首先在注入绑定的时候使用 @Named 注解:

@Inject
@Named("communicator")
private Communicator communicator;

@Inject
@Named("anotherCommunicator")
private Communicator anotherCommunicator;
随后在定义绑定的时候使用 @Named 注解:

@Provides
@Singleton
@Named("communicator")
public Communicator getCommunicator() {
    return new DefaultCommunicatorImpl();
}

@Provides
@Singleton
@Named("anotherCommunicator")
public Communicator getAnotherCommunicator() {
    return new AnotherCommunicatorImpl();
}

运行结果如下:

Message logging enabled
Sending Message + hello world
Another Sending Message + hello world

4.Guice 的工作原理

总的来说:

Guice:整个框架的门面
Injector:一个依赖的管理上下文
Binder:一个接口和实现的绑定
Module:一组 Binder
Provider:bean 的提供者
Key:Binder 中对应一个 Provider
Scope:Provider 的作用域

每个绑定 Binding 的结构如下:

public interface Binding<T> extends Element {
    Key<T> getKey();

    Provider<T> getProvider();

同时它继承了 Element,里面包含了 Source:

public interface Element {
    Object getSource();

可以看出每个绑定 Binding,包含一个键 Key 和 一个提供者 Provider:

键 Key 唯一地确定每一个绑定。 键 Key 包含了客户代码所依赖的类型以及一个可选的标注。你可以使用标注来区分指向同一类型的多个绑定。

例如,上述的代码中,Communicator 类型的就有两个键:

Key[type=demo.guice.Communicator, aaa@qq.com(value=communicator)]
Key[type=demo.guice.Communicator, aaa@qq.com(value=anotherCommunicator)]

对于每一个提供者 Provider,它提供所需类型的实例:

你可以提供一个类,Guice 会帮你创建它的实例。
你也可以给 Guice 一个你要绑定的类的实例。
你还可以实现你自己的 Provider,Guice 可以向其中注入依赖关系。
例如,上述的代码中,就有一个提供者是 class demo.guice.DefaultCommunicatorImpl
每个绑定还有一个可选的作用域。缺省情况下绑定没有作用域,Guice 为每一次注入创建一个新的对象。一个定制的作用域可以使你控制 Guice 是否创建新对象。例如,你可以使用 为每一个 HttpSession 创建一个实例。

我们可以通过如下的方式遍历每一个绑定 Binding

Injector injector = Guice.createInjector(new BasicModule());

Map<Key<?>, Binding<?>> bindings = injector.getBindings();

for (Map.Entry<Key<?>, Binding<?>> bingingEntry : bindings.entrySet()) {

    Binding binging = bingingEntry.getValue();

    Key key =  binging.getKey();
    Provider provider = binging.getProvider();

    System.out.println("Key: " + key.toString());

    System.out.println("Provider: " + provider.get().getClass());

    System.out.println("************");
}

输出如下:

Key: Key[type=com.google.inject.Stage, annotation=[none]]
Provider: class com.google.inject.Stage
************
Key: Key[type=com.google.inject.Injector, annotation=[none]]
Provider: class com.google.inject.internal.InjectorImpl
************
Key: Key[type=java.util.logging.Logger, annotation=[none]]
Provider: class java.util.logging.Logger
************
Key: Key[type=demo.guice.Communication, annotation=[none]]
Provider: class demo.guice.Communication
************
Key: Key[type=demo.guice.Communicator, aaa@qq.com(value=communicator)]
Provider: class demo.guice.DefaultCommunicatorImpl
************
Key: Key[type=demo.guice.Communicator, aaa@qq.com(value=anotherCommunicator)]
Provider: class demo.guice.AnotherCommunicatorImpl
************

injector.getInstance(XXX.class); 的过程:

先根据指定的类来 new Key(),Key 包括类信息 XXX.class 和注解信息,XXX.class的 hashcode 和注解的 hashcode 决定了 Key 的 hashcode,getProvider 时是根据 Key 的 hashcode 来判断是否是同一个Key,然后取到 Provider,由 Provider 提供最终的示例。
例如上面 Key[type=demo.guice.Communicator, aaa@qq.com(value=communicator)] 和 Key[type=demo.guice.Communicator, aaa@qq.com(value=anotherCommunicator)] 的 hashcode 就分别为 -1491509781 和 349671560。

5.Guice DI 与 Spring DI 的比较

参考 Guice与Spring的区别

5.1 使用方式:

Spring 将类与类之间的关系隔离到 XML 中,由容器负责注入被调用的对象
Guice 不使用 XML,而是使用注解 Annotation

5.2 运行效率:

Guice 使用注解 Annotation,cglib, 效率高,这是与与 Spring 最明显的一个区别,Spring 是在装载配置文件的时候把该注入的地方都注入完,而 Guice 呢,则是在使用的时候去注射,运行效率和灵活性高。

5.3 类耦合度:

Spring 耦合度低,强调类非侵入,以外部化的方式处理依赖关系,类里边是很干净的,在配置文件里做文章
Guice 耦合度高,代码级的标注,DI 标记 @inject 侵入代码中,耦合到了类层面上来

链接:https://www.jianshu.com/p/7fba7b43146a

相关标签: 依赖工具包