ActiveJ学习心得——inject(1)

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中独立于几个包之外的类或接口,有些是其他类的依赖,有些是需要在其他类中实现的接口,都是比较重要的。

猜你喜欢

转载自blog.csdn.net/zth_idea/article/details/120585412
今日推荐