faster proxy

This commit is contained in:
tursom 2023-01-19 18:59:59 +08:00
parent acf16136a4
commit d170678182
37 changed files with 873 additions and 523 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

0
ts-core/ts-proxy/a Normal file
View File

View File

@ -11,6 +11,7 @@ dependencies {
api(project(":ts-core")) api(project(":ts-core"))
api(project(":ts-core:ts-reflectasm")) api(project(":ts-core:ts-reflectasm"))
api(group = "cglib", name = "cglib", version = "3.3.0") api(group = "cglib", name = "cglib", version = "3.3.0")
implementation(group = "net.bytebuddy", name = "byte-buddy", version = "1.12.22")
implementation(group = "org.apache.commons", name = "commons-lang3", version = "3.8.1") implementation(group = "org.apache.commons", name = "commons-lang3", version = "3.8.1")
testApi(group = "junit", name = "junit", version = "4.13.2") testApi(group = "junit", name = "junit", version = "4.13.2")
} }

View File

@ -1,32 +0,0 @@
package cn.tursom.proxy
import cn.tursom.core.context.Context
class ListProxyContainer(
private val proxyList: MutableCollection<ProxyMethod> = ArrayList(),
) : MutableProxyContainer {
override var lastModify: Long = System.currentTimeMillis()
private set
override val context: Context = ProxyContainer.contextEnv.newContext()
override fun addProxy(proxy: ProxyMethod) {
lastModify = System.currentTimeMillis()
proxyList.add(proxy)
}
override fun addAllProxy(proxy: Collection<ProxyMethod>?): Boolean {
lastModify = System.currentTimeMillis()
return proxyList.addAll(proxy!!)
}
override fun removeProxy(proxy: ProxyMethod) {
lastModify = System.currentTimeMillis()
proxyList.remove(proxy)
}
override fun iterator(): MutableIterator<ProxyMethod> {
lastModify = System.currentTimeMillis()
return proxyList.iterator()
}
}

View File

@ -1,7 +0,0 @@
package cn.tursom.proxy
interface MutableProxyContainer : ProxyContainer {
fun addProxy(proxy: ProxyMethod)
fun addAllProxy(proxy: Collection<ProxyMethod>?): Boolean
fun removeProxy(proxy: ProxyMethod)
}

View File

@ -1,24 +1,27 @@
package cn.tursom.proxy package cn.tursom.proxy
import cn.tursom.core.allMethodsSequence
import cn.tursom.core.isPrivate
import cn.tursom.core.uncheckedCast import cn.tursom.core.uncheckedCast
import net.sf.cglib.proxy.Enhancer import cn.tursom.proxy.container.ListProxyContainer
import net.sf.cglib.proxy.Factory import cn.tursom.proxy.container.MutableProxyContainer
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.function.ProxyMethod
import cn.tursom.proxy.interceptor.LocalCachedProxyInterceptor
import cn.tursom.proxy.interceptor.ProxyInterceptor
import cn.tursom.reflect.final
import net.sf.cglib.proxy.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
object Proxy { object Proxy {
object CallSuperException : Exception()
val callSuper = object : ThreadLocal<Boolean>() {
override fun initialValue(): Boolean = throw CallSuperException
override fun get(): Boolean = try {
super.get()
} catch (_: CallSuperException) {
false
}
}
var defaultContainer: () -> MutableProxyContainer = { ListProxyContainer() } var defaultContainer: () -> MutableProxyContainer = { ListProxyContainer() }
private val cache = ConcurrentHashMap<Class<*>, Class<*>>() private val cache = ConcurrentHashMap<Class<*>, Class<*>>()
val directAccessorKey = ProxyContainer.ctxEnv.newKey<Any>()
private val methodProxyFieldSignature = MethodProxy::class.java.getDeclaredField("sig1").also {
it.isAccessible = true
it.final = false
}
fun getContainer(obj: Any): ProxyContainer? { fun getContainer(obj: Any): ProxyContainer? {
if (obj !is Factory) return null if (obj !is Factory) return null
@ -38,18 +41,88 @@ object Proxy {
builder: (Class<T>) -> T, builder: (Class<T>) -> T,
): Pair<T, MutableProxyContainer> { ): Pair<T, MutableProxyContainer> {
val target = getCachedTarget(clazz) val target = getCachedTarget(clazz)
val directAccessor = builder(target)
val obj = builder(target) val obj = builder(target)
injectCallback(obj as Factory, container)
container.target = obj
container.ctx[directAccessorKey] = directAccessor
injectCallback(obj as Factory, container, directAccessor)
return obj to container return obj to container
} }
inline fun <reified T : Any> get() = get(T::class.java) inline fun <reified T : Any> get() = get(T::class.java)
inline fun <reified T : Any> get( inline operator fun <reified T : Any> get(
argumentTypes: Array<out Class<*>>, argumentTypes: Array<out Class<*>>,
arguments: Array<out Any?>, arguments: Array<out Any?>,
container: MutableProxyContainer = defaultContainer(), container: MutableProxyContainer = defaultContainer(),
) = get(T::class.java, argumentTypes, arguments, container) ) = get(T::class.java, argumentTypes, arguments, container)
inline operator fun <reified T : Any, reified A1 : Any?> get(
a1: A1,
container: MutableProxyContainer = defaultContainer(),
) = get<T>(arrayOf(A1::class.java), arrayOf(a1), container)
inline operator fun <
reified T : Any,
reified A1 : Any?,
reified A2 : Any?,
> get(
a1: A1, a2: A2,
container: MutableProxyContainer = defaultContainer(),
) = get<T>(
arrayOf(A1::class.java, A2::class.java),
arrayOf(a1, a2),
container,
)
inline operator fun <
reified T : Any,
reified A1 : Any?,
reified A2 : Any?,
reified A3 : Any?,
> get(
a1: A1, a2: A2, a3: A3,
container: MutableProxyContainer = defaultContainer(),
) = get<T>(
arrayOf(A1::class.java, A2::class.java, A3::class.java),
arrayOf(a1, a2, a3),
container,
)
inline operator fun <
reified T : Any,
reified A1 : Any?,
reified A2 : Any?,
reified A3 : Any?,
reified A4 : Any?,
> get(
a1: A1, a2: A2, a3: A3, a4: A4,
container: MutableProxyContainer = defaultContainer(),
) = get<T>(
arrayOf(A1::class.java, A2::class.java, A3::class.java, A4::class.java),
arrayOf(a1, a2, a3, a4),
container,
)
inline operator fun <
reified T : Any,
reified A1 : Any?,
reified A2 : Any?,
reified A3 : Any?,
reified A4 : Any?,
reified A5 : Any?,
> get(
a1: A1, a2: A2, a3: A3, a4: A4, a5: A5,
container: MutableProxyContainer = defaultContainer(),
) = get<T>(
arrayOf(A1::class.java, A2::class.java, A3::class.java, A4::class.java, A5::class.java),
arrayOf(a1, a2, a3, a4, a5),
container,
)
operator fun <T : Any> get(clazz: Class<T>, container: MutableProxyContainer = defaultContainer()) = operator fun <T : Any> get(clazz: Class<T>, container: MutableProxyContainer = defaultContainer()) =
get(clazz, container, Class<T>::newInstance) get(clazz, container, Class<T>::newInstance)
@ -68,23 +141,56 @@ object Proxy {
if (interfaces.isNotEmpty()) { if (interfaces.isNotEmpty()) {
enhancer.setInterfaces(interfaces) enhancer.setInterfaces(interfaces)
} }
enhancer.setCallbackType(ProxyInterceptor::class.java)
enhancer.setCallbackFilter { 0 } val methods = clazz.allMethodsSequence.filter { !it.isPrivate() }.toList()
enhancer.setCallbackTypes(Array(methods.size) { MethodInterceptor::class.java })
enhancer.setCallbackFilter(methods::indexOf)
return enhancer return enhancer
} }
@JvmOverloads @JvmOverloads
fun injectCallback(obj: Any, container: ProxyContainer = defaultContainer()): ProxyContainer { fun injectCallback(obj: Any, container: ProxyContainer = defaultContainer(), target: Any = obj): ProxyContainer {
obj as Factory obj as Factory
if (obj.getCallback(0) != null && obj.getCallback(0) != ProxyDispatcher) { if (obj.getCallback(0) != null && obj.getCallback(0) is ProxyInterceptor) {
return (obj.getCallback(0) as ProxyInterceptor).container return (obj.getCallback(0) as ProxyInterceptor).container
} }
obj.setCallback(0, ProxyInterceptor(container)) val nonProxyClasses: MutableSet<Class<*>> = HashSet(listOf(Any::class.java))
repeat(obj.callbacks.size) {
obj.setCallback(it, LocalCachedProxyInterceptor(container, nonProxyClasses, target))
}
return container return container
} }
fun <T : Any> getCachedTarget(clazz: Class<T>): Class<T> = cache.computeIfAbsent(clazz) { fun <T : Any> getCachedTarget(clazz: Class<T>): Class<T> = cache.computeIfAbsent(clazz) {
newEnhancer(clazz).createClass() newEnhancer(clazz).createClass()
}.uncheckedCast() }.uncheckedCast()
fun <T : Any> getSuperCaller(
obj: T,
): T = getContainer(obj)?.ctx?.get(directAccessorKey).uncheckedCast()
fun addNonProxyClass(target: Any, nonProxyClass: Class<*>): Boolean {
if (target !is Factory ||
target.getCallback(0) == null ||
target.getCallback(0) !is ProxyInterceptor
) {
throw IllegalArgumentException()
}
return (target.getCallback(0) as ProxyInterceptor).nonProxyClasses.add(nonProxyClass)
}
fun removeNonProxyClass(target: Any, nonProxyClass: Class<*>): Boolean {
if (target !is Factory ||
target.getCallback(0) == null ||
target.getCallback(0) !is ProxyInterceptor
) {
throw IllegalArgumentException()
}
return (target.getCallback(0) as ProxyInterceptor).nonProxyClasses.remove(nonProxyClass)
}
} }

View File

@ -1,30 +0,0 @@
package cn.tursom.proxy
import cn.tursom.core.context.ArrayContextEnv
import cn.tursom.core.context.Context
interface ProxyContainer : Iterable<ProxyMethod> {
companion object {
val contextEnv = ArrayContextEnv()
inline fun ProxyContainer.forEachProxy(action: (ProxyMethod) -> Unit) {
for (t in this) {
action(t)
}
}
inline fun <R> ProxyContainer.forFirstProxy(action: (ProxyMethod) -> ProxyResult<R>): ProxyResult<R> {
for (t in this) {
val result = action(t)
if (result.success) {
return result
}
}
return ProxyResult.failed()
}
}
// to impl, use override val context: Context = ProxyContainer.contextEnv.newContext()
val context: Context
val lastModify: Long
}

View File

@ -1,12 +0,0 @@
package cn.tursom.proxy
import net.sf.cglib.proxy.MethodInterceptor
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
object ProxyDispatcher : MethodInterceptor {
override fun intercept(obj: Any, method: Method, args: Array<out Any>, proxy: MethodProxy): Any {
Proxy.injectCallback(obj)
return proxy(obj, args)
}
}

View File

@ -1,25 +0,0 @@
package cn.tursom.proxy
import net.sf.cglib.proxy.MethodInterceptor
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
class ProxyInterceptor(
val container: ProxyContainer = ListProxyContainer(),
// disable call super can save 20% time
private val supportCallSuper: Boolean = true,
) : MethodInterceptor {
override fun intercept(obj: Any, method: Method, args: Array<out Any?>, proxy: MethodProxy): Any? {
if (supportCallSuper && Proxy.callSuper.get()) {
Proxy.callSuper.remove()
return proxy.invokeSuper(obj, args)
}
val result = ProxyRunner.onProxy(obj, container, method, args, proxy)
return if (result.success) {
result.result
} else {
proxy.invokeSuper(obj, args)
}
}
}

View File

@ -1,150 +0,0 @@
package cn.tursom.proxy
import cn.tursom.proxy.annotation.ForEachProxy
import cn.tursom.proxy.annotation.ForFirstProxy
import cn.tursom.reflect.asm.ReflectAsmUtils
import com.esotericsoftware.reflectasm.MethodAccess
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
import java.util.concurrent.ConcurrentHashMap
internal typealias ProxyMethodHandler = (proxy: ProxyMethod, args: Array<out Any?>) -> ProxyResult<*>
interface ProxyMethod {
@Throws(Throwable::class)
fun onProxy(
ignored: Any,
c: ProxyContainer,
method: Method,
args: Array<out Any?>,
proxy: MethodProxy
): ProxyResult<*> {
val handlerCacheMap = getHandlerCacheMap(javaClass)
val handlerCache = handlerCacheMap[method]
if (handlerCache != null) {
return if (handlerCache.success) {
handlerCache.result(this, args)
} else {
ProxyResult.failed<Any>()
}
}
val reflectAsmHandler = ReflectASMProxyMethodInvoker[this, method, handlerCacheMap]
if (reflectAsmHandler != null) {
return reflectAsmHandler(this, args)
}
val javaReflectHandler = JavaReflectProxyMethodInvoker[this, method, handlerCacheMap]
if (javaReflectHandler != null) {
javaReflectHandler(this, args)
}
handlerCacheMap[method] = ProxyResult.failed()
return ProxyResult.failed<Any>()
}
companion object {
private val handlerCacheMapMap: MutableMap<
Class<out ProxyMethod>,
MutableMap<Method, ProxyResult<ProxyMethodHandler>>
> = HashMap()
fun getHandlerCacheMap(type: Class<out ProxyMethod>): MutableMap<Method, ProxyResult<ProxyMethodHandler>> {
var handlerCacheMap = handlerCacheMapMap[type]
if (handlerCacheMap == null) synchronized(handlerCacheMapMap) {
handlerCacheMap = handlerCacheMapMap[type]
if (handlerCacheMap == null) {
handlerCacheMap = ConcurrentHashMap()
handlerCacheMapMap[type] = handlerCacheMap!!
}
}
return handlerCacheMap!!
}
}
}
private class ReflectASMProxyMethodInvoker(
val methodAccess: MethodAccess,
val index: Int,
) : ProxyMethodHandler {
companion object {
private val fastInvoker: MethodAccess.(Any, Int, Array<out Any?>) -> Any? = MethodAccess::invoke
operator fun get(
proxy: ProxyMethod,
method: Method,
handlerCacheMap: MutableMap<Method, ProxyResult<ProxyMethodHandler>>
): ProxyMethodHandler? {
val reflectAsmMethod = try {
ReflectAsmUtils.getMethod(
proxy.javaClass,
method.name,
paramTypes = method.parameterTypes,
returnType = method.returnType,
)
} catch (e: Exception) {
e.printStackTrace()
null
}
if (reflectAsmMethod != null) {
val (methodAccess, index) = reflectAsmMethod
val invoker = ReflectASMProxyMethodInvoker(methodAccess, index)
handlerCacheMap[method] = ProxyResult.of(invoker)
return invoker
}
return null
}
}
override fun invoke(proxy: ProxyMethod, args: Array<out Any?>): ProxyResult<*> {
return ProxyResult.of(fastInvoker(methodAccess, proxy, index, args))
}
}
private class JavaReflectProxyMethodInvoker(
val method: Method,
) : ProxyMethodHandler {
companion object {
private val fastInvoker: Method.(Any, Array<out Any?>) -> Any? = Method::invoke
operator fun get(
proxy: ProxyMethod,
method: Method,
handlerCacheMap: MutableMap<Method, ProxyResult<ProxyMethodHandler>>
): ProxyMethodHandler? {
var invoker: ProxyMethodHandler? = null
val selfMethod: Method
try {
var methodName = method.name
for (annotation in method.annotations) {
if (annotation is ForEachProxy) {
if (annotation.name.isNotEmpty()) {
methodName = annotation.name
break
}
} else if (annotation is ForFirstProxy) {
if (annotation.name.isNotEmpty()) {
methodName = annotation.name
break
}
}
}
selfMethod = proxy.javaClass.getMethod(methodName, *method.parameterTypes)
selfMethod.isAccessible = true
invoker = JavaReflectProxyMethodInvoker(method)
handlerCacheMap[method] = ProxyResult(invoker, true)
} catch (_: Exception) {
}
return invoker
}
}
override fun invoke(proxy: ProxyMethod, args: Array<out Any?>): ProxyResult<*> {
return ProxyResult.of(fastInvoker(method, proxy, args))
}
}

View File

@ -1,45 +0,0 @@
package cn.tursom.proxy
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
import java.util.concurrent.ConcurrentHashMap
typealias ProxyMethodCacheFunction = (
obj: Any,
c: ProxyContainer,
method: Method,
args: Array<out Any?>,
proxy: MethodProxy
) -> ProxyResult<*>
class ProxyMethodCache(
private var lastModify: Long = 0,
) {
companion object {
val failed: ProxyMethodCacheFunction = { _, _, _, _, _ -> ProxyResult.failed }
}
private val functionMap = ConcurrentHashMap<MethodProxy, ProxyMethodCacheFunction>()
fun update(lastModify: Long, proxy: MethodProxy, function: ProxyMethodCacheFunction = functionMap[proxy] ?: failed) {
if (this.lastModify != lastModify) {
functionMap.clear()
}
this.lastModify = lastModify
functionMap[proxy] = function
}
operator fun invoke(
lastModify: Long,
obj: Any,
c: ProxyContainer,
method: Method,
args: Array<out Any?>,
proxy: MethodProxy,
): ProxyResult<*>? = if (lastModify != this.lastModify) {
null
} else {
functionMap[proxy]?.invoke(obj, c, method, args, proxy)
}
}

View File

@ -1,22 +0,0 @@
package cn.tursom.proxy
import cn.tursom.core.uncheckedCast
data class ProxyResult<out R>(
val result: R,
val success: Boolean = false,
) {
companion object {
val success: ProxyResult<*> = ProxyResult<Any?>(null, true)
val failed: ProxyResult<*> = ProxyResult<Any?>(null, false)
fun <R> of(): ProxyResult<R?> = success.uncheckedCast()
fun <R> of(result: R): ProxyResult<R> = if (result == null) {
success.uncheckedCast()
} else {
ProxyResult(result, true)
}
fun <R> failed(): ProxyResult<R> = failed.uncheckedCast()
}
}

View File

@ -1,148 +0,0 @@
package cn.tursom.proxy
import cn.tursom.proxy.ProxyContainer.Companion.forEachProxy
import cn.tursom.proxy.ProxyContainer.Companion.forFirstProxy
import cn.tursom.proxy.annotation.ForEachProxy
import cn.tursom.proxy.annotation.ForFirstProxy
import net.sf.cglib.proxy.MethodProxy
import org.apache.commons.lang3.StringUtils
import java.lang.reflect.Method
import java.util.*
object ProxyRunner {
private val errMsgSearchList = arrayOf("%M", "%B", "%A")
private val proxyMethodCacheKey = ProxyContainer.contextEnv.newKey<ProxyMethodCache>()
.withDefault { ProxyMethodCache() }
/**
* will be call when proxy method invoke.
* 在代理方法被调用时该方法会被调用
*/
@Throws(Throwable::class)
fun onProxy(obj: Any, c: ProxyContainer, method: Method, args: Array<out Any?>, proxy: MethodProxy): ProxyResult<*> {
val cache = c.context[proxyMethodCacheKey]
cache(c.lastModify, obj, c, method, args, proxy)?.let {
return it
}
var handler: ProxyMethodCacheFunction? = null
for (annotation in method.annotations) when (annotation) {
is ForEachProxy -> {
handler = onForeachProxy(annotation)
break
}
is ForFirstProxy -> {
handler = onForFirstProxy(annotation)
break
}
}
if (handler == null) {
//handler = ProxyContainerHandlerCache.callSuper
handler = onForFirstProxy(ForFirstProxy.EMPTY)
}
return handler(obj, c, method, args, proxy)
}
fun onForFirstProxy(forFirstProxy: ForFirstProxy): ProxyMethodCacheFunction {
val classes = when (forFirstProxy.value.size) {
0 -> emptyList()
1 -> listOf(forFirstProxy.value[0].java)
else -> forFirstProxy.value.asSequence().map { it.java }.toSet()
}
return { o, c, m: Method, a, p ->
onForFirstProxy(o, c, m, a, p, forFirstProxy, classes)
}
}
private fun onForFirstProxy(
obj: Any,
container: ProxyContainer,
method: Method,
args: Array<out Any?>,
proxy: MethodProxy,
forFirstProxy: ForFirstProxy,
classes: Collection<Class<*>>,
): ProxyResult<*> {
val result = container.forFirstProxy { p ->
if (classes.isEmpty() || classes.stream().anyMatch { c: Class<*> -> c.isInstance(p) }) {
val result = p.onProxy(obj, container, method, args, proxy)
if (forFirstProxy.cache && result.success) {
container.context[proxyMethodCacheKey].update(container.lastModify, proxy, p::onProxy)
}
return@forFirstProxy result
} else {
return@forFirstProxy ProxyResult.failed
}
}
if (result.success) {
return result
}
// when request not handled
if (forFirstProxy.must) {
// generate error message
var errMsg: String = forFirstProxy.errMsg
if (errMsg.isBlank()) {
errMsg = "no proxy handled on method %M"
}
val replacementList = arrayOfNulls<String>(errMsgSearchList.size)
// todo use efficient contains
if (errMsg.contains(errMsgSearchList[0])) {
replacementList[0] = method.toString()
}
if (errMsg.contains(errMsgSearchList[1])) {
replacementList[1] = obj.toString()
}
if (errMsg.contains(errMsgSearchList[2])) {
replacementList[2] = Arrays.toString(args)
}
errMsg = StringUtils.replaceEach(errMsg, errMsgSearchList, replacementList)
val exceptionConstructor = forFirstProxy.errClass.java.getConstructor(String::class.java)
if (forFirstProxy.cache) {
container.context[proxyMethodCacheKey].update(container.lastModify, proxy) { _, _, _, _, _ ->
throw exceptionConstructor.newInstance(errMsg)
}
}
throw exceptionConstructor.newInstance(errMsg)
}
if (forFirstProxy.cache) {
container.context[proxyMethodCacheKey].update(container.lastModify, proxy)
}
return ProxyResult.failed
}
fun onForeachProxy(forEachProxy: ForEachProxy) = onForeachProxy(when (forEachProxy.value.size) {
0 -> emptyList()
1 -> listOf(forEachProxy.value[0].java)
else -> forEachProxy.value.asSequence().map { it.java }.toSet()
}, forEachProxy.cache)
private val emptyProxyList = ArrayList<ProxyMethod>()
private fun onForeachProxy(
classes: Collection<Class<*>>,
cache: Boolean,
): ProxyMethodCacheFunction = { o, c, m, a, proxy ->
val proxyList = if (cache) ArrayList<ProxyMethod>() else emptyProxyList
c.forEachProxy { p ->
if (classes.isEmpty() || classes.any { c: Class<*> -> c.isInstance(p) }) {
val result = p.onProxy(o, c, m, a, proxy)
if (cache && result.success) {
proxyList.add(p)
}
}
}
if (cache) {
c.context[proxyMethodCacheKey].update(c.lastModify, proxy, onCachedForeachProxy((proxyList)))
}
ProxyResult.failed<Any?>()
}
private fun onCachedForeachProxy(proxyList: List<ProxyMethod>): ProxyMethodCacheFunction = { o, c, m, a, proxy ->
proxyList.forEach { p ->
p.onProxy(o, c, m, a, proxy)
}
ProxyResult.failed<Any?>()
}
}

View File

@ -13,7 +13,7 @@ annotation class ForFirstProxy(
*/ */
val must: Boolean = false, val must: Boolean = false,
val errMsg: String = "", val errMsg: String = "",
val errClass: KClass<out RuntimeException> = RuntimeException::class, val errClass: KClass<out Throwable> = Throwable::class,
val cache: Boolean = true, val cache: Boolean = true,
) { ) {
companion object { companion object {

View File

@ -0,0 +1,54 @@
package cn.tursom.proxy.container
import cn.tursom.core.context.Context
import cn.tursom.proxy.function.ProxyMethod
import cn.tursom.proxy.util.CglibUtil
import net.sf.cglib.proxy.MethodProxy
class ListProxyContainer(
private val proxyList: MutableCollection<Any> = ArrayList(),
) : MutableProxyContainer {
override lateinit var target: Any
override val ctx: Context = ProxyContainer.ctxEnv.newContext()
private fun clearCache() {
ctx[ProxyMethodCache.ctxKey].clear()
}
override fun addProxy(proxy: Any) {
clearCache()
proxyList.add(proxy)
if (proxy is ProxyMethod) {
proxy.onProxyAdded(this)
}
clearCache()
}
override fun addAllProxy(proxy: Collection<Any>?) {
clearCache()
if (proxyList.addAll(proxy!!)) proxy.forEach {
if (it !is ProxyMethod) return@forEach
it.onProxyAdded(this)
}
clearCache()
}
override fun removeProxy(proxy: Any) {
clearCache()
if (proxyList.remove(proxy) && proxy is ProxyMethod) {
proxy.onProxyRemoved(this)
}
clearCache()
}
override fun iterator(): Iterator<Any> {
return proxyList.iterator()
}
}

View File

@ -0,0 +1,9 @@
package cn.tursom.proxy.container
interface MutableProxyContainer : ProxyContainer {
override var target: Any
fun addProxy(proxy: Any)
fun addAllProxy(proxy: Collection<Any>?)
fun removeProxy(proxy: Any)
}

View File

@ -0,0 +1,14 @@
package cn.tursom.proxy.container
import cn.tursom.core.context.ArrayContextEnv
import cn.tursom.core.context.Context
interface ProxyContainer : Iterable<Any> {
companion object {
val ctxEnv = ArrayContextEnv()
}
// to impl, use override val context: Context = ProxyContainer.contextEnv.newContext()
val ctx: Context
val target: Any
}

View File

@ -0,0 +1,28 @@
package cn.tursom.proxy.container
import cn.tursom.proxy.util.IntMap
import net.sf.cglib.proxy.MethodProxy
class ProxyMethodCache {
companion object {
val ctxKey = ProxyContainer.ctxEnv.newKey<ProxyMethodCache>()
.withDefault { ProxyMethodCache() }
}
//private val functionMap = HashMap<Int, ProxyMethodCacheFunction>()
private val functionMap = IntMap<ProxyMethodCacheFunction>()
fun clear() {
synchronized(functionMap) {
functionMap.clear()
}
}
fun update(proxy: MethodProxy, function: ProxyMethodCacheFunction) {
synchronized(this) {
functionMap[proxy.superIndex] = function
}
}
operator fun get(proxy: MethodProxy) = functionMap[proxy.superIndex]
}

View File

@ -0,0 +1,13 @@
package cn.tursom.proxy.container
import cn.tursom.proxy.container.ProxyContainer
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
fun interface ProxyMethodCacheFunction : (
Any?,
ProxyContainer,
Method?,
Array<out Any?>?,
MethodProxy?,
) -> Any?

View File

@ -0,0 +1,25 @@
package cn.tursom.proxy.function
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
class CachedOnForEachProxyImpl(
private val proxyList: List<ProxyMethodCacheFunction>,
) : ProxyMethodCacheFunction {
override fun invoke(
o: Any?,
c: ProxyContainer,
m: Method?,
a: Array<out Any?>?,
proxy: MethodProxy?,
): Any? {
proxy!!
proxyList.forEach { p ->
p(o, c, m, a, proxy)
}
return proxy.invokeSuper(o, a)
}
}

View File

@ -0,0 +1,18 @@
package cn.tursom.proxy.function
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
object CallSuperProxyMethodCacheFunction : ProxyMethodCacheFunction {
override fun invoke(
obj: Any?,
c: ProxyContainer,
method: Method?,
args: Array<out Any?>?,
proxy: MethodProxy?,
): Any? {
return proxy!!.invokeSuper(obj, args)
}
}

View File

@ -0,0 +1,61 @@
package cn.tursom.proxy.function
import cn.tursom.proxy.annotation.ForEachProxy
import cn.tursom.proxy.annotation.ForFirstProxy
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
internal class JavaReflectProxyMethodInvoker(
private val self: Any,
private val method: Method,
) : ProxyMethodCacheFunction {
companion object {
private val fastInvoker: Method.(Any?, Array<out Any?>?) -> Any? = Method::invoke
operator fun get(
proxy: Any,
method: Method,
): JavaReflectProxyMethodInvoker? {
var invoker: JavaReflectProxyMethodInvoker? = null
val selfMethod: Method
try {
var methodName = method.name
for (annotation in method.annotations) {
if (annotation is ForEachProxy) {
if (annotation.name.isNotEmpty()) {
methodName = annotation.name
break
}
} else if (annotation is ForFirstProxy) {
if (annotation.name.isNotEmpty()) {
methodName = annotation.name
break
}
}
}
selfMethod = proxy.javaClass.getMethod(methodName, *method.parameterTypes)
selfMethod.isAccessible = true
invoker = JavaReflectProxyMethodInvoker(proxy, method)
//handlerCacheMap[method] = ProxyResult(invoker, true)
} catch (_: Exception) {
}
return invoker
}
}
override fun invoke(
obj: Any?,
c: ProxyContainer,
method: Method?,
args: Array<out Any?>?,
proxy: MethodProxy?,
): Any? {
return fastInvoker(this.method, self, args)
}
}

View File

@ -0,0 +1,52 @@
package cn.tursom.proxy.function
import cn.tursom.proxy.annotation.ForEachProxy
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCache
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
class OnForEachProxyImpl(
forEachProxy: ForEachProxy,
) : ProxyMethodCacheFunction {
companion object {
private val emptyProxyList = ArrayList<ProxyMethodCacheFunction>()
}
private val classes: Collection<Class<*>> = when (forEachProxy.value.size) {
0 -> emptyList()
1 -> listOf(forEachProxy.value[0].java)
else -> forEachProxy.value.asSequence().map { it.java }.toSet()
}
private val cache: Boolean = forEachProxy.cache
override fun invoke(
o: Any?,
c: ProxyContainer,
m: Method?,
a: Array<out Any?>?,
proxy: MethodProxy?,
): Any? {
m!!
proxy!!
val proxyList = if (cache) ArrayList() else emptyProxyList
c.forEach { p ->
if (classes.isEmpty() || classes.any { c: Class<*> -> c.isInstance(p) }) {
val handler = ProxyMethod.getHandler(p, m) ?: return@forEach
handler(o, c, m, a, proxy)
if (cache) {
proxyList.add(handler)
}
}
}
if (cache) {
c.ctx[ProxyMethodCache.ctxKey].update(
proxy, CachedOnForEachProxyImpl(proxyList)
)
}
return proxy.invokeSuper(o, a)
}
}

View File

@ -0,0 +1,97 @@
package cn.tursom.proxy.function
import cn.tursom.proxy.annotation.ForFirstProxy
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCache
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import net.sf.cglib.proxy.MethodProxy
import org.apache.commons.lang3.StringUtils
import java.lang.reflect.Constructor
import java.lang.reflect.Method
import java.util.*
class OnForFirstProxyImpl(
private val ffpAnnotation: ForFirstProxy,
) : ProxyMethodCacheFunction {
companion object {
private val errMsgSearchList = arrayOf("%M", "%B", "%A")
}
private val classes: Collection<Class<*>> = when (ffpAnnotation.value.size) {
0 -> emptyList()
1 -> listOf(ffpAnnotation.value[0].java)
else -> ffpAnnotation.value.asSequence().map { it.java }.toSet()
}
override fun invoke(
obj: Any?,
container: ProxyContainer,
method: Method?,
args: Array<out Any?>?,
proxy: MethodProxy?,
): Any? {
method!!
proxy!!
container.forEach { p ->
if (classes.isNotEmpty() && classes.none { c: Class<*> -> c.isInstance(p) }) {
return@forEach
}
val handler = ProxyMethod.getHandler(p, method) ?: return@forEach
if (ffpAnnotation.cache) {
container.ctx[ProxyMethodCache.ctxKey].update(proxy, handler)
}
return handler(obj, container, method, args, proxy)
}
// when request not handled
if (ffpAnnotation.must) {
// generate error message
var errMsg: String = ffpAnnotation.errMsg
if (errMsg.isBlank()) {
errMsg = "no proxy handled on method %M"
}
val replacementList = arrayOfNulls<String>(errMsgSearchList.size)
// todo use efficient contains
if (errMsg.contains(errMsgSearchList[0])) {
replacementList[0] = method.toString()
}
if (errMsg.contains(errMsgSearchList[1])) {
replacementList[1] = obj.toString()
}
if (errMsg.contains(errMsgSearchList[2])) {
replacementList[2] = Arrays.toString(args)
}
errMsg = StringUtils.replaceEach(errMsg, errMsgSearchList, replacementList)
val exceptionConstructor = ffpAnnotation.errClass.java.getConstructor(String::class.java)
if (ffpAnnotation.cache) {
container.ctx[ProxyMethodCache.ctxKey].update(
proxy, ExceptionProxyMethodCacheFunctionImpl(exceptionConstructor, errMsg)
)
}
throw exceptionConstructor.newInstance(errMsg)
}
if (ffpAnnotation.cache) {
container.ctx[ProxyMethodCache.ctxKey].update(
proxy,
CallSuperProxyMethodCacheFunction,
)
}
return proxy.invokeSuper(obj, args)
}
}
private class ExceptionProxyMethodCacheFunctionImpl(
private val exceptionConstructor: Constructor<out Throwable>,
private val errMsg: String,
) : ProxyMethodCacheFunction {
override fun invoke(
obj: Any?,
c: ProxyContainer,
method: Method?,
args: Array<out Any?>?,
proxy: MethodProxy?,
) = throw exceptionConstructor.newInstance(errMsg)
}

View File

@ -0,0 +1,53 @@
package cn.tursom.proxy.function
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
interface ProxyMethod {
fun onProxyAdded(container: ProxyContainer) {
// NO-OP
}
fun onProxyRemoved(container: ProxyContainer) {
// NO-OP
}
fun onProxyInvoke(
o: Any?,
c: ProxyContainer,
m: Method?,
a: Array<out Any?>?,
proxy: MethodProxy?,
next: ProxyMethodCacheFunction,
): Any? {
return next(o, c, m, a, proxy)
}
companion object {
fun getHandler(proxy: Any, method: Method): ProxyMethodCacheFunction? {
var handler = getReflectHandler(proxy, method) ?: return null
if (proxy is ProxyMethod) {
handler = ProxyMethodInvoker(proxy, handler)
}
return handler
}
private fun getReflectHandler(proxy: Any, method: Method): ProxyMethodCacheFunction? {
val reflectAsmHandler = ReflectASMProxyMethodInvoker[proxy, method]
if (reflectAsmHandler != null) {
return reflectAsmHandler
}
val javaReflectHandler = JavaReflectProxyMethodInvoker[proxy, method]
if (javaReflectHandler != null) {
return javaReflectHandler
}
return null
}
}
}

View File

@ -0,0 +1,21 @@
package cn.tursom.proxy.function
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
internal class ProxyMethodInvoker(
private val proxyMethod: ProxyMethod,
private val next: ProxyMethodCacheFunction,
) : ProxyMethodCacheFunction {
override fun invoke(
obj: Any?,
c: ProxyContainer,
method: Method?,
args: Array<out Any?>?,
proxy: MethodProxy?,
): Any? {
return proxyMethod.onProxyInvoke(obj, c, method, args, proxy, next)
}
}

View File

@ -0,0 +1,48 @@
package cn.tursom.proxy.function
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import cn.tursom.reflect.asm.ReflectAsmUtils
import com.esotericsoftware.reflectasm.MethodAccess
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
internal class ReflectASMProxyMethodInvoker(
private val self: Any,
private val methodAccess: MethodAccess,
private val index: Int,
) : ProxyMethodCacheFunction {
companion object {
private val fastInvoker: MethodAccess.(Any, Int, Array<out Any?>?) -> Any? = MethodAccess::invoke
operator fun get(
proxy: Any,
method: Method,
): ReflectASMProxyMethodInvoker? {
val reflectAsmMethod = try {
ReflectAsmUtils.getMethod(
proxy.javaClass,
method.name,
paramTypes = method.parameterTypes,
returnType = method.returnType,
)
} catch (e: Exception) {
e.printStackTrace()
null
} ?: return null
val (methodAccess, index) = reflectAsmMethod
return ReflectASMProxyMethodInvoker(proxy, methodAccess, index)
}
}
override fun invoke(
obj: Any?,
c: ProxyContainer,
method: Method?,
args: Array<out Any?>?,
proxy: MethodProxy?,
): Any? {
return fastInvoker(methodAccess, self, index, args)
}
}

View File

@ -0,0 +1,42 @@
package cn.tursom.proxy.interceptor
import cn.tursom.reflect.asm.ReflectAsmUtils
import com.esotericsoftware.reflectasm.MethodAccess
import net.sf.cglib.proxy.InvocationHandler
import java.lang.reflect.Method
class LocalCachedNoProxyInvocationHandler(
val proxy: Any,
) : InvocationHandler {
companion object {
private val fastInvoker: MethodAccess.(Any, Int, Array<out Any?>?) -> Any? = MethodAccess::invoke
}
private var handler: (method: Method?, args: Array<out Any>?) -> Any? = DefaultHandler()
override fun invoke(ignore: Any, method: Method?, args: Array<out Any>?): Any? {
return handler(method, args)
}
private inner class DefaultHandler : (Method?, Array<out Any>?) -> Any? {
override fun invoke(method: Method?, args: Array<out Any>?): Any? {
method!!
val (methodAccess, index) = ReflectAsmUtils.getMethodByRegex(
proxy.javaClass,
"CGLIB\\\$${method.name}\\\$.*".toRegex(),
*method.parameterTypes,
method.returnType,
)!!
handler = MethodAccessHandler(methodAccess, index)
return fastInvoker(methodAccess, proxy, index, args)
}
}
private inner class MethodAccessHandler(
private val methodAccess: MethodAccess,
private val index: Int,
) : (Method?, Array<out Any>?) -> Any? {
override fun invoke(method: Method?, args: Array<out Any>?) = fastInvoker(methodAccess, proxy, index, args)
}
}

View File

@ -0,0 +1,33 @@
package cn.tursom.proxy.interceptor
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCache
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
class LocalCachedProxyInterceptor(
container: ProxyContainer,
nonProxyClasses: MutableSet<Class<*>>,
val target: Any,
) : ProxyInterceptor(container, nonProxyClasses) {
private var cache: ProxyMethodCacheFunction? = null
override fun intercept(obj: Any?, method: Method?, args: Array<out Any?>?, proxy: MethodProxy): Any? {
var cache = this.cache
if (cache != null) {
return cache(target, container, method, args, proxy)
}
val methodCache = container.ctx[ProxyMethodCache.ctxKey]
this.cache = methodCache[proxy]
cache = this.cache
if (cache != null) {
return cache(target, container, method, args, proxy)
}
return super.intercept(target, method, args, proxy)
}
}

View File

@ -0,0 +1,13 @@
package cn.tursom.proxy.interceptor
import net.sf.cglib.proxy.MethodInterceptor
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
class NoProxyProxyInterceptor(
private val target: Any,
) : MethodInterceptor {
override fun intercept(obj: Any?, method: Method?, args: Array<out Any>?, proxy: MethodProxy): Any? {
return proxy.invokeSuper(target, args)
}
}

View File

@ -0,0 +1,59 @@
package cn.tursom.proxy.interceptor
import cn.tursom.proxy.annotation.ForEachProxy
import cn.tursom.proxy.annotation.ForFirstProxy
import cn.tursom.proxy.container.ListProxyContainer
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCache
import cn.tursom.proxy.function.CallSuperProxyMethodCacheFunction
import cn.tursom.proxy.function.OnForEachProxyImpl
import cn.tursom.proxy.function.OnForFirstProxyImpl
import net.sf.cglib.proxy.MethodInterceptor
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
open class ProxyInterceptor(
val container: ProxyContainer = ListProxyContainer(),
val nonProxyClasses: MutableSet<Class<*>> = HashSet(listOf(Any::class.java)),
) : MethodInterceptor {
override fun intercept(obj: Any?, method: Method?, args: Array<out Any?>?, proxy: MethodProxy): Any? {
val cache = container.ctx[ProxyMethodCache.ctxKey]
var handler = cache[proxy]
if (handler != null) {
return handler(obj, container, method, args, proxy)
}
method!!
nonProxyClasses.forEach { nonProxyClass ->
nonProxyClass.declaredMethods.forEach {
if (it.name == method.name &&
it.returnType.isAssignableFrom(method.returnType) &&
it.parameterTypes.contentEquals(method.parameterTypes)
) {
cache.update(proxy, CallSuperProxyMethodCacheFunction)
return proxy.invokeSuper(obj, args)
}
}
}
//var handler: ProxyMethodCacheFunction? = null
for (annotation in method.annotations) when (annotation) {
is ForEachProxy -> {
handler = OnForEachProxyImpl(annotation)
break
}
is ForFirstProxy -> {
handler = OnForFirstProxyImpl(annotation)
break
}
}
if (handler == null) {
handler = OnForFirstProxyImpl(ForFirstProxy.EMPTY)
}
return handler(obj, container, method, args, proxy)
}
}

View File

@ -1,9 +1,12 @@
package cn.tursom.proxy package cn.tursom.proxy.util
import cn.tursom.core.final
import cn.tursom.core.static
import cn.tursom.reflect.asm.ReflectAsmUtils import cn.tursom.reflect.asm.ReflectAsmUtils
import net.sf.cglib.core.Signature import net.sf.cglib.core.Signature
import net.sf.cglib.proxy.Callback import net.sf.cglib.proxy.Callback
import net.sf.cglib.proxy.MethodProxy import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Field
object CglibUtil { object CglibUtil {
fun getFactoryData(clazz: Class<*>): Any? { fun getFactoryData(clazz: Class<*>): Any? {
@ -37,4 +40,4 @@ object CglibUtil {
"CGLIB\$SET_STATIC_CALLBACKS" "CGLIB\$SET_STATIC_CALLBACKS"
)!!(callbacks) )!!(callbacks)
} }
} }

View File

@ -0,0 +1,42 @@
package cn.tursom.proxy.util
import cn.tursom.core.uncheckedCast
class IntMap<V> {
companion object {
private fun upToPowerOf2(n: Int): Int {
if (n and n - 1 == 0) {
return n
}
var n = n
n = n or (n ushr 1)
n = n or (n ushr 2)
n = n or (n ushr 4)
n = n or (n ushr 8)
n = n or (n ushr 16)
return n + 1
}
}
private var array = arrayOfNulls<Any>(32)
fun clear() {
array = arrayOfNulls(16)
}
operator fun get(index: Int): V? = if (index >= array.size) {
null
} else {
array[index].uncheckedCast()
}
operator fun set(index: Int, value: V) {
if (index >= array.size) {
array = array.copyOf(upToPowerOf2(index + 1))
}
array[index] = value
}
}

View File

@ -1,57 +1,86 @@
package cn.tursom.proxy package cn.tursom.proxy
import cn.tursom.core.allFieldsSequence
import cn.tursom.core.static
import com.esotericsoftware.reflectasm.MethodAccess
import net.sf.cglib.proxy.InvocationHandler
import net.sf.cglib.proxy.MethodProxy
import org.junit.Test import org.junit.Test
import org.objectweb.asm.ClassWriter import org.objectweb.asm.ClassWriter
import java.io.File import java.io.File
class Example { class Example {
companion object{
var bytes: ByteArray? = null
fun saveBytes(b: ByteArray) {
bytes = b
}
}
open class TestClass protected constructor() { open class TestClass protected constructor() {
//@get:ForEachProxy
open var a: Int = 0 open var a: Int = 0
} }
class GetA( class GetA(
private val t: TestClass, t: TestClass,
) : ProxyMethod { ) {
fun getA(): Int { val t: TestClass = Proxy.getSuperCaller(t)
Proxy.callSuper.set(true)
return t.a + 1 val a: Int
} get() = t.a + 1
//fun getA(): Int {
// return t.a + 1
//}
} }
@Test @Test
fun getClass() { fun getClass() {
val access = MethodAccess.get(TestClass::class.java)
println(access)
val writer = ClassWriter(0) val writer = ClassWriter(0)
val enhancer = Proxy.newEnhancer(TestClass::class.java) val enhancer = Proxy.newEnhancer(TestClass::class.java)
enhancer.setCallbackType(InvocationHandler::class.java)
enhancer.setCallbackFilter { 0 }
val clazz = enhancer.createClass() val clazz = enhancer.createClass()
CglibUtil.setStaticCallbacks(clazz, arrayOf(ProxyInterceptor())) //CglibUtil.setStaticCallbacks(clazz, arrayOf(ProxyInterceptor()))
val instance = clazz.newInstance() //val instance = clazz.newInstance()
enhancer.generateClass(writer) enhancer.generateClass(writer)
File("TestClass.class").writeBytes(writer.toByteArray()) File("TestClass_InvocationHandler.class").writeBytes(writer.toByteArray())
clazz.allFieldsSequence.forEach {
if (!it.static) return@forEach
it.isAccessible = true
println("${it.name} = ${it[null]}")
if (it.type == MethodProxy::class.java) {
val methodProxy = it[null] as MethodProxy
println(methodProxy.signature)
println(methodProxy.superName)
println(methodProxy.superIndex)
}
}
}
@Test
fun getMethodAccessClass() {
val (t, container) = Proxy.get<TestClass>()
MethodAccess.get(t.javaClass)!!
//File("TestClass_MethodAccess.class").writeBytes(bytes!!)
} }
@Test @Test
fun test() { fun test() {
//val enhancer = Enhancer()
//enhancer.setSuperclass(TestClass::class.java)
//enhancer.setCallback(ProxyDispatcher)
//val t = enhancer.create() as TestClass
//val enhancer = Enhancer()
//enhancer.setSuperclass(TestClass::class.java)
//enhancer.setCallback(ProxyRefDispatcher {
// println("on Dispatcher")
// ProxyInterceptor()
//})
//
//val t = enhancer.create() as TestClass
//val writer = ClassWriter(0)
//enhancer.generateClass(writer)
//File("TestClass_ProxyRefDispatcher.class").writeBytes(writer.toByteArray())
val (t, container) = Proxy.get<TestClass>() val (t, container) = Proxy.get<TestClass>()
//val t = Proxy.getCachedTarget(TestClass::class.java).newInstance() val getA = GetA(t)
//val container = Proxy.injectCallback(t) println(getA.t == t)
//container as MutableProxyContainer
container.addProxy(GetA(t)) container.addProxy(getA)
println(t.javaClass) println(t.javaClass)
repeat(10) { repeat(10) {