定义接口

package com.lightsword.biz

/**
* @author: Jack
* 2021/3/18 上午1:01
*/
interface IUserAbility {
fun getUser(biz:String): String
}

使用注解

package com.lightsword.biz

import com.lightsword.da.model.BizEnum
import com.lightsword.da.model.DomainAbility
import com.lightsword.da.model.DomainEnum

/**
* @author: Jack
* 2021/3/18 上午1:06
*/
@DomainAbility(domain = DomainEnum.USER, biz = BizEnum.BIZ_1)
class Biz1UserAbility : IUserAbility

实现接口

class Biz1UserAbility : IUserAbility {
override fun getUser(biz:String): String {
return "$biz user"
}
}

枚举类

​enum class BizEnum​

package com.lightsword.da.model


/**
* @author: Jack
* 2021/3/16 上午11:24
*/
enum class BizEnum {
/**
* 业务身份
*/
BIZ_1,
BIZ_2,
NULL,
;
}


package com.lightsword.da.model


/**
* @author: Jack
* 2021/3/16 上午11:24
*/
enum class DomainEnum {
/**
* 领域定义
*/
USER,
PRODUCT,
ORDER,
;

}

注解

​annotation class DomainAbility​

package com.lightsword.da.model

import org.springframework.stereotype.Component

/**
* @author: Jack
* 2021/3/16 上午11:18
*
* 1.Kotlin中的元注解类定义于kotlin.annotation包中,主要有: @Target、@Retention、@Repeatable、@MustBeDocumented 4种元注解
* 2.相比Java中5种元注解: @Target、@Retention、@Repeatable、@Documented、@Inherited少了 @Inherited元注解。
* 3.注解类中只能拥有如下类型的参数: 基本数据类型、字符串、枚举、类引用类型、其他的注解类(例如Deprecated注解类中的ReplaceWith注解类)
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Component
annotation class DomainAbility(
/**
* 能力领域
*/
val domain: DomainEnum,
/**
* 业务身份
*/
val biz: BizEnum
)

高阶函数与泛型

fun <Ext, R> execute(domain: DomainEnum, biz: BizEnum, clz: Class<Ext>, f: (Ext) -> R): R

package com.lightsword.da

import com.lightsword.da.model.BizEnum
import com.lightsword.da.model.DomainEnum
import java.util.concurrent.ConcurrentHashMap

/**
* @author: Jack
* 2021/3/16 下午5:02
*
* Spring Bean 初始化流程:
1、 Spring 先检查注解注入的bean,并将它们实例化
2、 然后spring初始化bean的顺序是按照xml中配置的顺序依次执行构造
3、 如果某个类实现了ApplicationContextAware接口,会在类初始化完成后调用setApplicationContext()方法:
4、 如果某个类实现了InitializingBean接口,会在类初始化完成后,并在setApplicationContext()方法执行完毕后,调用afterPropertiesSet()方法进行操作
*/
object DomainAbilityInvoker {
/**
* DomainAbility 对象实例的内存缓存
*/
private val DOMAIN_ABILITY_CACHE = ConcurrentHashMap<String, Any>()

fun <Ext, R> execute(domain: DomainEnum, biz: BizEnum, clz: Class<Ext>, f: (Ext) -> R): R {
val ext = find(domain, biz, clz)
return f(ext)
}

private fun <Ext> find(domain: DomainEnum, biz: BizEnum, clz: Class<Ext>): Ext {
val clzName = clz.name
val domainName = domain.name
val bizName = biz.name
val key = genKey(clz, bizName, domainName)
// 从缓存中获取
val cachedExtension = DOMAIN_ABILITY_CACHE[key] as Ext
if (null != cachedExtension) {
return cachedExtension
}
// 缓存中没找到, 那么去 ExtensionPool 里面重新加载一下, key = interfaceName
val domainAbilityExtensionList = ExtensionPool.getDomainAbilityExtension(clzName)
val domainAbilityExtension = domainAbilityExtensionList?.find {
it.domainEnum.name == domainName && it.biz.name == bizName
}
val extensionBean = domainAbilityExtension?.beanInstance

if (null != extensionBean) {
DOMAIN_ABILITY_CACHE[key] = extensionBean
}

return extensionBean as Ext
}

private fun <Ext> genKey(clz: Class<Ext>, bizName: String, domainName: String) =
clz.name + "|" + bizName + "|" + domainName
}

反射

​val extensionBeanMap = ctx.getBeansWithAnnotation(DomainAbility::class.java)​​​​val beanClazz = bean::class.java​​​​val domainAbilityAnno = beanClazz.getAnnotation(DomainAbility::class.java) ?: continue​

package com.lightsword.da

import com.lightsword.da.model.DomainAbility
import com.lightsword.da.model.DomainAbilityExtension
import org.springframework.beans.factory.InitializingBean
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.stereotype.Component
import java.util.concurrent.atomic.AtomicBoolean

/**
* @author: Jack
* 2021/3/16 下午5:02
*
* Spring Bean 初始化流程:
1、 Spring 先检查注解注入的bean,并将它们实例化
2、 然后spring初始化bean的顺序是按照xml中配置的顺序依次执行构造
3、 如果某个类实现了ApplicationContextAware接口,会在类初始化完成后调用setApplicationContext()方法:
4、 如果某个类实现了InitializingBean接口,会在类初始化完成后,并在setApplicationContext()方法执行完毕后,调用afterPropertiesSet()方法进行操作
*/
@Component
object DomainAbilityLoader : ApplicationContextAware, InitializingBean {

lateinit var ctx: ApplicationContext

var ATOMIC_BOOLEAN = AtomicBoolean()

/**
* 实现了ApplicationContextAware接口的类会被调用 setApplicationContext() 方法,从而获取到 Spring容器的上下文。
*/
override fun setApplicationContext(applicationContext: ApplicationContext) {
this.ctx = applicationContext
}

override fun afterPropertiesSet() {
init()
}

fun init() {
// 防止启动两次
if (ATOMIC_BOOLEAN.compareAndSet(false, true)) {
loadDomainAbility(this.ctx)
}
}

private fun loadDomainAbility(ctx: ApplicationContext) {
synchronized(this) {
try {
val domainAbilityExtensionMap = HashMap<String, MutableList<DomainAbilityExtension>>()
val extensionBeanMap = ctx.getBeansWithAnnotation(DomainAbility::class.java)
val extensionList = ArrayList(extensionBeanMap.values)

// 父 ApplicationContext 的处理逻辑
val parentCtx = ctx.parent
if (parentCtx != null) {
extensionList.addAll(parentCtx.getBeansWithAnnotation(DomainAbility::class.java).values)
}

if (extensionList.isEmpty()) {
return
}

for (bean in extensionList) {
val beanClazz = bean::class.java
val domainAbilityAnno = beanClazz.getAnnotation(DomainAbility::class.java) ?: continue

val domainAbilityExtension = DomainAbilityExtension()
domainAbilityExtension.biz = domainAbilityAnno.biz
domainAbilityExtension.beanInstance = bean
domainAbilityExtension.domainEnum = domainAbilityAnno.domain
/**
* 代码说明:
* var instances: MutableList<DomainAbilityProviderExtensionInstance>?
* instances = domainAbilityProviderExtensionInstanceMap[key]
* if (instances == null) {
* instances = mutableListOf()
* }
* instances.add(domainAbilityProviderExtensionInstance)
*/
// key = interfaceName
val key = getInterfaceName(beanClazz)
val instances = domainAbilityExtensionMap.computeIfAbsent(key, { k -> mutableListOf() })
instances.add(domainAbilityExtension)
}

// domainAbilityProviderExtensionInstanceMap values 根据优先级排序
domainAbilityExtensionMap.values.forEach { list ->
list.sortBy { it.priority }
}

// domainAbilityProviderExtensionInstance 放进 ExtensionInstancePool 中
ExtensionPool.putAllDomainAbilityExtension(domainAbilityExtensionMap)

} catch (e: Exception) {
throw e
}
}
}

/**
* 接口名称
*/
private fun getInterfaceName(beanClazz: Class<*>): String {
val beanInterface = if (beanClazz.interfaces.isEmpty()) {
beanClazz.superclass.interfaces[0]
} else {
beanClazz.interfaces[0]
}
return beanInterface.canonicalName
}

}

Kotlin与Java互操作

package com.lightsword

import com.lightsword.biz.IUserAbility
import com.lightsword.da.DomainAbilityInvoker
import com.lightsword.da.model.BizEnum
import com.lightsword.da.model.DomainEnum
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import javax.servlet.http.HttpServletRequest

@SpringBootApplication
class Application

fun main(args: Array<String>) {
runApplication<Application>(*args)
}

@RestController
class HelloController {

@GetMapping("/hello")
fun hello(@RequestParam(value = "biz") biz: String, httpRequest: HttpServletRequest): Any {

val bizEnum = when (biz) {
"BIZ_1" -> BizEnum.BIZ_1
"BIZ_2" -> BizEnum.BIZ_2
else -> BizEnum.NULL
}

// fun <Ext, R> execute(domain: DomainEnum, biz: BizEnum, clz: Class<Ext>, f: (Ext) -> R): R
return DomainAbilityInvoker.execute(
DomainEnum.USER,
bizEnum,
IUserAbility::class.java,
{ iUserAbility -> iUserAbility.getUser(biz) }
)

}

}

synchronized 同步锁

private fun loadDomainAbility(ctx: ApplicationContext) {
synchronized(this) {...}
}


/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/

@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("StandardKt")
package kotlin

import kotlin.contracts.*
import kotlin.jvm.internal.unsafe.*

/**
* Executes the given function [block] while holding the monitor of the given object [lock].
*/
@kotlin.internal.InlineOnly
public actual inline fun <R> synchronized(lock: Any, block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}

@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE", "INVISIBLE_MEMBER")
monitorEnter(lock)
try {
return block()
}
finally {
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE", "INVISIBLE_MEMBER")
monitorExit(lock)
}
}

Kotlin Contract 契约编程

public actual inline fun <R> synchronized(lock: Any, block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
...
}

关于:contract{ ... } Kotlin 的契约编程, ​