AOP之aspectj

AOP之aspectj

aop,英文全称为Aspect Oriented Programming,意思是面向切面编程,是一种高内聚,低耦合的编程思想。在很多业务中都有广泛的应用。

业务场景

有这么一个需求,要统计activity中的oncreate方法耗时,为后续做卡顿优化提供数据支撑的基础。

  • 一般实现,在每个activity的oncreate方法中添加统计耗时代码,比如
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        long start = System.currentTimeMillis();//1
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, " spend time =" + (System.currentTimeMillis() - start));//2

    }

想象一下,每个activity中的oncreate方法都多出1,2步骤的代码,如果是所有activity,所有的生命周期都要统计呢,是不是感觉这个工作很繁琐,并且重复,无意义。程序员是比较懒的一群生物,他会想办法偷懒,所以,aspectj这时候就出现了。

有aspectj后的业务代码

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
    }

如果有了aspectj技术以后,那么业务程序员还是一如既往的正常编写业务代码,无需添加任何统计代码,那么统计的需求如何实现呢。为了让例子更简单易懂,我们这里还是只统计oncreate,避免代码太多,所带来的迷糊困惑

aspectj

引入

伟大的开源社区其实有非常好的aspectj的sdk,这里我推荐hujiang,开源地址https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx
该项目是在注解的基础上实现aop的,记住是注解,这是他的巨大优势(易上手),同时也有点缺陷(有一定的业务代码入侵)

开始

代码工程地址demo

  • 首先在项目工程的build.gradle中引入配置
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
  • 然后再在app中的build.gradle中引入
implementation "org.aspectj:aspectjrt:1.8.9"
apply plugin: 'android-aspectjx'
aspectjx {
    include 'com.example'
}

然后就可以开始愉快的编写aop代码了

aspectj代码

@Aspect
public class SpendTimeAspect {
    @Pointcut("execution(* *.onCreate(..))")
    public void injectSpendCode() {
        //生命所有的类中oncreate方法,作为切入点
    }

    @Around("injectSpendCode()")
    public void aroundOnCreate(ProceedingJoinPoint joinPoint) throws Throwable {
        //插入的代码
        long start = System.currentTimeMillis();
        //执行原来的代码
        Object[] params = joinPoint.getArgs();
        joinPoint.proceed(params);
        //插入的代码
        Log.d("SpendTimeAspect","method spend "+(System.currentTimeMillis()-start));
    }

}

讲解一下代码步骤

  1. 首先新建一个Java,名字自己取就好,这里是SpendTimeAspect
  2. 使用注解@Aspect来声明类,这里的作用是告诉编译器,这个类要插桩行为
  3. 使用注解@Pointcut来声明哪个类哪个方法要进行插桩,规则匹配支持通配符。其中*.onCreate(..)的意思就是所有类的oncreate方法要进行插桩,该方法无需任何实现
  4. 使用注解@Around来声明要插入的代码,就像以前一样正常编写java,这里的代码都会被插桩过去。

代码效果大家可以拷贝工程下来运行看一下,有兴趣研究aspectj源码的同学,可以继续深究一下,你会发现aspectj的实现也是asm来实现的,所以asm是个更加强大的aop实现技术方案,只不过入门门槛高。后续也会有文章来讲解这个,然后hujiang这个插件在aspectj基础上,配合google的transform流程

aspectj注解语法

其实,大多数业务情况,掌握我上面所用到的例子中的语法就可以完成很多很多黑科技了。本着文章的完整性,还是系统性讲一次语法相关的吧。

  • @Aspect 用它声明一个类,表示一个需要执行的切面。
  • @Pointcut 声明一个切点。
  • @Before/@After/@Around/@AfterReturning/@AfterThrowing(统称为Advice类型) 声明切面代码的执行顺序,建议使用around,可以满足大部分需求

@Aspect

直接声明一个类,这样子才会执行这个类里面的插桩语法

@Pointcut

指定了一个代码织入点,注解内的execution(* .onCreate(..))是一个切点表达式,第一个号表示返回值可为任意类型,后跟包名+类名+方法名(可以用*代表全匹配的意思),括号内表示参数列表, .. 表示匹配任意个参数

  • Method call:方法被调用
  • Method execution:方法执行
  • Constructor call:构造函数被调用
  • Constructor execution:构造函数执行
  • Static initialization:static 块初始化
  • Field get:读取属性
  • Field set:写入属性
  • Handler:异常处理

@Advice

  • @Before:切入点前织入
  • @After:切入点后织入,无论连接点执行如何,包括正常的 return 和 throw 异常
  • @AfterReturning:只有在切入点正常返回之后才会执行,不指定返回类型时匹配所有类型
  • @AfterThrowing:只有在切入点抛出异常后才执行,不指定异常类型时匹配所有类型
  • @Around:替代原有切点,如果要执行原来代码的话,调用 ProceedingJoinPoint.proceed()

语法大全

语法手册

总结

aspectJ是一个容易上手来实现aop的技术方案,赶紧动起小手,自己一步一步的写个demo调试一下。也欢迎在我的demo基础上,动手实验.


希望我的文字能给你带来帮助,同时也欢迎关注个人公众号,我的所有文章第一手信息回优先发布这里。
个人公众号:河边的小黑屋

AOP之aspectjAOP之aspectj 河边的小黑屋 发布了8 篇原创文章 · 获赞 5 · 访问量 9979 私信 关注
上一篇:java-如何获取带注释的方法参数及其注释


下一篇:题如何在Gradle项目中使用带有Profiled注释的Perf4J?