Android - AOP编程及注解

228 阅读12分钟

AOP切片编程

让我用一个餐厅服务流程来生动解释 AOP 的工作原理:

一、AOP 基本概念

让我用餐厅服务的例子来生动解释 @Around 和其他 AOP 通知类型:

AOP 通知类型总览
graph TD
    A[AOP通知类型] --> B[Before]
    A --> C[Around]
    A --> D[After]
    A --> E[AfterReturning]
    A --> F[AfterThrowing]
    
    B --> B1[点餐前准备]
    C --> C1[整个服务过程]
    D --> D1[服务后收尾]
    E --> E1[成功后处理]
    F --> F1[异常后处理]
各种通知类型详解
@Aspect
class RestaurantService {
    
    // 1. @Before - 前置通知
    // 像服务员在顾客点餐前准备菜单、擦桌子
    @Before("execution(* orderFood(..))")
    fun beforeOrder() {
        println("准备菜单、擦桌子")
    }
    
    // 2. @Around - 环绕通知
    // 像服务员负责整个用餐过程,从迎接到送别
    @Around("execution(* orderFood(..))")
    fun aroundOrder(joinPoint: ProceedingJoinPoint): Any? {
        // 1. 点餐前
        println("欢迎光临,这是菜单")
        
        try {
            // 2. 执行点餐
            val result = joinPoint.proceed()
            
            // 3. 点餐后
            println("祝您用餐愉快")
            return result
        } catch (e: Exception) {
            println("非常抱歉,处理异常")
            throw e
        }
    }
    
    // 3. @After - 后置通知
    // 像服务员在顾客用完餐后收拾餐具
    @After("execution(* orderFood(..))")
    fun afterOrder() {
        println("收拾餐具、整理桌面")
    }
    
    // 4. @AfterReturning - 返回后通知
    // 像服务员在顾客满意用餐后送别
    @AfterReturning("execution(* orderFood(..))")
    fun afterOrderSuccess() {
        println("欢迎下次光临")
    }
    
    // 5. @AfterThrowing - 异常后通知
    // 像服务员处理投诉
    @AfterThrowing("execution(* orderFood(..))")
    fun handleComplaint() {
        println("处理投诉、道歉赔偿")
    }
}
@Around 特别说明
// @Around 是最强大的通知类型,可以完全控制原方法的执行
@Aspect
class ImageLoadingAspect {
    
    @Around("execution(* loadImage(..))")
    fun aroundImageLoad(joinPoint: ProceedingJoinPoint): Any? {
        // 1. 前置处理(顾客进门)
        val startTime = System.currentTimeMillis()
        println("开始加载图片")
        
        try {
            // 2. 执行原方法(用餐过程)
            val result = joinPoint.proceed()
            
            // 3. 成功处理(用餐完毕)
            val costTime = System.currentTimeMillis() - startTime
            println("图片加载成功,耗时:$costTime")
            return result
            
        } catch (e: Exception) {
            // 4. 异常处理(处理投诉)
            println("图片加载失败:${e.message}")
            throw e
            
        } finally {
            // 5. 最终处理(打扫餐桌)
            println("清理资源")
        }
    }
}
执行流程图
sequenceDiagram
    participant Client as 调用方
    participant Around as @Around
    participant Before as @Before
    participant Method as 原方法
    participant After as @After
    participant Return as @AfterReturning
    
    Client->>Around: 调用方法
    Around->>Before: 前置处理
    Before->>Method: 执行方法
    
    alt 成功执行
        Method->>After: 后置处理
        After->>Return: 返回处理
        Return->>Around: 完成处理
    else 发生异常
        Method->>After: 后置处理
        After->>Around: 异常处理
    end
    
    Around->>Client: 返回结果
实际应用示例
// 图片加载监控示例
@Aspect
class ImageMonitor {
    
    @Around("execution(* com.bumptech.glide.RequestBuilder.into(..))")
    fun monitorImageLoading(joinPoint: ProceedingJoinPoint): Any? {
        val startTime = System.nanoTime()
        val request = joinPoint.args[0]
        
        return try {
            // 1. 执行图片加载
            val result = joinPoint.proceed()
            
            // 2. 记录成功信息
            val loadTime = System.nanoTime() - startTime
            recordSuccess(request, loadTime)
            
            result
        } catch (e: Exception) {
            // 3. 记录失败信息
            recordError(request, e)
            throw e
        }
    }
}
@Around 的优势
  1. 完全控制
  • 可以在方法执行前后添加逻辑
  • 可以修改方法的参数和返回值
  • 可以决定是否执行原方法
  1. 异常处理
  • 可以捕获并处理异常
  • 可以转换异常类型
  • 可以添加自定义异常处理
  1. 灵活性
  • 可以实现其他所有通知类型的功能
  • 可以根据条件决定执行流程
  • 可以添加任意自定义逻辑

这样理解 @Around 是不是更清晰了?它就像一个:

  • 全能服务员,负责整个服务流程
  • 可以在任何时候介入处理
  • 可以完全控制服务过程
  • 可以处理任何异常情况

二、实际应用示例

// 1. 图片加载监控切面
@Aspect
class ImageLoadingAspect {
    
    // 监控所有图片加载请求(相当于监控所有点餐请求)
    @Pointcut("execution(* com.bumptech.glide.RequestBuilder.into(..))")
    fun imageLoadPoint() {}
    
    @Around("imageLoadPoint()")
    fun monitorImageLoading(joinPoint: ProceedingJoinPoint): Any? {
        // 1. 记录开始信息(顾客开始点餐)
        val startTime = System.nanoTime()
        val requestInfo = extractRequestInfo(joinPoint)
        
        try {
            // 2. 执行原方法(厨师准备餐点)
            val result = joinPoint.proceed()
            
            // 3. 记录结果(记录订单完成情况)
            recordLoadingSuccess(
                requestInfo = requestInfo,
                costTime = System.nanoTime() - startTime
            )
            
            return result
        } catch (e: Exception) {
            // 4. 异常处理(处理异常情况)
            recordLoadingFailure(requestInfo, e)
            throw e
        }
    }
}

// 2. Bitmap创建监控切面
@Aspect
class BitmapCreationAspect {
    
    // 监控Bitmap创建(相当于监控厨师备菜)
    @Around("call(android.graphics.Bitmap.createBitmap(..))")
    fun monitorBitmapCreation(joinPoint: ProceedingJoinPoint): Bitmap? {
        // 1. 获取参数(获取菜品规格)
        val width = joinPoint.args[0] as Int
        val height = joinPoint.args[1] as Int
        
        // 2. 检查大小(检查份量)
        checkBitmapSize(width, height)
        
        // 3. 执行创建(准备菜品)
        return joinPoint.proceed() as Bitmap?
    }
}

三、工作流程图

sequenceDiagram
    participant Client as 顾客(调用方)
    participant Waiter as 服务员(切面)
    participant Kitchen as 厨房(原方法)
    participant Monitor as 监控系统
    
    Client->>Waiter: 发起请求(点餐)
    
    Note over Waiter: @Before
    Waiter->>Monitor: 记录开始(记录点单)
    
    Note over Waiter: @Around
    Waiter->>Kitchen: 转发请求(传递订单)
    Kitchen-->>Waiter: 返回结果(出餐)
    
    Note over Waiter: @After
    Waiter->>Monitor: 记录完成(记录完成)
    
    Waiter-->>Client: 返回结果(送餐)

四、具体实现示例

// 1. 定义监控注解(相当于定义服务标准)
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MonitorImageLoad

// 2. 实现切面(相当于培训服务员)
@Aspect
class ImageMonitorAspect {
    
    @Around("@annotation(com.example.MonitorImageLoad)")
    fun monitorImage(joinPoint: ProceedingJoinPoint): Any? {
        val methodName = joinPoint.signature.name
        val startTime = System.currentTimeMillis()
        
        return try {
            // 执行原方法
            val result = joinPoint.proceed()
            
            // 记录成功信息
            val costTime = System.currentTimeMillis() - startTime
            logSuccess(methodName, costTime)
            
            result
        } catch (e: Exception) {
            // 记录失败信息
            logError(methodName, e)
            throw e
        }
    }
}

// 3. 使用示例(相当于实际服务)
class ImageLoader {
    
    @MonitorImageLoad
    fun loadImage(url: String, imageView: ImageView) {
        Glide.with(imageView.context)
            .load(url)
            .into(imageView)
    }
}

五、数据收集流程

graph TD
    A[请求进入] --> B{切面拦截}
    
    B --> C[前置处理]
    B --> D[方法执行]
    B --> E[后置处理]
    
    C --> C1[记录参数]
    C --> C2[检查条件]
    
    D --> D1[执行原方法]
    
    E --> E1[记录结果]
    E --> E2[统计分析]

六、实际效果

// 监控报告示例
"""
=== 图片加载监控报告 ===

1. 加载统计
- 总请求数:1000次
- 成功率:98%
- 平均耗时:150ms

2. 异常分布
- OOM:5次
- 超时:10次
- 解码失败:5次

3. 大图统计
- 数量:50张
- 平均大小:3MB
- 最大图片:10MB

4. 优化建议
- 建议对大于2MB的图片进行压缩
- 列表页面建议使用缩略图
- 建议增加图片预加载
"""

七、AOP优势

  1. 非侵入性
  • 像隐形服务员,不影响正常流程
  • 随时可以调整监控策略
  1. 可复用性
  • 统一的监控标准
  • 方便在不同场景使用
  1. 维护性好
  • 集中管理所有监控点
  • 易于修改和升级

这样的 AOP 系统:

  • 清晰易懂
  • 使用方便
  • 效果明显
  • 易于维护

@Pointcut的详细讲解

一、基本语法

@Aspect
public class AspectExample {
    // 1. 最简单的切点
    @Pointcut("execution(* com.example.*.*(..))") // 匹配com.example包下所有方法
    public void basicPointcut() {} // 空方法,只是定义切点
    
    // 2. 使用切点
    @Before("basicPointcut()") // 引用上面定义的切点
    public void beforeMethod() {
        System.out.println("方法执行前");
    }
}

二、常用表达式

@Aspect
class MethodAspect {
    // 1. 匹配方法名
    @Pointcut("execution(* on*(..))")  // 匹配所有以on开头的方法
    fun onMethods() {}
    
    // 2. 匹配注解
    @Pointcut("@annotation(com.example.LogMethod)") // 匹配带@LogMethod注解的方法
    fun logMethods() {}
    
    // 3. 匹配类
    @Pointcut("within(com.example.MainActivity)") // 匹配MainActivity中的所有方法
    fun mainActivityMethods() {}
    
    // 使用这些切点
    @Around("onMethods() || logMethods()")  // 组合多个切点
    fun aroundMethod(joinPoint: ProceedingJoinPoint) {
        println("方法执行前")
        joinPoint.proceed()
        println("方法执行后")
    }
}
@Aspect
public class PointcutExpressions {
    // 1. 方法执行
    @Pointcut("execution(public * *(..))")  // 所有public方法
    void publicMethod() {}
    
    // 2. 包范围
    @Pointcut("within(com.example.service.*)") // service包下的所有类
    void inServicePackage() {}
    
    // 3. 带注解的方法
    @Pointcut("@annotation(com.example.Log)") // 带@Log注解的方法
    void loggedMethod() {}
    
    // 4. 特定参数
    @Pointcut("execution(* *(String, ..))") // 第一个参数是String的方法
    void stringParamMethod() {}
}

三、组合使用

@Aspect
public class CombinedPointcuts {
    // 1. 与操作
    @Pointcut("publicMethod() && inServicePackage()")
    void publicServiceMethod() {} // service包中的public方法
    
    // 2. 或操作
    @Pointcut("execution(* save*(..)) || execution(* update*(..))")
    void saveOrUpdateMethod() {} // 以save或update开头的方法
    
    // 3. 非操作
    @Pointcut("within(com.example..*) && !within(com.example.util..*)")
    void notInUtilPackage() {} // 不在util包下的方法
}

四、实际应用示例

@Aspect
public class LoggingAspect {
    // 1. 定义切点
    @Pointcut("execution(* com.example.service.*.*(..))")
    void serviceLayer() {}
    
    @Pointcut("@annotation(com.example.LogExecutionTime)")
    void logExecutionTime() {}
    
    // 2. 使用切点
    @Around("serviceLayer() && logExecutionTime()")
    public Object logTimeAndExecute(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        Object result = joinPoint.proceed(); // 执行原方法
        
        long end = System.currentTimeMillis();
        System.out.println("方法执行时间: " + (end - start) + "ms");
        
        return result;
    }
}

五、参数绑定

@Aspect
public class ParameterAspect {
    // 1. 绑定参数
    @Pointcut("execution(* updateUser(String)) && args(userId)")
    void userUpdate(String userId) {}
    
    // 2. 使用绑定的参数
    @Before("userUpdate(userId)")
    public void beforeUserUpdate(String userId) {
        System.out.println("更新用户: " + userId);
    }
    
    // 3. 绑定注解
    @Pointcut("@annotation(logger)")
    void loggedMethod(Logger logger) {}
    
    @Around("loggedMethod(logger)")
    public Object around(ProceedingJoinPoint point, Logger logger) throws Throwable {
        System.out.println("日志级别: " + logger.level());
        return point.proceed();
    }
}

六、完整示例

@Aspect
public class MonitoringAspect {
    private static final Logger logger = LoggerFactory.getLogger(MonitoringAspect.class);
    
    // 1. 定义多个切点
    @Pointcut("execution(* com.example.service.*.*(..))")
    void service() {}
    
    @Pointcut("execution(* com.example.repository.*.*(..))")
    void repository() {}
    
    @Pointcut("@annotation(com.example.Monitor)")
    void monitored() {}
    
    // 2. 组合使用
    @Around("(service() || repository()) && monitored()")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        try {
            // 执行原方法
            return joinPoint.proceed();
        } finally {
            // 记录执行时间
            long duration = System.currentTimeMillis() - startTime;
            logger.info("方法 {} 执行时间: {}ms", 
                joinPoint.getSignature().getName(), duration);
        }
    }
}

七、使用建议

  1. 命名规范
// 好的命名
@Pointcut("execution(* com.example.service.*.*(..))")
void serviceLayerOperation() {}

// 不好的命名
@Pointcut("execution(* com.example.service.*.*(..))")
void xyz() {}
  1. 模块化
// 分离通用切点
@Aspect
public class CommonPointcuts {
    @Pointcut("within(com.example..*)")
    public void inApplication() {}
}

// 使用通用切点
@Aspect
public class LoggingAspect {
    @Before("CommonPointcuts.inApplication()")
    public void log() {
        // 记录日志
    }
}

总结:

  1. @Pointcut 用于定义可重用的切点
  2. 支持多种匹配表达式
  3. 可以组合使用
  4. 支持参数绑定
  5. 有助于代码复用和维护

注解和AOP编程的关系

🌟 形象的比喻

/* 
想象一个生产线:
- 产品(原始方法)是核心
- 质检点(切面)在不同位置
- 质检标记(注解)表明在哪里需要检查
*/

// 1. 注解就像质检标记
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class QualityCheck(
    val checkPoint: String
)

// 2. AOP就像质检流程
@Aspect
class QualityAspect {
    @Before("@annotation(QualityCheck)")
    fun checkQuality(checkPoint: String) {
        println("在$checkPoint进行质检")
    }
}

💡 实际关系示例

// 1. 日志记录示例
// 注解:标记需要记录日志的地方
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Log(
    val description: String = ""
)

// AOP:实现日志记录的切面
@Aspect
class LoggingAspect {
    @Around("@annotation(log)")
    fun logAround(
        joinPoint: ProceedingJoinPoint,
        log: Log
    ): Any? {
        // 方法执行前记录
        println("开始执行: ${log.description}")
        
        try {
            // 执行原方法
            val result = joinPoint.proceed()
            
            // 方法执行后记录
            println("执行完成: ${log.description}")
            return result
        } catch (e: Exception) {
            // 异常记录
            println("执行异常: ${e.message}")
            throw e
        }
    }
}

// 使用示例
class UserService {
    @Log("用户注册")
    fun register(user: User) {
        // 注册逻辑
    }
}

⚡ 协同工作流程

// 1. 性能监控示例
// 注解:标记需要监控的方法
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Monitor(
    val threshold: Long = 1000 // 阈值(毫秒)
)

// AOP:实现性能监控的切面
@Aspect
class PerformanceAspect {
    @Around("@annotation(monitor)")
    fun monitorPerformance(
        joinPoint: ProceedingJoinPoint,
        monitor: Monitor
    ): Any? {
        val startTime = System.currentTimeMillis()
        
        try {
            // 执行原方法
            val result = joinPoint.proceed()
            
            // 检查执行时间
            val executionTime = System.currentTimeMillis() - startTime
            if (executionTime > monitor.threshold) {
                logSlowExecution(
                    joinPoint.signature.name,
                    executionTime
                )
            }
            
            return result
        } catch (e: Exception) {
            logError(e)
            throw e
        }
    }
}

🔄 工作关系图

graph TD
    A[源代码] --> B[注解标记]
    B --> C[AOP切面]
    C --> D[前置处理]
    C --> E[后置处理]
    C --> F[环绕处理]
    D --> G[原方法执行]
    E --> G
    F --> G
    
    style A fill:#f9f,stroke:#333,stroke-width:2px
    style C fill:#bbf,stroke:#333,stroke-width:2px

📊 实际应用场景

// 1. 事务管理
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Transactional

@Aspect
class TransactionAspect {
    @Around("@annotation(Transactional)")
    fun handleTransaction(joinPoint: ProceedingJoinPoint): Any? {
        val transaction = beginTransaction()
        try {
            val result = joinPoint.proceed()
            transaction.commit()
            return result
        } catch (e: Exception) {
            transaction.rollback()
            throw e
        }
    }
}

// 2. 缓存管理
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Cacheable(
    val key: String = ""
)

@Aspect
class CacheAspect {
    private val cache = mutableMapOf<String, Any>()
    
    @Around("@annotation(cacheable)")
    fun handleCache(
        joinPoint: ProceedingJoinPoint,
        cacheable: Cacheable
    ): Any? {
        val key = generateKey(cacheable.key, joinPoint.args)
        
        // 检查缓存
        cache[key]?.let { return it }
        
        // 执行方法并缓存结果
        val result = joinPoint.proceed()
        cache[key] = result
        return result
    }
}

💡 关系总结

  1. 角色定位:
- 注解:标记要处理的点
- AOP:实现处理逻辑
  1. 协作方式:
- 注解定义切点
- AOP实现切面
- 共同完成横切关注点
  1. 优势互补:
- 注解提供声明式配置
- AOP提供集中式处理
- 降低代码耦合度

⚠️ 使用建议

// 1. 合理使用
class GoodPractice {
    // 注解应该简洁明确
    @Log("用户操作")
    @Monitor(threshold = 500)
    fun userOperation() {
        // 业务逻辑
    }
}

// 2. 避免过度使用
class BadPractice {
    // 避免过多注解堆积
    @Log
    @Monitor
    @Cacheable
    @Transactional
    @ValidateInput
    fun complexOperation() { // 不推荐
        // 业务逻辑
    }
}

记住:

  1. 注解是"什么"和"在哪里"
  2. AOP是"如何做"
  3. 两者配合实现横切关注点
  4. 共同提高代码可维护性

举一个详细例子

// 1. 定义注解
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class CheckPermission(
    val permission: String
)

// 2. 完整的注解处理器(切面)
@Aspect  // 标记这是一个切面
class PermissionAspect {
    
    // @Before: 在原方法执行前调用
    @Before("@annotation(checkPermission)")  // 指定切入点
    fun checkPermission(checkPermission: CheckPermission) {
        val hasPermission = checkPermissionImpl(checkPermission.permission)
        if (!hasPermission) {
            throw SecurityException("没有${checkPermission.permission}权限")
        }
    }
    
    private fun checkPermissionImpl(permission: String): Boolean {
        // 实际的权限检查逻辑
        return true 
    }
}

// 3. 使用示例
class CameraService {
    @CheckPermission("android.permission.CAMERA")
    fun takePhoto(): String {
        return "照片已拍摄"
    }
}

// 4. 调用流程
fun main() {
    // AOP框架(如Spring)会自动创建代理对象
    val cameraService = CameraService()
    
    // 当调用takePhoto时:
    // 1. AOP拦截到带有@CheckPermission注解的方法调用
    // 2. 调用PermissionAspect.checkPermission进行权限检查
    // 3. 权限检查通过后,才会执行原始的takePhoto方法
    cameraService.takePhoto()
}

关键点说明:

  1. 需要使用 AOP 框架(如 Spring)来处理注解
  2. @Aspect 标记这是一个切面类
  3. @Before 等注解定义切入时机
  4. AOP 框架会自动创建代理,拦截方法调用

实际执行顺序:

sequenceDiagram
    participant Client
    participant AOP代理
    participant PermissionAspect
    participant CameraService

    Client->>AOP代理: 调用takePhoto()
    AOP代理->>PermissionAspect: 执行权限检查
    PermissionAspect-->>AOP代理: 检查结果
    AOP代理->>CameraService: 权限通过,执行原方法
    CameraService-->>Client: 返回结果

注解标签的基本使用

🌟 注解的基本标签

// @Retention - 保留期(决定标签什么时候可见)
/*
就像商品标签的不同类型:
- SOURCE:一次性标签,用完就扔(只在源码可见)
- BINARY:包装标签,到商店就撕掉(在编译后的类文件可见)
- RUNTIME:永久标签,一直保留(运行时可见)
*/

// 示例:运行时可见的标签
@Retention(AnnotationRetention.RUNTIME)
annotation class MyTag

// 示例:仅源码可见的标签
@Retention(AnnotationRetention.SOURCE)
annotation class DevNote

💡 Target标签(标记可以贴在哪里)

/*
就像商场里的标签:
- CLASS:可以贴在商品类别上
- FUNCTION:可以贴在使用说明上
- PROPERTY:可以贴在商品属性上
- FIELD:可以贴在具体规格上
*/

// 1. 可以贴在类上的标签
@Target(AnnotationTarget.CLASS)
annotation class Controller

// 2. 可以贴在函数上的标签
@Target(AnnotationTarget.FUNCTION)
annotation class TestCase

// 3. 多个位置都可以贴的标签
@Target(
    AnnotationTarget.CLASS,
    AnnotationTarget.FUNCTION
)
annotation class Important

⚡ 实际使用例子

// 1. 权限检查标签
@Retention(AnnotationRetention.RUNTIME) // 运行时需要检查
@Target(AnnotationTarget.FUNCTION)      // 贴在函数上
annotation class RequiresPermission(
    val permission: String  // 需要的权限
)

class CameraFeature {
    @RequiresPermission("android.permission.CAMERA")
    fun takePhoto() {
        // 拍照功能
    }
}

// 2. 测试相关标签
@Retention(AnnotationRetention.RUNTIME) // 运行时可见
@Target(AnnotationTarget.FUNCTION)      // 贴在测试方法上
annotation class Test

class MyTests {
    @Test  // 标记这是一个测试方法
    fun testFeature() {
        // 测试代码
    }
}

// 3. API版本标签
@Retention(AnnotationRetention.RUNTIME) // 运行时检查
@Target(AnnotationTarget.FUNCTION)      // 贴在API方法上
annotation class APIVersion(
    val version: Int  // API版本号
)

class UserAPI {
    @APIVersion(1)  // 标记这是V1版本的API
    fun oldMethod() {}
    
    @APIVersion(2)  // 标记这是V2版本的API
    fun newMethod() {}
}

🔄 生动的类比

/* 
想象一个图书馆的图书分类系统:

@Retention 就像书的保存期限:
- SOURCE:临时展览的书(用完就撤)
- BINARY:馆内阅览的书(在馆内可见)
- RUNTIME:可以借出的书(随时可用)

@Target 就像书可以放的位置:
- CLASS:可以放在总分类区
- FUNCTION:可以放在具体分区
- PROPERTY:可以放在特定书架
*/

// 示例:图书馆书籍分类
@Retention(AnnotationRetention.RUNTIME) // 可借阅
@Target(AnnotationTarget.CLASS)         // 放在分类区
annotation class BookCategory(
    val category: String,
    val ageLimit: Int
)

@BookCategory("儿童读物", ageLimit = 12)
class StoryBook {
    // 书的内容
}

📱 Android实际应用

// 1. View绑定(就像给控件贴标签)
class MainActivity : AppCompatActivity() {
    @BindView(R.id.textView)  // 标记这个变量绑定到哪个View
    lateinit var textView: TextView
    
    @OnClick(R.id.button)     // 标记这是按钮点击处理函数
    fun handleClick() {
        // 处理点击
    }
}

// 2. Room数据库(就像给数据表贴标签)
@Entity(tableName = "users")  // 标记这是一个数据库表
data class User(
    @PrimaryKey           // 标记主键
    val id: Int,
    
    @ColumnInfo(name = "user_name")  // 标记列名
    val name: String
)

// 3. 权限管理(就像给功能贴权限标签)
class LocationFeature {
    @RequiresPermission(
        Manifest.permission.ACCESS_FINE_LOCATION
    )
    fun getLocation() {
        // 获取位置
    }
}

💡 小结

记住这些类比:

  1. @Retention 就像标签的保质期

    • SOURCE:一次性标签
    • BINARY:临时标签
    • RUNTIME:永久标签
  2. @Target 就像标签可以贴的位置

    • CLASS:可以贴在商品类别上
    • FUNCTION:可以贴在使用方法上
    • PROPERTY:可以贴在属性说明上
  3. 注解参数就像标签上的具体信息

    • 就像商品标签上的价格、日期等

注解调用流程图

让我用一个清晰的流程图来展示注解的调用过程。

graph TD
    A[定义注解] --> B[使用注解标注代码]
    B --> C{编译时还是运行时?}
    
    C -->|编译时| D[编译时注解处理]
    C -->|运行时| E[运行时注解处理]
    
    D --> D1[注解处理器APT]
    D1 --> D2[生成新代码]
    D2 --> D3[编译完成]
    
    E --> E1[加载类]
    E1 --> E2[反射读取注解]
    E2 --> E3[解析注解信息]
    E3 --> E4[执行相应操作]
    
    %% 示例说明
    style A fill:#f9f,stroke:#333,stroke-width:2px
    style B fill:#bbf,stroke:#333,stroke-width:2px
    style C fill:#dfd,stroke:#333,stroke-width:2px

具体示例流程

graph TD
    A[定义权限注解 RequiresPermission] --> B[在方法上使用注解]
    B --> C[运行时]
    
    C --> D[调用带注解的方法]
    D --> E[注解处理器拦截]
    E --> F{检查权限}
    
    F -->|有权限| G[执行方法]
    F -->|无权限| H[抛出异常]
    
    %% 颜色标注
    style A fill:#f9f,stroke:#333,stroke-width:2px
    style F fill:#dfd,stroke:#333,stroke-width:2px

依赖注入流程

graph TD
    A[定义注入注解 Inject] --> B[标注需要注入的字段]
    B --> C[启动应用]
    
    C --> D[依赖注入处理器扫描]
    D --> E[找到Inject注解]
    E --> F[创建依赖实例]
    F --> G[注入依赖]
    
    %% 样式
    style A fill:#f9f,stroke:#333,stroke-width:2px
    style E fill:#bbf,stroke:#333,stroke-width:2px

实际应用流程(如 Room)

graph TD
    A[定义实体类注解 Entity] --> B[标注数据类]
    B --> C[编译时处理]
    
    C --> D[Room注解处理器]
    D --> E[生成DAO实现]
    E --> F[生成数据库代码]
    
    F --> G[运行时]
    G --> H[使用生成的代码]
    
    %% 关键节点突出显示
    style A fill:#f9f,stroke:#333,stroke-width:2px
    style D fill:#bbf,stroke:#333,stroke-width:2px
    style F fill:#dfd,stroke:#333,stroke-width:2px

注解处理时序

sequenceDiagram
    participant 代码
    participant 注解
    participant 处理器
    participant 执行结果
    
    代码->>注解: 使用注解
    注解->>处理器: 触发处理
    处理器->>处理器: 解析注解
    处理器->>执行结果: 执行相应操作
    执行结果->>代码: 返回结果

这些流程图展示了:

  1. 注解的基本调用流程
  2. 编译时vs运行时处理
  3. 实际应用场景
  4. 处理时序

数据埋点实现方案

把埋点比作在路上设置检查站:

代码埋点
class TrackManager {
    companion object {
        private val events = mutableListOf<TrackEvent>()
        
        fun track(eventName: String, params: Map<String, Any> = emptyMap()) {
            val event = TrackEvent(
                name = eventName,
                params = params,
                timestamp = System.currentTimeMillis()
            )
            events.add(event)
            
            // 达到一定数量后上传
            if (events.size >= 100) {
                uploadEvents()
            }
        }
        
        private fun uploadEvents() {
            viewModelScope.launch(Dispatchers.IO) {
                try {
                    api.uploadEvents(events)
                    events.clear()
                } catch (e: Exception) {
                    Log.e("Track", "Upload failed", e)
                }
            }
        }
    }
}
无侵入式埋点
// 使用AOP实现自动埋点
@Aspect
class AutoTrackAspect {
    @Around("execution(* android.app.Activity.onCreate(..))")
    fun trackPageView(joinPoint: ProceedingJoinPoint) {
        val activity = joinPoint.target as Activity
        TrackManager.track("page_view", mapOf(
            "page_name" to activity.javaClass.simpleName
        ))
        joinPoint.proceed()
    }
}

埋点流程:

flowchart TD
    A[触发埋点] --> B{埋点类型}
    B -->|代码埋点| C[手动调用]
    B -->|无侵入埋点| D[AOP处理]
    C --> E[收集数据]
    D --> E
    E --> F[本地缓存]
    F --> G{达到上传条件?}
    G -->|是| H[批量上传]
    G -->|否| F