Google Guice 分析

最近工作中使用到了 Presto 作为分布式即席查询引擎,其中插件相关的管理基本使用 Guice 注入框架进行插件生命周期管理,因此在此对 Guice 进行简单分析。

常见类说明

类名 说明
Guice 框架注册入口
Injector 注入器声明主类
Binder 接口类与实现之间的绑定器
Module 管理及建立Binder关系的类
Provider 注入服务提供者,与 Module 中配置效果一致
Key Provider 键名,类似 spring 中 Bean 的名称,与 Provider 一对一关系
Scope Provider 的作用范围

因此,可以简单进行如下理解:

  • Guice 依据 Module 中的配置,将接口与实现类建立绑定关系,在 Injector 中形成接口与实现之间的 Mapping 关系
  • 在 Injector 中的 Mapping 中 使用 Binding 来维护绑定关系,其中用 Key 来维护接口(在 Key 中通常也会含有一个 Annotation 属性,用来标记当前接口所指定的那个实现类名称),用 Provider 来维护实现
  • 通过 injector.getInstance(XXX.class) 的方式创建对应接口的实例, 亦可使用 @inject 标注来完成

实例说明

  1. 构建 Module 对象
  • 普通方式
import com.google.inject.AbstractModule;

public class BasicModule extends AbstractModule {

    @Override
    protected void configure() {
        // 将 ILogger 与 LoggerImpl 进行绑定
        bind(Key.get(ILogger.class, Names.named("ilogger1"))).to(LoggerImpl.class);
        bind(Key.get(ILogger.class, Names.named("ilogger2"))).to(Logger2Impl.class);

        // 或者通过 普通接口,标记注解内容,临时创建 Provider 的方式进行
        bind(ILogger.class).annotatedWith(Names.named("ilogger1")).toProvider(new Provider() {
            @Override
            public ILogger get() {
                return new LoggerImpl();
            }
        });

        // 将 Logger 实例绑定为 new Logger(true) 
        bind(Logger.class).toInstance(new Logger(true));
    }
}
  • 注解方式
public class BasicModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(Logger.class).toInstance(new Logger(true));
    }

    @Provides
    @Singleton
    @Named("ilogger1")
    public ILogger getILogger1() {
        return new LoggerImpl();
    }

    @Provides
    @Singleton
    @Named("ilogger2")
    public ILogger getILogger2() {
        return new Logger2Impl();
    }
}

public class StringModule extends AbstractModule {

    @Override
    protected void configure() {
        // 将 String 实例绑定为 "jdbc:phoenix:localhost:2181/hbase"
        bind(String.class).annotatedWith(Names.named("url")).toInstance("jdbc:phoenix:localhost:2181/hbase");
    }

}

多实现接口定义及使用过程中均需要通过 @Named 注解进行名称绑定,否则会出现 A binding to XXX was already configured at YYY

  1. 定义接口及其实现
public interface ILogger {
    boolean log(String message);
}

public class LoggerImpl implements ILogger {
    public boolean log(String message) {
        System.out.println("Logger Message: " + message);
        return true;
    }
}

public class Logger2Impl implements ILogger {
    public boolean log(String message) {
        System.out.println("Logger2 Message: " + message);
        return true;
    }
}
  1. 测试运行
import com.google.inject.*;
import com.google.inject.name.Named;

import java.util.Map;

public class Logger {
    private boolean hasEnabled;

    @Inject
    @Named("ilogger1")
    private ILogger iLogger1;

    @Inject
    @Named("ilogger2")
    private ILogger iLogger2;

    public Logger(boolean enabled) {
        if (enabled) {
            hasEnabled = enabled;
            System.out.println("Logger has enabled");
        }
    }

    public boolean log(String message) {
        if (hasEnabled) {
            iLogger1.log(message);
        }
        return true;
    }


    public boolean log2(String message) {
        if (hasEnabled) {
            iLogger2.log(message);
        }
        return true;
    }

    public static void main(String[] args) {
        // 同时配置两个模块: BasicModule 和 StringModule
        Injector injector = Guice.createInjector(new BasicModule(), new StringModule());
        Logger logger = injector.getInstance(Logger.class);
        String url = injector.getInstance(Key.get(String.class, Names.named("url")));
        logger.log("hello world  111");
        logger.log2("hello world  222");
        System.out.println("******" + url + "******");
        System.out.println();

        Map, Binding> bindings = injector.getAllBindings();
        for (Map.Entry, 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("************");
        }
    }
}

其输出内容如下:

Logger has enabled
Logger Message: hello world  111
Logger2 Message: hello world  222
******jdbc:phoenix:localhost:2181/hbase******

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=com.devhub.guice.Logger, annotation=[none]]
Provider: class com.devhub.guice.Logger
************
Key: Key[type=com.devhub.guice.ILogger, [email protected](value=ilogger1)]
Provider: class com.devhub.guice.LoggerImpl
************
Key: Key[type=com.devhub.guice.ILogger, [email protected](value=ilogger2)]
Provider: class com.devhub.guice.Logger2Impl
************
Key: Key[type=com.devhub.guice.Logger2Impl, annotation=[none]]
Provider: class com.devhub.guice.Logger2Impl
************
Key: Key[type=com.devhub.guice.LoggerImpl, annotation=[none]]
Provider: class com.devhub.guice.LoggerImpl
************

Process finished with exit code 0

多实现接口使用应与其定义一致,使用 @Named 注解进行标注名称

由上述例子可看出,Named 中定义的名称会被存放到 Key 中,从而区别不同的实现

如上述所示,可同时注入多个 Module ,每个 Module 配置不同的内容,从而将接口实现进行多类型,方便模块公用

Gradle 依赖库

compile group: 'com.google.inject', name: 'guice', version: '4.2.2'

参考

  • guice wiki 说明文档

你可能感兴趣的:(Google Guice 分析)