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来获取.
作者:kason_zhang
链接:https://www.jianshu.com/p/0a1e6267b46f