Elasticsearch Guice 的使用

Elasticsearch 源代码中使用了Guice框架进行依赖注入. 为了方便阅读源码对这块的不理解, 此处我先通过模仿ES guice的使用方式简单写了一个基本Demo 方便理解, 之后再来理一下ES的Guice使用. 编写的测试类原理图如下:

1 Demo

image.png

总共有两个Module, 一个是ToolModule 用于绑定IAnimal接口 ITool接口以及Map对象. 另一个是HumanModule 用于绑定Person对象.其中Person的构造函数通过@Inject注解注入其他实例.
pom文件需要加入以下两个:

<dependency>
            <groupId>com.google.inject.extensions</groupId>
            <artifactId>guice-multibindings</artifactId>
            <version>4.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.google.inject/guice -->
        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>4.0</version>
        </dependency>

首先看一下ToolModule的实现, 它绑了三个实例,

package org.elasticsearch.test;

import com.google.inject.AbstractModule;
import com.google.inject.multibindings.MapBinder;

/**
 * Created by zhangkai12 on 2018/5/29.
 */
public class ToolModule extends AbstractModule {
    @Override
    protected void configure() {
        // 此处注入的实例可以注入到其他类的构造函数中, 只要那个类使用@Inject进行注入即可
        bind(IAnimal.class).to(IAnimalImpl.class);
        bind(ITool.class).to(IToolImpl.class);
        // 注入Map实例
        MapBinder<String,String> mapBinder = MapBinder.newMapBinder(binder(), String.class, String.class);
        mapBinder.addBinding("test1").toInstance("test1");
        mapBinder.addBinding("test2").toInstance("test2");
    }
}

package org.elasticsearch.test;

/**
 * Created by zhangkai12 on 2018/5/29.
 */
public interface ITool {

    public void doWork();
}

package org.elasticsearch.test;

/**
 * Created by zhangkai12 on 2018/5/29.
 */
public class IToolImpl implements ITool {

    @Override
    public void doWork() {
        System.out.println("use tool to do work");
    }
}
package org.elasticsearch.test;

/**
 * Created by zhangkai12 on 2018/5/29.
 */
public interface IAnimal {
    void work();
}
package org.elasticsearch.test;

/**
 * Created by zhangkai12 on 2018/5/29.
 */
public class IAnimalImpl implements IAnimal {

    @Override
    public void work() {
        System.out.println("animals can also do work");
    }
}

bind(IAnimal.class).to(IAnimalImpl.class);bind(ITool.class).to(IToolImpl.class); 是将接口与其具体实现绑定起来, MapBinder<String,String> mapBinder = MapBinder.newMapBinder(binder(), String.class, String.class); mapBinder.addBinding("test1").toInstance("test1");
mapBinder.addBinding("test2").toInstance("test2"); 则是完成Map的绑定. 后面来看看Person类和HumanModule

package org.elasticsearch.test;


import com.google.inject.Inject;
import com.google.inject.multibindings.MapBinder;

import java.util.Map;

/**
 * Created by zhangkai12 on 2018/5/29.
 */
public class Person {


    private IAnimal iAnimal;
    private ITool iTool;
    private Map<String,String> map;
    @Inject
    public Person(IAnimal iAnimal, ITool iTool, Map<String,String> mapBinder) {
        this.iAnimal = iAnimal;
        this.iTool = iTool;
        this.map = mapBinder;
    }

    public void startWork() {

        iTool.doWork();
        iAnimal.work();
        for (Map.Entry entry : map.entrySet()) {
            System.out.println("注入的map 是 " + entry.getKey() + " value " + entry.getValue());
        }
    }
}
package org.elasticsearch.test;

import com.google.inject.AbstractModule;

/**
 * Created by zhangkai12 on 2018/5/29.
 */
public class HumanModule extends AbstractModule{

    @Override
    protected void configure() {
        bind(Person.class).asEagerSingleton();
    }
}

Person类的构造函数是通过注入的方式,注入对象实例的.
最后CustomModuleBuilder进行统一管理所有的Module, 实例化所有Module中的对象. 完成依赖注入. 这里的CustomModuleBuilder是修改自Elasticsearch中的ModulesBuilder, 其原理是一样的.就是一个迭代器,内部封装的是Module集合, 统一管理所有的Module.

package org.elasticsearch.test;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import org.elasticsearch.common.inject.Injectors;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class CustomModuleBuilder implements Iterable<Module> {

    private final List<Module> modules = new ArrayList<>();

    public CustomModuleBuilder add(Module... newModules) {
        for (Module module : newModules) {
            modules.add(module);
        }
        return this;
    }

    @Override
    public Iterator<Module> iterator() {
        return modules.iterator();
    }

    public Injector createInjector() {
        Injector injector = Guice.createInjector(modules);
        //Injectors.cleanCaches(injector);
        // in ES, we always create all instances as if they are eager singletons
        // this allows for considerable memory savings (no need to store construction info) as well as cycles
        //((InjectorImpl) injector).readOnlyAllSingletons();
        return injector;
    }

}

这样就可以从Main函数看卡是如何进行使用的.

package org.elasticsearch.test;
import com.google.inject.Injector;

/**
 * Created by zhangkai12 on 2018/5/29.
 */
public class Main {
    public static void main(String[] args) {
        CustomModuleBuilder modules = new CustomModuleBuilder();
        modules.add(new ToolModule());
        modules.add(new HumanModule());
        Injector injector = modules.createInjector(); // 注入所有module下的实例
        Person instance = injector.getInstance(Person.class);
        instance.startWork();

    }
}

通过CustomModuleBuilder 的createInjector获取Injector 对象, 根据Injector 对象取相应的具体实例对象.

2 ES 中Guice的使用

ES中TransportClient初始化时的Guice的使用是这样的, 如下图所示:


image.png

TransportClient的初始化代码:

public TransportClient build() {
            Settings settings = InternalSettingsPreparer.prepareSettings(this.settings);
            settings = settingsBuilder()
                    .put(NettyTransport.PING_SCHEDULE, "5s") // enable by default the transport schedule ping interval
                    .put(settings)
                    .put("network.server", false)
                    .put("node.client", true)
                    .put(CLIENT_TYPE_SETTING, CLIENT_TYPE)
                    .build();

            PluginsService pluginsService = new PluginsService(settings, null, null, pluginClasses);
            this.settings = pluginsService.updatedSettings();

            Version version = Version.CURRENT;

            final ThreadPool threadPool = new ThreadPool(settings);
            NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry();

            boolean success = false;
            try {
                ModulesBuilder modules = new ModulesBuilder();
                modules.add(new Version.Module(version));
                // plugin modules must be added here, before others or we can get crazy injection errors...
                for (Module pluginModule : pluginsService.nodeModules()) {
                    modules.add(pluginModule);
                }
                modules.add(new PluginsModule(pluginsService));
                modules.add(new SettingsModule(this.settings));
                modules.add(new NetworkModule(namedWriteableRegistry));
                modules.add(new ClusterNameModule(this.settings));
                modules.add(new ThreadPoolModule(threadPool));
                modules.add(new TransportModule(this.settings, namedWriteableRegistry));
                modules.add(new SearchModule() {
                    @Override
                    protected void configure() {
                        // noop
                    }
                });
                modules.add(new ActionModule(true));
                modules.add(new ClientTransportModule(hostFailedListener));
                modules.add(new CircuitBreakerModule(this.settings));

                pluginsService.processModules(modules);

                Injector injector = modules.createInjector();
                final TransportService transportService = injector.getInstance(TransportService.class);
                transportService.start();
                transportService.acceptIncomingRequests();

                TransportClient transportClient = new TransportClient(injector); // 初始化TransportClient对象
                success = true;
                return transportClient;
            } finally {
                if (!success) {
                    ThreadPool.terminate(threadPool, 10, TimeUnit.SECONDS);
                }
            }
        }

可以看到确实是先通过ModulesBuilder modules = new ModulesBuilder() 创建一个迭代器, 然后将各个Module通过add方法加入进去, 最后通过Injector injector = modules.createInjector();创建Injector对象, 之后便可根据Injector对象去获取实例了. 各个Module会绑定自己所需要的实例, 这里以SettingsModule举例:

public class SettingsModule extends AbstractModule {

    private final Settings settings;

    public SettingsModule(Settings settings) {
        this.settings = settings;
    }

    @Override
    protected void configure() {
        bind(Settings.class).toInstance(settings);
        bind(SettingsFilter.class).asEagerSingleton();
    }
}

可以看到它绑定了两个,一个是Settings类, 一个单例类SettingsFilter, 它们的实例对象都可以通过Injector来获取.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • 此文为本人学习guice的过程中,翻译的官方文档,如有不对的地方,欢迎指出。另外还有一些附件说明、吐槽、疑问点,持...
    李眼镜阅读 3,486评论 2 5
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • AngularJS是什么?AngularJs(后面就简称ng了)是一个用于设计动态web应用的结构框架。首先,它是...
    200813阅读 1,592评论 0 3
  • core package 概要:Core是所有其他包的基础包.它提供了大部分功能包括metadata,templa...
    LOVE小狼阅读 2,564评论 0 3
  • 就在半个小时前,家里突然停电了 (说明一下,我现在是和上海的两位老夫老妻一起住,他们是房东,60多了,我暂时先称呼...
    弹琴对窝阅读 453评论 0 0