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 的优势
- 完全控制
- 可以在方法执行前后添加逻辑
- 可以修改方法的参数和返回值
- 可以决定是否执行原方法
- 异常处理
- 可以捕获并处理异常
- 可以转换异常类型
- 可以添加自定义异常处理
- 灵活性
- 可以实现其他所有通知类型的功能
- 可以根据条件决定执行流程
- 可以添加任意自定义逻辑
这样理解 @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优势
- 非侵入性
- 像隐形服务员,不影响正常流程
- 随时可以调整监控策略
- 可复用性
- 统一的监控标准
- 方便在不同场景使用
- 维护性好
- 集中管理所有监控点
- 易于修改和升级
这样的 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);
}
}
}
七、使用建议
- 命名规范
// 好的命名
@Pointcut("execution(* com.example.service.*.*(..))")
void serviceLayerOperation() {}
// 不好的命名
@Pointcut("execution(* com.example.service.*.*(..))")
void xyz() {}
- 模块化
// 分离通用切点
@Aspect
public class CommonPointcuts {
@Pointcut("within(com.example..*)")
public void inApplication() {}
}
// 使用通用切点
@Aspect
public class LoggingAspect {
@Before("CommonPointcuts.inApplication()")
public void log() {
// 记录日志
}
}
总结:
@Pointcut用于定义可重用的切点- 支持多种匹配表达式
- 可以组合使用
- 支持参数绑定
- 有助于代码复用和维护
注解和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
}
}
💡 关系总结
- 角色定位:
- 注解:标记要处理的点
- AOP:实现处理逻辑
- 协作方式:
- 注解定义切点
- AOP实现切面
- 共同完成横切关注点
- 优势互补:
- 注解提供声明式配置
- AOP提供集中式处理
- 降低代码耦合度
⚠️ 使用建议
// 1. 合理使用
class GoodPractice {
// 注解应该简洁明确
@Log("用户操作")
@Monitor(threshold = 500)
fun userOperation() {
// 业务逻辑
}
}
// 2. 避免过度使用
class BadPractice {
// 避免过多注解堆积
@Log
@Monitor
@Cacheable
@Transactional
@ValidateInput
fun complexOperation() { // 不推荐
// 业务逻辑
}
}
记住:
- 注解是"什么"和"在哪里"
- AOP是"如何做"
- 两者配合实现横切关注点
- 共同提高代码可维护性
举一个详细例子
// 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()
}
关键点说明:
- 需要使用 AOP 框架(如 Spring)来处理注解
- @Aspect 标记这是一个切面类
- @Before 等注解定义切入时机
- 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() {
// 获取位置
}
}
💡 小结
记住这些类比:
-
@Retention就像标签的保质期- SOURCE:一次性标签
- BINARY:临时标签
- RUNTIME:永久标签
-
@Target就像标签可以贴的位置- CLASS:可以贴在商品类别上
- FUNCTION:可以贴在使用方法上
- PROPERTY:可以贴在属性说明上
-
注解参数就像标签上的具体信息
- 就像商品标签上的价格、日期等
注解调用流程图
让我用一个清晰的流程图来展示注解的调用过程。
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 执行结果
代码->>注解: 使用注解
注解->>处理器: 触发处理
处理器->>处理器: 解析注解
处理器->>执行结果: 执行相应操作
执行结果->>代码: 返回结果
这些流程图展示了:
- 注解的基本调用流程
- 编译时vs运行时处理
- 实际应用场景
- 处理时序
数据埋点实现方案
把埋点比作在路上设置检查站:
代码埋点
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