You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin中实现@RestrictCallers注解以限制方法调用者?

实现@RestrictCallers注解的两种方案

你定义的@RestrictCallers注解本身只是一个标记,Kotlin不会自动强制执行调用限制,需要配合运行时校验或编译期检查来实现需求。以下是两种具体实现方式:


一、运行时校验(快速实现)

通过获取调用栈,在方法执行前检查调用者是否属于注解指定的类,非法调用直接抛出异常。

修改后的代码实现

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class RestrictCallers(val caller: KClass<out Any>)

class DlgManager {
    fun dismiss(dlg: Dialogue) {
        dlg.onDismiss()
    }
}

class Dialogue {
    @RestrictCallers(DlgManager::class)
    fun onDismiss() {
        // 1. 获取当前方法的注解
        val restrictAnnotation = this::onDismiss.findAnnotation<RestrictCallers>()
            ?: throw IllegalStateException("RestrictCallers annotation not found")
        
        val allowedCallerClass = restrictAnnotation.caller.java
        
        // 2. 遍历调用栈,找到实际调用者
        val stackTrace = Thread.currentThread().stackTrace
        val validCallingFrame = stackTrace.firstOrNull { frame ->
            // 跳过当前类、Thread类的栈帧,找到真正的调用者
            !frame.className.startsWith(this::class.java.name) && 
            !frame.className.startsWith("java.lang.Thread")
        } ?: throw IllegalAccessException("Cannot determine caller")
        
        // 3. 校验调用者是否合法
        val callingClass = Class.forName(validCallingFrame.className)
        if (!allowedCallerClass.isAssignableFrom(callingClass)) {
            throw IllegalAccessException(
                "Method onDismiss() can only be called by ${allowedCallerClass.simpleName}, " +
                "but was called by ${callingClass.simpleName}"
            )
        }
        
        // 4. 原方法业务逻辑
        println("Dialogue dismissed")
    }
}

优缺点

  • 优点:实现简单,无需额外工具或插件,适合快速验证需求。
  • 缺点:有运行时性能开销,且调用栈存在被篡改的可能,安全性并非绝对。

二、编译期检查(推荐方案)

通过自定义Kotlin Lint规则或编译器插件,在编译阶段直接拦截非法调用,从根源上阻止错误代码生成。这里以Kotlin Lint规则为例说明:

核心实现逻辑

  1. 自定义Lint检测器,扫描所有方法调用。
  2. 识别带有@RestrictCallers注解的方法。
  3. 检查调用该方法的类是否属于注解指定的允许类。
  4. 若非法调用,直接抛出编译错误。

Lint检测器代码示例

import com.android.tools.lint.detector.api.*
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression

class RestrictCallersDetector : Detector(), SourceCodeScanner {
    companion object {
        val ISSUE = Issue.create(
            id = "RestrictedMethodInvocation",
            briefDescription = "Invoking a method restricted to specific callers",
            explanation = "This method can only be called by the class specified in @RestrictCallers.",
            category = Category.CORRECTNESS,
            priority = 6,
            severity = Severity.ERROR,
            implementation = Implementation(
                RestrictCallersDetector::class.java,
                Scope.JAVA_FILE_SCOPE
            )
        )
    }

    // 指定要扫描的方法(可扩展为扫描所有带@RestrictCallers的方法)
    override fun getApplicableMethodNames(): List<String> {
        return listOf("onDismiss")
    }

    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
        // 获取方法上的@RestrictCallers注解
        val restrictAnnotation = method.getAnnotation("com.yourpackage.RestrictCallers")
            ?: return
        
        // 解析注解中允许的调用类
        val allowedCaller = restrictAnnotation.findAttributeValue("caller")?.text
            ?.removeSurrounding("class ", "::class")
            ?: return
        
        // 获取当前调用所在的类
        val callingClassName = context.evaluatedOwner?.className
            ?: return
        
        // 校验调用者是否合法
        if (callingClassName != allowedCaller) {
            context.report(
                ISSUE,
                node,
                context.getLocation(node),
                "Method `${method.name}` can only be called by $allowedCaller"
            )
        }
    }
}

集成方式

将上述检测器注册到项目的Lint配置中,编译时Lint会自动扫描代码,非法调用onDismiss()的代码会直接报错,无法通过编译。

优缺点

  • 优点:编译期拦截,无运行时开销,安全性高,能从根源上避免非法调用。
  • 缺点:需要编写Lint规则或编译器插件,实现复杂度较高。

替代方案(无需注解)

如果不想实现注解,也可以通过Kotlin的访问控制简化逻辑:

  • 将Dialogue的onDismiss()设为internal,并把DlgManager放在同一个模块内,其他模块无法调用。但同一模块内的其他类仍能调用,适合模块内的简单限制。

内容的提问来源于stack exchange,提问作者digory doo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 17:42:23