文章目录

  • 一、定义 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 切面 | 定义切入点 | 逐个处理切入点的各个连接点 )相关推荐

  1. 【AOP 面向切面编程】Android Studio 使用 AspectJ 监控方法运行原理分析

    文章目录 一.查看使用 AspectJ 后生成的 Class 字节码类 二.AspectJ 的本质 一.查看使用 AspectJ 后生成的 Class 字节码类 在 Android Studio 中查 ...

  2. 【错误记录】Android Studio 配置 AspectJ 报错 ( all buildscript {} blocks must appear before any plugins {} )

    文章目录 一.报错信息 二.解决方案 一.报错信息 在 Android Studio 配置 AspectJ 报错 : Build file 'D:\002_Project\002_Android_Le ...

  3. 利用消息机制实现.NET AOP(面向方面编程)--基本概念和实现

    本系列文章主要讲述如何利用.NET的Remoting消息框架实现AOP 本系列文章试图实现三种AOP实现 本系列文章以示例代码为主,对一些概念和原理不会进行过于深入的讨论 这里不谈论AOP在现实开发中 ...

  4. 【走过巨坑】android studio对于jni调用及运行闪退无法加载库的问题解决方案

    [走过巨坑]android studio对于jni调用及运行闪退无法加载库的问题解决方案 参考文章: (1)[走过巨坑]android studio对于jni调用及运行闪退无法加载库的问题解决方案 ( ...

  5. android studio 使用问题 解决方法

    android studio 使用问题 解决方法 参考文章: (1)android studio 使用问题 解决方法 (2)https://www.cnblogs.com/zl1991/p/59613 ...

  6. Android让APP运行在新环境上,Android Studio环境在真手机运行app项目教程

    对于Android Studio环境在真手机运行app项目的相关操作有许多网友咨询过,小编今天就分享Android Studio环境在真手机运行app项目的详细步骤,一起好好学习下吧! 要想将Andr ...

  7. Android Studio真机调试方法

    Android Studio真机调试方法 打开手机USB调试功能 安装Google USB Driver 连接手机,指定驱动程序 选中手机,进行调试 打开手机USB调试功能 以华为Mate 20为例, ...

  8. Android studio 多渠道版本打包方法 flavor dimension

    Android studio 多渠道版本打包方法 flavor dimension 前言 开始 新建项目 修改build.gradle 创建差异化文件 创建图标 修改APP配色 修改APP名字 APP ...

  9. Android Studio生成错误日志方法

    Android Studio生成错误日志方法 Use the Android Debugger to get a crash dump. On the Android device navigate ...

最新文章

  1. windows上报错:Could not find a version that satisfies the requirement torch==0.4.1
  2. LeetCode Minimum Depth of Binary Tree
  3. 本app(仿手机支付宝界面)ios源码
  4. android adb server is out of date
  5. Spring Cloud微服务实战:外卖订餐系统
  6. treelist自动定位行_国内首创!金川集团千米深井双定位补偿摇台投用
  7. mysql 5.7.6 5.7.19_免编译安装mysql 5.7.19
  8. iOS-----线程同步与线程通信
  9. linux 查看日志
  10. #10015 灯泡(无向图连通性+二分)
  11. Html中的表格、Table和div,表格页面、员工登记表、Div代码
  12. [面试题]100层楼丢玻璃球,一旦超过某层就会破,你只有两个球。
  13. 高中数学知识点:导数的概念及运算(复习+解析+答案)
  14. m3u8视频下载转为mp4
  15. 传奇服务器人物技能怎么修改,传奇服务端上线0级技能,直接设置3级技能的设置方法...
  16. HDOJ 5620-KK's Steel【斐波那契数列】
  17. PHP日活10万,小程序日活超4亿,近10万商家开通直播,私域红利已来 !
  18. 十六进制转八进制算法
  19. windows10 安装 cuda8.0 cudnn6.0 tensorflow-gpu1.3.0 pycharm jupyter 及路径
  20. DockerScan:Docker安全分析测试工具

热门文章

  1. android 模仿大众点评团购卷列表多余3条时折叠,点击时显示剩余全部的功能
  2. 曳舞---1、曳舞各个动作要点
  3. python错误 ImportError: No module named setuptools 解决方法[转]
  4. JZ2440开发笔记(4)——设置静态IP
  5. 从零开始学习Kafka
  6. Python学习---Python安装与基础1205
  7. bzoj 2190: [SDOI2008]仪仗队 线性欧拉函数
  8. FastDFS安装、配置、部署(三)-Storage配置具体解释
  9. 第一阶段SCRUM冲刺 03
  10. Linux系统调用--getrlimit()与setrlimit()函数详解