com.mayabot.nlp.common.injector.Injector.kt Maven / Gradle / Ivy
package com.mayabot.nlp.common.injector
import org.jetbrains.annotations.Nullable
import java.util.concurrent.ConcurrentHashMap
// 单例标记注解
// 默认实现注解
// 不支持泛型
interface Injector {
fun getInstance(clazz: Class): T?
fun getInstance(clazz: Class, tag: String): T?
fun cache(): ConcurrentHashMap, Any>
companion object {
@JvmStatic
fun create(modules: List = emptyList()): Injector {
val mergeMap = ConcurrentHashMap()
modules.forEach {
it.configure()
val map = it.result()
map.forEach { (t, u) ->
mergeMap[t] = u
}
}
return InjectorImpl(mergeMap)
}
}
}
inline fun Injector.getInstance() = this.getInstance(T::class.java)
private fun makeKey(clazz: Class, tag: String): String {
return "${clazz.name}-$tag"
}
@Suppress("UNCHECKED_CAST")
class InjectorImpl(
private val bindMap: ConcurrentHashMap
) : Injector {
private val singletonCache = ConcurrentHashMap, Any>()
override fun cache() = singletonCache
override fun getInstance(
clazz: Class
): T? {
return this.getInstance(clazz, "")
}
//TODO 需要线程同步?
override fun getInstance(
clazz: Class,
tag: String
): T? {
val injector = this
val key = makeKey(clazz, tag)
var beanFactory = bindMap[key]
if (beanFactory != null) {
if (beanFactory == NULLBeanFactory) {
return null
}
if (beanFactory is InstanceBeanFactory) {
return beanFactory.obj as T
}
return beanFactory.create(this) as T
}
if (tag == "") {
beanFactory = makeAtomBeanFactory(clazz)
}
if (beanFactory == null) {
bindMap[key] = NULLBeanFactory
return null
} else {
bindMap[key] = beanFactory
}
return beanFactory.create(injector) as T
}
// 关键点在于自动产生
fun makeAtomBeanFactory(clazz: Class<*>): BeanFactory {
val ans = clazz.declaredAnnotations
val defaultImpl: Class<*>? = ans.filterIsInstance(ImplementedBy::class.java).map { it.value.java }.firstOrNull()
if (clazz.isInterface && defaultImpl == null) {
return NULLBeanFactory
}
val targetClass = if (clazz.isInterface) defaultImpl!! else clazz
val bf = TargetClassFactory(targetClass)
return bf
}
}
val NULLBeanFactory = InstanceBeanFactory(Unit)
class InstanceBeanFactory(val obj: Any) : BeanFactory {
override fun create(injector: Injector): Any {
return obj
}
}
/**
* 用反射自动创建一个Class的工厂
*/
class TargetClassFactory(val clazz: Class<*>) : BeanFactory {
val constructors = clazz.declaredConstructors
var singleton = clazz.declaredAnnotations.any { it is Singleton }
init {
if (clazz.isInterface) {
throw java.lang.RuntimeException("$clazz must not be interface")
}
if (constructors.size > 1) {
throw RuntimeException("${clazz} has more Constructors")
}
}
override fun create(injector: Injector): Any {
if (singleton) {
val singletonCache = injector.cache()
val x = singletonCache[clazz]
if (x == null) {
val c2 = create2(injector)
singletonCache[clazz] = c2
return c2
} else {
return x
}
}else{
return create2(injector)
}
}
private fun create2(injector: Injector): Any {
// class,如果存在唯一一个构造函数
// 如果构造函数里面的参数是Nullable的,那么autoBind=false
if (constructors.isEmpty()) {
return clazz.newInstance()!!
} else {
val constructor = constructors.first()
val count = constructor.parameterCount
val parameterTypes = constructor.parameterTypes
val parameterAnnotations = constructor.parameterAnnotations
if (count == 0) {
return constructor.newInstance()
}
val varpars = Array(count) { null }
for (i in 0 until count) {
val pc = parameterTypes[i]
val nullAble = parameterAnnotations[i].any { it is Nullable }
val obj = injector.getInstance(pc)
varpars[i] = obj
if (!nullAble && obj == null) {
throw RuntimeException("Init $clazz ,$pc is null")
}
}
return constructor.newInstance(*varpars)
}
}
}
interface Module {
fun configure()
fun result(): Map
}
fun createModule(block: AbstractModule.() -> Unit): Module {
return object : AbstractModule() {
override fun configure() {
this.block()
}
}
}
abstract class AbstractModule : Module {
private val bindMap = HashMap()
fun bind(clazz: Class): BindCallback {
return BindCallback(makeKey(clazz, ""))
}
fun bind(clazz: Class, tag: String): BindCallback {
return BindCallback(makeKey(clazz, tag))
}
override fun result() = bindMap
inner class BindCallback(
private val key: String) {
fun toInstance(obj: X) {
bindMap[key] = InstanceBeanFactory(obj as Any)
}
fun toClass(clazz: Class<*>) {
bindMap[key] = TargetClassFactory(clazz)
}
}
}