【AOP 面向切面编程】Android Studio 使用 AspectJ 监控方法运行 ( 定义连接点注解 | 定义 Aspect 切面 | 定义切入点 | 逐个处理切入点的各个连接点 )
文章目录
- 一、定义 Join Point 连接点注解
- 二、定义 Aspect 切面
- 1、定义 Aspect 切面
- 2、定义 Aspect 切面
- 3、逐个处理切入点的各个连接点
- 4、完整 Aspect 切面代码
- 三、使用 AspectJ 埋点并监控方法性能
一、定义 Join Point 连接点注解
要监控哪些方法 , 首先要对该方法添加注解 , 该注解标记哪些方法是 Join Point 连接点 , 所有被该注解标记的方法构成 一组 Join Point 连接点 , 即 Point Cut 切入点 ;
package com.example.aop_demo;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 该注解标记哪些方法是 Join Point 连接点* 所有被该注解标记的方法构成 一组 Join Point 连接点 , 即 Point Cut 切入点*/
@Retention(RetentionPolicy.RUNTIME) // 注解保留到运行时
@Target(ElementType.METHOD) // 注解作用于方法上
public @interface Monitor {String value();
}
注解的用法如下 : 如果想要监控下面的 public void textClick(View view)
方法 , 在该方法上添加 @Monitor
注解即可 ;
@Monitor("textClick")public void textClick(View view) {// 休眠 500 msSystemClock.sleep(500);}
二、定义 Aspect 切面
1、定义 Aspect 切面
Aspect 切面 : Java 中的类声明是 对事物的抽象 , AspectJ 中的 Aspect 切面就是 对切面的抽象 , 其中包含了 Point Cut 切入点 和 Advice 通知 ; 使用 @Aspect
注解修饰 ;
/*** 定义 Aspect 切面*/
@Aspect
public class MonitorAspect {}
2、定义 Aspect 切面
Point Cut 切入点 : 一组 Join Point 连接点 , 通过 逻辑关系 / 正则表达式 / 通配符 等关系组合 , 定义了 Advice 通知发生的位置 ;
解析 "execution(@com.example.aop_demo.Monitor * *(..))"
匹配规则 :
@com.example.aop_demo.Monitor
表示带该注解的方法- 第 1 个
*
表示在所有包下面 - 第 2 个
*
表示在所有类下面 (..)
表示所有方法 , 参数不限
整体含义 : 所有的包 所有的类 中 , 带有 @com.example.aop_demo.Monitor
注解的方法 , 都是 Pointcut 切入点 , 每个方法都是一个 JoinPoint 连接点 ;
/*** 定义切入点* "execution(@com.example.aop_demo.Monitor * *(..))" 表示* 带有 @com.example.aop_demo.Monitor 注解的* 所有包下面的 所有类中的 所有方法, 方法参数不限* 上述方法组成 切入点 , 每个方法都是一个 Join Point 连接点** execution(@com.example.aop_demo.Monitor * *(..)) 解读* - @com.example.aop_demo.Monitor 表示带该注解的方法* - 第 1 个 * 表示在所有包下面* - 第 2 个 * 表示在所有类下面* - (..) 表示所有方法 , 参数不限** 所有的包 所有的类 中 , 带有 @com.example.aop_demo.Monitor 注解的方法 , 都是 Pointcut 切入点* 上述每个方法都是一个 Join Point 连接点*/@Pointcut("execution(@com.example.aop_demo.Monitor * *(..))")public void pointCut(){}
3、逐个处理切入点的各个连接点
@Around("pointCut()")
注解中传入的注解属性是 切入点 的名称 , 就是上面定义的 public void pointCut(){}
方法对应的 Pointcut 切入点 ;
获取 JoinPoint 连接点 对应 方法的相关属性 :
- 获取方法上的注解 , 以及注解属性 : 首先获取方法签名 , 在回去方法签名对应的
Method
对象 , 获取该对象上的注解 , 根据注解调用注解中定义的获取属性的接口方法 ;
// 获取方法上 @Monitor("onClick") 注解中的注解属性 字符串// 获取被 @Monitor("onClick") 注解修饰的方法的 方法签名MethodSignature signature = (MethodSignature) joinPoint.getSignature();// 根据方法签名获取方法// 然后获取方法上的 @Monitor 注解Monitor annotation = signature.getMethod().getAnnotation(Monitor.class);// 获取 @Monitor("onClick") 注解中的注解属性String value = annotation.value();
- 获取方法名称 :
signature.getDeclaringType()
就是方法所在的类的字节码类对象 , 然后调用getSimpleName
方法即可获取类名 ;
// 获取方法名称String className = signature.getDeclaringType().getSimpleName();
- 获取方法所在类名称 : 直接调用方法签名的
getName
方法 , 即可获取方法名 ;
// 获取方法所在的类名String methodName = signature.getName();
调用 joinPoint.proceed()
方法 , 可同步执行该具体的方法 , 方法的上下可以进行用户自己的埋点业务逻辑 , 如统计方法执行耗时等操作 ;
// 执行具体的方法result = joinPoint.proceed();
代码示例 :
/*** 逐个处理 Pointcut 切入点 中的 JoinPoint 连接点* 一个一个处理** @Around("pointCut()") 注解中传入的注解属性是* 切入点的名称 , 就是上面定义的 public void pointCut(){} 方法** @param joinPoint* @return*/@Around("pointCut()")public Object processJoinPoint(ProceedingJoinPoint joinPoint) {Object result = null;try {// 获取方法上 @Monitor("onClick") 注解中的注解属性 字符串// 获取被 @Monitor("onClick") 注解修饰的方法的 方法签名MethodSignature signature = (MethodSignature) joinPoint.getSignature();// 根据方法签名获取方法// 然后获取方法上的 @Monitor 注解Monitor annotation = signature.getMethod().getAnnotation(Monitor.class);// 获取 @Monitor("onClick") 注解中的注解属性String value = annotation.value();// 获取方法名称String className = signature.getDeclaringType().getSimpleName();// 获取方法所在的类名String methodName = signature.getName();// 记录方法执行开始时间long startTime = System.currentTimeMillis();// 执行具体的方法result = joinPoint.proceed();// 记录方法执行结束时间long endTime = System.currentTimeMillis();Log.i(TAG, "执行 " + className + " 中的 " + methodName +" 方法花费了 " + (endTime - startTime) + " ms , 注解属性为 " + value );} catch (Throwable throwable) {throwable.printStackTrace();}return result;}
4、完整 Aspect 切面代码
package com.example.aop_demo;import android.util.Log;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;/*** 定义 Aspect 切面*/
@Aspect
public class MonitorAspect {private static final String TAG = "MonitorAspect";/*** 定义切入点* "execution(@com.example.aop_demo.Monitor * *(..))" 表示* 带有 @com.example.aop_demo.Monitor 注解的* 所有包下面的 所有类中的 所有方法, 方法参数不限* 上述方法组成 切入点 , 每个方法都是一个 Join Point 连接点** execution(@com.example.aop_demo.Monitor * *(..)) 解读* - @com.example.aop_demo.Monitor 表示带该注解的方法* - 第 1 个 * 表示在所有包下面* - 第 2 个 * 表示在所有类下面* - (..) 表示所有方法 , 参数不限** 所有的包 所有的类 中 , 带有 @com.example.aop_demo.Monitor 注解的方法 , 都是 Pointcut 切入点* 上述每个方法都是一个 Join Point 连接点*/@Pointcut("execution(@com.example.aop_demo.Monitor * *(..))")public void pointCut(){}/*** 逐个处理 Pointcut 切入点 中的 JoinPoint 连接点* 一个一个处理** @Around("pointCut()") 注解中传入的注解属性是* 切入点的名称 , 就是上面定义的 public void pointCut(){} 方法** @param joinPoint* @return*/@Around("pointCut()")public Object processJoinPoint(ProceedingJoinPoint joinPoint) {Object result = null;try {// 获取方法上 @Monitor("onClick") 注解中的注解属性 字符串// 获取被 @Monitor("onClick") 注解修饰的方法的 方法签名MethodSignature signature = (MethodSignature) joinPoint.getSignature();// 根据方法签名获取方法// 然后获取方法上的 @Monitor 注解Monitor annotation = signature.getMethod().getAnnotation(Monitor.class);// 获取 @Monitor("onClick") 注解中的注解属性String value = annotation.value();// 获取方法名称String className = signature.getDeclaringType().getSimpleName();// 获取方法所在的类名String methodName = signature.getName();// 记录方法执行开始时间long startTime = System.currentTimeMillis();// 执行具体的方法result = joinPoint.proceed();// 记录方法执行结束时间long endTime = System.currentTimeMillis();Log.i(TAG, "执行 " + className + " 中的 " + methodName +" 方法花费了 " + (endTime - startTime) + " ms , 注解属性为 " + value );} catch (Throwable throwable) {throwable.printStackTrace();}return result;}
}
三、使用 AspectJ 埋点并监控方法性能
监控 public void textClick(View view)
方法性能 , 在该方法上使用 @Monitor
注解 , 该注解已经被定义为切入点 , 所有的包 所有的类 中 , 带有 @com.example.aop_demo.Monitor
注解的方法 , 都是 Pointcut 切入点 , 应用执行时 , 会自动执行埋点业务逻辑 , 这里是在方法执行前后 , 分别记录时间 , 最终计算出方法的耗时 ;
package com.example.aop_demo;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Monitor("textClick")public void textClick(View view) {// 休眠 500 msSystemClock.sleep(500);}
}
执行结果 : 点击按钮 , 触发 textClick
方法 , 触发了埋点逻辑 , 自动计算出了该方法的执行耗时 ;
2021-09-22 22:23:04.954 12492-12492/com.example.aop_demo I/MonitorAspect: 执行 MainActivity 中的 textClick 方法花费了 501 ms , 注解属性为 textClick
【AOP 面向切面编程】Android Studio 使用 AspectJ 监控方法运行 ( 定义连接点注解 | 定义 Aspect 切面 | 定义切入点 | 逐个处理切入点的各个连接点 )相关推荐
- 【AOP 面向切面编程】Android Studio 使用 AspectJ 监控方法运行原理分析
文章目录 一.查看使用 AspectJ 后生成的 Class 字节码类 二.AspectJ 的本质 一.查看使用 AspectJ 后生成的 Class 字节码类 在 Android Studio 中查 ...
- 【错误记录】Android Studio 配置 AspectJ 报错 ( all buildscript {} blocks must appear before any plugins {} )
文章目录 一.报错信息 二.解决方案 一.报错信息 在 Android Studio 配置 AspectJ 报错 : Build file 'D:\002_Project\002_Android_Le ...
- 利用消息机制实现.NET AOP(面向方面编程)--基本概念和实现
本系列文章主要讲述如何利用.NET的Remoting消息框架实现AOP 本系列文章试图实现三种AOP实现 本系列文章以示例代码为主,对一些概念和原理不会进行过于深入的讨论 这里不谈论AOP在现实开发中 ...
- 【走过巨坑】android studio对于jni调用及运行闪退无法加载库的问题解决方案
[走过巨坑]android studio对于jni调用及运行闪退无法加载库的问题解决方案 参考文章: (1)[走过巨坑]android studio对于jni调用及运行闪退无法加载库的问题解决方案 ( ...
- android studio 使用问题 解决方法
android studio 使用问题 解决方法 参考文章: (1)android studio 使用问题 解决方法 (2)https://www.cnblogs.com/zl1991/p/59613 ...
- Android让APP运行在新环境上,Android Studio环境在真手机运行app项目教程
对于Android Studio环境在真手机运行app项目的相关操作有许多网友咨询过,小编今天就分享Android Studio环境在真手机运行app项目的详细步骤,一起好好学习下吧! 要想将Andr ...
- Android Studio真机调试方法
Android Studio真机调试方法 打开手机USB调试功能 安装Google USB Driver 连接手机,指定驱动程序 选中手机,进行调试 打开手机USB调试功能 以华为Mate 20为例, ...
- Android studio 多渠道版本打包方法 flavor dimension
Android studio 多渠道版本打包方法 flavor dimension 前言 开始 新建项目 修改build.gradle 创建差异化文件 创建图标 修改APP配色 修改APP名字 APP ...
- Android Studio生成错误日志方法
Android Studio生成错误日志方法 Use the Android Debugger to get a crash dump. On the Android device navigate ...
最新文章
- windows上报错:Could not find a version that satisfies the requirement torch==0.4.1
- LeetCode Minimum Depth of Binary Tree
- 本app(仿手机支付宝界面)ios源码
- android adb server is out of date
- Spring Cloud微服务实战:外卖订餐系统
- treelist自动定位行_国内首创!金川集团千米深井双定位补偿摇台投用
- mysql 5.7.6 5.7.19_免编译安装mysql 5.7.19
- iOS-----线程同步与线程通信
- linux 查看日志
- #10015 灯泡(无向图连通性+二分)
- Html中的表格、Table和div,表格页面、员工登记表、Div代码
- [面试题]100层楼丢玻璃球,一旦超过某层就会破,你只有两个球。
- 高中数学知识点:导数的概念及运算(复习+解析+答案)
- m3u8视频下载转为mp4
- 传奇服务器人物技能怎么修改,传奇服务端上线0级技能,直接设置3级技能的设置方法...
- HDOJ 5620-KK's Steel【斐波那契数列】
- PHP日活10万,小程序日活超4亿,近10万商家开通直播,私域红利已来 !
- 十六进制转八进制算法
- windows10 安装 cuda8.0 cudnn6.0 tensorflow-gpu1.3.0 pycharm jupyter 及路径
- DockerScan:Docker安全分析测试工具
热门文章
- android 模仿大众点评团购卷列表多余3条时折叠,点击时显示剩余全部的功能
- 曳舞---1、曳舞各个动作要点
- python错误 ImportError: No module named setuptools 解决方法[转]
- JZ2440开发笔记(4)——设置静态IP
- 从零开始学习Kafka
- Python学习---Python安装与基础1205
- bzoj 2190: [SDOI2008]仪仗队 线性欧拉函数
- FastDFS安装、配置、部署(三)-Storage配置具体解释
- 第一阶段SCRUM冲刺 03
- Linux系统调用--getrlimit()与setrlimit()函数详解