2021SC@SDUSC
一、Inject概述
ActiveJ Inject是一个轻量级的、强大的依赖注入库,具有极强的性能,没有第三方的依赖性。 它对多线程友好,功能丰富,可以夸耀的是,它的启动时间和运行时间非常快,大大超过了Spring DI或Guice。 ActiveJ Inject是ActiveJ技术之一,但它对第三方的依赖性很小,可以作为一个独立的组件使用。
ActiveJ Inject特点:
- 支持基于注解的配置以及手动绑定,以避免反射开销。
- 绑定被分组成模块,可以在其他应用程序中重复使用。
- 针对单线程和多线程使用情况的优化注入
- 有组合、覆盖和转换绑定的能力
- 支持单子、嵌套作用域和瞬时绑定
- 依赖关系图的处理在启动时进行一次
- 提供反省依赖关系图的方法
- 没有第三方依赖
依赖性注入: 重定义
利用各种强大的工具享受开发。 ActiveJ Inject简化了开发、调试、重构和重用你的代码,没有限制和开销。
注释处理被分离到一个标准插件中,该插件默认使用,并允许生成缺失的依赖。 然而,如果你需要实现复杂的业务逻辑,你可以使用 ActiveJ Inject DSL(DSL即Domain Specific Language,中文翻译为领域特定语言),甚至可以创建自己的注解处理插件。
DSL提供了对编程式绑定生成的支持,对依赖关系图的反省,转换,自动生成缺失的绑定,以及对已经存在的绑定的修改。通过这种方式,您可以使用Java的全部功能,在运行时直接根据运行时信息和设置,通过算法创建复杂的绑定和依赖关系图。
Module cookbook = new AbstractModule() {
@Provides
Sugar sugar() { return new Sugar("Sugar", 10.f); }
@Provides
Butter butter() { return new Butter("Butter", 20.0f); }
@Provides
Flour flour() { return new Flour("Flour", 100.0f); }
@Provides
Pastry pastry(Sugar sugar, Butter butter, Flour flour) {
return new Pastry(sugar, butter, flour);
}
@Provides
Cookie cookie(Pastry pastry) {
return new Cookie(pastry);
}
};
Injector.of(cookbook).getInstance(Cookie.class);
二、core-inject结构
源代码中core-inject结构如下:
如上图所示,io.activej.inject下属annotation、binding、impl、module、util这五个包,而且还有Inject、InstanceInjector、InstanceProvider、Key、KeyPattern、Qualifiers、ResourceLocator、Scope这8个单独的类。这一次的博客就是读一下这8个单独的类的功能。
三、代码解读
3.1 Injector
Injector是ActiveJ Injector的主要工作部件。
它存储绑定图的trie和已生成的单例缓存。
每个注入器与每个Key正好零个或一个实例关联。
Injector使用trie根目录下的绑定图递归创建并存储与某些Key关联的对象实例。trie的分支用于enter scopes。
Injector为组件 ,以后序方式递归地遍历依赖关系图,并首先创建它们,提供所有需要的依赖关系(注入)。
绑定是默认的单子–如果一个实例被创建过一次,就不会再从头开始创建。 如果 ,它被其他绑定所需要,Injector将从缓存中获取它。 你不需要为它应用任何额外的注释。
为了提供请求的密钥,Injector递归地创建它的所有依赖,如果在它的范围内没有找到绑定,则退回到其父范围的Injector。
下面看一下Injector类里面的方法:
构造方法:
private Injector(@Nullable Injector parent, Trie<Scope, ScopeLocalData> scopeDataTree) {
this.parent = parent;
this.scopeDataTree = scopeDataTree;
ScopeLocalData data = scopeDataTree.get();
this.localSlotMapping = data.slotMapping;
this.localCompiledBindings = data.compiledBindings;
AtomicReferenceArray[] scopeCaches = parent == null ?
new AtomicReferenceArray[1] :
Arrays.copyOf(parent.scopeCaches, parent.scopeCaches.length + 1);
AtomicReferenceArray localCache = new AtomicReferenceArray(data.slots);
localCache.set(0, this);
scopeCaches[scopeCaches.length - 1] = localCache;
this.scopeCaches = scopeCaches;
}
useSpecializer方法:
此方法启用编译绑定的专门化。依赖于ActiveJ Specializer模块。
public static void useSpecializer() {
try {
Class<?> aClass = Class.forName("io.activej.specializer.Utils$InjectorSpecializer");
Constructor<?> constructor = aClass.getConstructor();
constructor.setAccessible(true);
Object specializerInstance = constructor.newInstance();
Function<CompiledBinding<?>, CompiledBinding<?>> specializer = (Function<CompiledBinding<?>, CompiledBinding<?>>) specializerInstance;
Injector.bytecodePostprocessorFactory = () -> specializer;
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException | InstantiationException e) {
throw new IllegalStateException("Can not access ActiveJ Specializer", e);
}
}
几个of方法:
此构造函数将给定的模块(以及DefaultModule)组合在一起,然后compile(Injector,Module)编译它们。
public static Injector of(Module... modules) {
return compile(null, Modules.combine(Modules.combine(modules), new DefaultModule()));
}
public static Injector of(@Nullable Injector parent, Module... modules) {
return compile(parent, Modules.combine(Modules.combine(modules), new DefaultModule()));
}
public static Injector of(@NotNull Trie<Scope, Map<Key<?>, Binding<?>>> bindings) {
return compile(null, UNSCOPED,
bindings.map(map -> map.entrySet().stream().collect(toMap(Entry::getKey, entry -> singleton(entry.getValue())))),
errorOnDuplicate(),
identity(),
refusing());
}
compile方法:
我认为这个编译方法还是比较重要的。它是最成熟的编译方法,允许您创建任何配置的Injector。需要注意的是,任何Injector都会设置一个Injector key绑定,以提供其自身。下面介绍一下这些key:
- @param parent 父Injector,当此Injector无法满足请求时调用该Injector
- @param scope Injector的范围,可以描述为绑定trie的“根前缀”,当{ enterScope 进入范围}时使用
- @param bindingsMultimap 绑定集图的trie,每个键具有多个可能冲突的绑定,这些绑定作为编译的一部分进行解析。
- @param multibinder 在每次绑定冲突时调用的multibinder(请参见{@link Multibinders#combinedMultibinder})
- @param transformer 每次绑定调用一次的转换器(请参见{@link BindingTransformers#combinedTransformer})
- @param generator 对每个缺少的绑定调用的生成器(请参见{@link BindingGenerators#combinedGenerator})
public static Injector compile(@Nullable Injector parent,
Scope[] scope,
@NotNull Trie<Scope, Map<Key<?>, Set<Binding<?>>>> bindingsMultimap,
@NotNull Multibinder<?> multibinder,
@NotNull BindingTransformer<?> transformer,
@NotNull BindingGenerator<?> generator) {
Trie<Scope, Map<Key<?>, Binding<?>>> bindings = Preprocessor.reduce(bindingsMultimap, multibinder, transformer, generator);
Set<Key<?>> known = new HashSet<>();
known.add(Key.of(Injector.class)); // injector is hardcoded in and will always be present
if (parent != null) {
known.addAll(parent.localCompiledBindings.keySet());
}
Preprocessor.check(known, bindings);
Trie<Scope, ScopeLocalData> scopeDataTree = compileBindingsTrie(
parent != null ? parent.scopeCaches.length : 0,
scope,
bindings,
parent != null ? parent.localCompiledBindings : emptyMap()
);
return new Injector(parent, scopeDataTree);
}
Injector中还有一些其他方法,以上是我挑出的我认为比较重要的几个方法。
3.2 InstanceInjector接口
这是一个函数,可以将实例注入{@link io.activej.inject.annotation.inject}
某些已存在对象的字段和方法。
这就是所谓的“post-injections(后注入)”,因为这种注入不是对象创建的一部分。
它有一个io.activej.inject.module.DefaultModule default generator,只能通过依赖它然后从Injector请求它来获得。
源代码如下:
public interface InstanceInjector<T> {
Key<T> key();
void injectInto(T existingInstance);
}
它可以将实例注入到 Inject 一些已经存在的对象的字段和方法。 看一下这个简单的例子。
@Inject
String message;
@Provides
String message() {
return "Hello, world!";
}
@Override
protected void run() {
System.out.println(message);
}
public static void main(String[] args) throws Exception {
Launcher launcher = new InstanceInjectorExample();
launcher.launch(args);
}
可能会有些疑惑的问题是Launcher ,实际上如何知道消息变量包含 “Hello, world!” 字符串,以便在 run() 方法中显示它? 在这里,在DI的内部工作中, InstanceInjector 实际上是给launcher提供了帮助。
3.3 InstanceProvider接口
与其他DI框架不同,提供程序只是{Injector.getInstance}的一个版本,带有一个烘焙键。
如果您需要一个每次返回一个新对象的函数,那么您需要使绑定{@link Transient}。
它存在的主要原因是它的绑定有一个{io.activej.inject.module.DefaultModule default generator},因此{io.activej.inject.annotation.provider methods}等可以流畅地请求它。
此外,它还可以用于延迟依赖循环解析。
public interface InstanceProvider<T> {
Key<T> key();
T get();
}
3.4 ResourceLocator接口
作为资源定位器,其中一些方法也是getInstance方法的改写。
public interface ResourceLocator {
<T> @NotNull T getInstance(@NotNull Key<T> key);
<T> @NotNull T getInstance(@NotNull Class<T> type);
<T> @Nullable T getInstanceOrNull(@NotNull Key<T> key);
<T> @Nullable T getInstanceOrNull(@NotNull Class<T> type);
<T> T getInstanceOr(@NotNull Key<T> key, T defaultValue);
<T> T getInstanceOr(@NotNull Class<T> type, T defaultValue);
}
3.5 Qualifiers
此类包含用于验证和创建用作限定符的对象的实用程序方法。
限定符用作附加标记,以区分具有相同类型的不同Key。
public final class Qualifiers {
public static Object uniqueQualifier() {
return new UniqueQualifierImpl();
}
public static Object uniqueQualifier(@Nullable Object qualifier) {
return qualifier instanceof UniqueQualifierImpl ? qualifier : new UniqueQualifierImpl(qualifier);
}
public static boolean isUnique(@Nullable Object qualifier) {
return qualifier instanceof UniqueQualifierImpl;
}
}
3.6 Key
Key定义绑定的标识。在任何DI中,Key通常是一种对象类型以及一些可选标记,用于区分使对象具有相同类型的绑定。
在ActiveJ Inject中,Key也是一个类型标记特殊的抽象类,可以用Java中最短的语法存储类型信息。
例如,要创建一个key的类型:Map<String, List<Integer>>, 你可以使用这个语法: new Key<Map<String, List<Integer>>>(){}。
如果您的类型在编译时未知,则可以使用io.activej.types.types#parameteredType生成一个参数化类型,并将其交给ofType Key.ofType构造函数。
构造方法如下:
public Key() {
this.type = getTypeParameter();
this.qualifier = null;
}
public Key(@Nullable Object qualifier) {
this.type = getTypeParameter();
this.qualifier = qualifier;
}
Key(@NotNull Type type, @Nullable Object qualifier) {
this.type = type;
this.qualifier = qualifier;
}
qualified方法:
返回具有相同类型的新键,但限定符已替换为给定的键
public Key<T> qualified(Object qualifier) {
return new KeyImpl<>(type, qualifier);
}
getRawType()方法:
{Types#getRawType(Type)}(key.getType())的快捷方式。
还将结果强制转换为正确参数化的类。
public @NotNull Class<T> getRawType() {
return (Class<T>) Types.getRawType(type);
}
getDisplayString方法:
如果此key具有限定符,则返回具有显示字符串格式(包名称已剥离)和前置限定符显示字符串的基础类型。
public String getDisplayString() {
return (qualifier != null ? Utils.getDisplayString(qualifier) + " " : "") + ReflectionUtils.getDisplayName(type);
}
3.7 KeyPattern
匹配依赖项注入Key的模式
如果Key#getType()Key的类型可分配给此模式的类型,并且此模式的限定符为null或匹配 Key#getQualifier()Key的限定符,则Key匹配。
构造方法:
public KeyPattern() {
this.type = getTypeParameter();
this.qualifier = null;
}
public KeyPattern(Object qualifier) {
this.type = getTypeParameter();
this.qualifier = predicateOf(qualifier);
}
public KeyPattern(Predicate<?> qualifier) {
this.type = getTypeParameter();
this.qualifier = qualifier;
}
KeyPattern(@NotNull Type type, Predicate<?> qualifier) {
this.type = type;
this.qualifier = qualifier;
}
3.8 Scope
Scope 给我们提供了 “local singletons”,它的寿命与scope本身一样长。 ActiveJ Inject的作用域与其他DI库有些不同。
- Injector 的内部结构是一个 前缀树 ,前缀是一个范围。
- 树的标识符(或前缀)是简单的注释。
- Injector可以进入范围。 这意味着你创建了一个 Injector ,它的范围将被设置为它所进入的那个范围。
- 这可以做多次,所以你可以在某些范围内有 N个Injector。
构造方法:
private Scope(@NotNull Class<? extends Annotation> annotationType, boolean threadsafe) {
this.annotationType = annotationType;
this.threadsafe = threadsafe;
}
of方法:
从标记(或无状态)注释创建范围,仅由其类标识。
public static Scope of(Class<? extends Annotation> annotationType) {
checkArgument(isMarker(annotationType), "Scope by annotation type only accepts marker annotations with no arguments");
ScopeAnnotation scopeAnnotation = annotationType.getAnnotation(ScopeAnnotation.class);
checkArgument(scopeAnnotation != null, "Only annotations annotated with @ScopeAnnotation meta-annotation are allowed");
return new Scope(annotationType, scopeAnnotation.threadsafe());
}
从真实(或其自定义代理impl)注释实例创建范围。
public static Scope of(Annotation annotation) {
return of(annotation.annotationType());
}
四、总结
本次解读的代码时core-inject中独立于几个包之外的类或接口,有些是其他类的依赖,有些是需要在其他类中实现的接口,都是比较重要的。