com.itangcent.common.spi.SpiUtils.kt Maven / Gradle / Ivy
package com.itangcent.common.spi
import com.itangcent.common.logger.ILogger
import com.itangcent.common.logger.ILoggerProvider
import com.itangcent.common.utils.notNullOrEmpty
import com.itangcent.common.utils.safeComputeIfAbsent
import java.lang.reflect.Proxy
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.concurrent.getOrSet
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
object SpiUtils {
private val serviceCache = ConcurrentHashMap()
private val loadStack = ThreadLocal>()
private val NON = Object()
fun setService(
service: KClass,
serviceBean: S
) {
serviceCache[service] = serviceBean
}
@Suppress("UNCHECKED_CAST")
fun loadService(
service: KClass
): S? {
return cache(service) {
loadService(service, SpiUtils::class.java.classLoader) ?: NON
}?.takeIf { it !== NON } as S?
}
@Suppress("UNCHECKED_CAST")
fun loadServices(
service: KClass
): List? {
return cache("list-" + service.qualifiedName!!) {
loadServices(service, SpiUtils::class.java.classLoader) ?: NON
}?.takeIf { it !== NON } as List?
}
fun loadService(
service: KClass,
loader: ClassLoader
): S? {
val serviceInstance = ServiceLoader.load(
service.java,
loader
).firstOrNull()
if (!service.isSubclassOf(ILoggerProvider::class)) {
LOG?.info("load service ${service.qualifiedName}:$serviceInstance")
}
return serviceInstance
}
fun loadServices(
service: KClass,
loader: ClassLoader
): List? {
val serviceInstance = ServiceLoader.load(
service.java,
loader
).toList()
if (!service.isSubclassOf(ILoggerProvider::class)) {
LOG?.info("load services ${service.qualifiedName}:$serviceInstance")
}
return serviceInstance.takeIf { it.notNullOrEmpty() }
}
private val serviceUltimateBeanCache = ConcurrentHashMap()
@Suppress("UNCHECKED_CAST")
fun loadUltimateBean(
service: KClass
): S? {
return serviceUltimateBeanCache.safeComputeIfAbsent(service) {
createProxy(service) ?: NON
}?.takeIf { it !== NON } as S?
}
@Suppress("UNCHECKED_CAST")
private fun createProxy(cls: KClass): S? {
val loadServices = loadServices(cls) ?: return null
return Proxy.newProxyInstance(
cls.java.classLoader, arrayOf(cls.java),
ProxyBean(loadServices.toTypedArray())
) as S
}
@Suppress("UNCHECKED_CAST")
private fun cache(key: Any, call: () -> T): T? {
serviceCache[key]?.let { return it as T }
val stack = loadStack.getOrSet { Stack() }
if (stack.contains(key)) {
return null
}
stack.push(key)
try {
return serviceCache.safeComputeIfAbsent(key) {
call()
} as T
} finally {
loadStack.get().pop()
}
}
}
//background idea log
private val LOG: ILogger? = SpiUtils.loadService(ILoggerProvider::class)?.getLogger(SpiUtils::class)