最近工作中使用到了 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
标注来完成
实例说明
- 构建
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<ILogger>() {
@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
- 定义接口及其实现
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;
}
}
- 测试运行
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<Key<?>, Binding<?>> bindings = injector.getAllBindings();
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("************");
}
}
}
其输出内容如下:
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, annotation=@com.google.inject.name.Named(value=ilogger1)]
Provider: class com.devhub.guice.LoggerImpl
************
Key: Key[type=com.devhub.guice.ILogger, annotation=@com.google.inject.name.Named(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'
网友评论