所谓切面编程,就算将一个完整的流程切分成若干个,然后主流程只关心调用的方法,而不关心具体的实现逻辑,而子流程在完成业务逻辑后会把通知主流程方法继续向下执行或者通知失败;说白了就是方法的拦截操作,可以在方法执行前后进行一定的处理,然后根据需求判断是否是真正进入执行该方法或是直接跳过该方法抛出异常之类的,可以类比成OkHttp的拦截器

Aspect是一个实现切面编程的框架,本篇介绍一下Aspect常用的一种方法

配置信息

应用build配置里添加依赖

implementation 'org.aspectj:aspectjrt:1.9.2'

配置脚本信息,这里是固定格式

import org.aspectj.tools.ajc.Mainproject.android.applicationVariants.all { variant ->JavaCompile javaCompile = variant.javaCompilejavaCompile.doLast {String[] args = ["-1.7","-inpath", javaCompile.destinationDir.toString(),"-d", javaCompile.destinationDir.toString(),"-aspectpath", javaCompile.classpath.asPath,"-classpath", javaCompile.classpath.asPath,"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]new Main().runMain(args, false)}
}

root配置

classpath 'org.aspectj:aspectjtools:1.9.2'

代码信息

1.声明一个类,用@Aspect标记,表示这个是个AOP的编译入口,里面定义的方法会被检测编译

@Aspect
public class AspectTest{...
}

2.定义两个注解入口

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestEntrance {String[] params();int code();
}

这是方法入口,就是我们主流程调用的子流程方法会用这个进行标记;然后编译后会插入我们拦截的部分插桩方法,当该方法被调用时候,会优先进入我们的插入的逻辑,在里面判断最终是否执行这个方法或者绕过这个方法,走额外定义的失败方法

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestFail {
}

这是方法失败的标记,上面判断逻辑认为是不符合的我们会手动绕过该方法,反射调用失败的方法

3.定义切入点

 @Pointcut("execution(@com.example.aop.annoation.RequestEntrance void *(..)) && @annotation(request)")public void pointcutSolve(RequestEntrance request) {}

这里的格式说明一下

execution 是定义的切入点执行入口,会根据该括号内的表达式匹配相应的方法;

@com.example.aop.annoation.RequestEntrance 这个上面定义的注解的全路径格式;

void 是匹配的方法的返回值类型;

* 是通配符,表示匹配任意名称的方法,也可以写固定名称或者 xx*等表示xx开头的方法;

这句表达式的意思就是匹配 RequestEntrance标记的所有void的方法;

这是常用的注解标记的方法匹配;

如果不使用注解,则可以定义成  execution(* xx.xx.methodName()) 第一个*表示任意返回参数,后面是方法名的全路径;

后面的 && 符号表示添加一个参数传递,传递的是annotation类型的名字为request的参数,这个名字得和定义的方法中的参数名字保持一致;annotation不能写错;这里的annotation主要是为了获取该注解的参数,比如定义权限申请的场合,会在参数中声明需要授权的权限信息

4.定义拦截方法,就是调用上面的PointCut方法,这里参数名也要保持一致

 @Around("pointcutSolve(request)")public void mainEntrance(ProceedingJoinPoint joinPoint, RequestEntrance request) {String name = joinPoint.getSignature().getName();Log.e("1234", "开始执行代码插入逻辑 -> "+name);Object thisObj = joinPoint.getThis();Object[] args = joinPoint.getArgs();String params[] = request.params();int code = request.code();boolean value = true;if (value) {try {if (args != null && args.length > 0){String[] first = (String[]) args[0];first[0] = "这是替换的文字";}joinPoint.proceed(args);Log.e("1234", "插入逻辑执行结束 -> "+name);} catch (Throwable throwable) {throwable.printStackTrace();}} else {Log.e("1234", "执行插入失败方法 -> "+name);solveFail(thisObj, code, params);}}

拦截的入口使用Around标记;除此之外,Aspect还提供了@Before,@After , @AfterThrowing,  @AfterReturning 方法,分别表示在方法执行前,方法执行后,方法抛出异常后,方法返回值返回后执行;而相对而已,Around能实现上面的所有方法

这里我定义了一个拦截方法,并定义了一个开关可以控制继续方法的执行或直接走失败,如果有参数,会把第一个参数替换掉再去调用原方法

  private void solveFail(Object target, int code, String... params) {try {Method[] methods = target.getClass().getDeclaredMethods();for (Method method : methods) {if (method.isAnnotationPresent(RequestFail.class)) {Class<?>[] paramTypes = method.getParameterTypes();if (paramTypes.length < 2) {throw new RuntimeException("参数个数不正确");}if (!paramTypes[0].isAssignableFrom(int.class)) {throw new RuntimeException("首个必须是int类型");}if (!paramTypes[1].isAssignableFrom(String[].class)) {throw new RuntimeException("第二个必须是数组类型");}method.invoke(target, code, params);}}} catch (Exception e) {e.printStackTrace();}}

失败的方法我通过反射去调用实际的对象中的方法,并把注解中设置的值返回

然后在主页面定义了有注解的三个方法

 @RequestEntrance(params = "t1", code = 1)public void request1() {Log.e(TAG, "request1 方法执行了  " );}@RequestEntrance(params = {"t2", "t3"}, code = 2)public void request2(String... params) {Log.e(TAG, "request2 方法执行了,参数是 " + Arrays.asList(params).toString());}@RequestFailpublic void requsetFail(int code, String... params) {Log.e(TAG, "requestFail " + code +"  "+ Arrays.asList(params).toString());}

分别是一个无参的,一个多个参数的,以及一个失败的方法

先调用第一个request1方法,打印日志是

E/1234: 开始执行代码插入逻辑 -> request1
E/1234: request1 方法执行了
E/1234: 插入逻辑执行结束 -> request1

然后把上面的标记改成false,走失败方法

E/1234: 开始执行代码插入逻辑 -> request1
E/1234: 执行插入失败方法 -> request1
E/1234: requestFail 1  [t1]

可以看出原方法直接绕过没有执行,直接走到了上面定义的requestFail方法,并正确传入了注解的code和param参数

然后调用第二个request2方法,request2("1234","asdf")

打印日志是

E/1234: 开始执行代码插入逻辑 -> request2
E/1234: 执行插入失败方法 -> request2
E/1234: requestFail 2  [t2, t3]

把上面的标记改回true,打印日志是

E/1234: 开始执行代码插入逻辑 -> request2
E/1234: request2 方法执行了,参数是 [这是替换的文字, asdf]
E/1234: 插入逻辑执行结束 -> request2

总结

1.需要声明@Aspect类,标记这里面定义的方法会被检测是否拦截执行方法

2.需要声明@Pointcut注解的方法,在这个注解里会定义所匹配的方法类型,当执行的方法和这里定义的表达式匹配,那么会走到下面@Around定义拦截方法中

3.需要声明@Around注解的方法,这里处理具体拦截的业务逻辑,方法执行前后都可以进行操作,需要定义一个ProceedingJoinPoint参数类型,一般放在第一位;当确定中断拦截,原方法继续执行时,调用这个参数的process方法即可;

4.根据需求定义方法入口和失败的注解类,并标记相应的方法;

调用入口方法, 处理子业务逻辑,处理完毕后继续入口方法的执行,或者绕过该方法走失败的方法回调

Aspect基础使用方法相关推荐

  1. SaltStack介绍——SaltStack是一种新的基础设施管理方法开发软件,简单易部署,可伸缩的足以管理成千上万的服务器,和足够快的速度控制,与他们交流...

    SaltStack介绍和架构解析 简介 SaltStack是一种新的基础设施管理方法开发软件,简单易部署,可伸缩的足以管理成千上万的服务器,和足够快的速度控制,与他们交流,以毫秒为单位.SaltSta ...

  2. Linux 下的NFS server 架设基础及方法

    Linux 下的NFS server 架设基础及方法<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office: ...

  3. 27、Python 面向对象(创建类、创建实例对象、访问属性、内置类属性、对象销毁、类的继承、方法重写、基础重载方法、运算符重载、类属性与方法、下划线双下划线)

    27Python面向对象(Python2) Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的.本章节我们将详细介绍Python的面向对象编程. ...

  4. Linux基础优化方法(四)———远程连接缓慢优化

    Linux基础优化方法(四)---远程连接缓慢优化 一.优化原因 二.优化方法 第一步:修改SSH服务配置文件 /etc/ssh/sshd_config 第二步:修改/etc/hosts配置文件 第三 ...

  5. Linux基础优化方法(三)———字符集编码设置优化

    Linux基础优化方法(三)---字符集编码设置优化 一.什么是字符编码 二.编码GB2312.GBK.UTF-8 三.工作时有乱码的原因 四.进行优化 1.CentOS 6 ①.查看默认编码信息: ...

  6. Linux基础优化方法(二)———系统安全相关优化:防火墙和selinux

    Linux基础优化方法(二)---系统安全相关优化:防火墙和selinux 一.系统防火墙服务优化 1.CentOS 6 ①.查看防火墙服务状态 ②.临时关闭防火墙服务 ③.永久关闭防火墙服务 2.C ...

  7. Linux基础优化方法(一)———优化命令提示符和yum源仓库

    Linux基础优化方法(一)---优化命令提示符和yum源仓库 一.优化命令提示符 1.为什么要优化 2.命令提示符内容 3.优化方法:修改PS1环境变量 ①.修改命令提示内容 ②.命令提示符如何修改 ...

  8. laravel获取当前的url以及当前的基础域名方法汇总

    原文地址:https://phpartisan.cn/news/58.html 来源于:laravel获取当前的url以及当前的基础域名方法汇总 - Laravel学习网 laravel中我们常常需要 ...

  9. vim的安装以及基础使用方法;

    目录 Linux软件包管理器 yum 查看软件包: 如何安装软件: 如何卸载捏,同样是一条命令: Linux编辑器-vim使用: 1.vim的基本概念 2.vim的基本操作 3.vim正常模式命令集 ...

  10. 手游自动化测试基础:方法及流程

    手游自动化测试基础:方法及流程 编写手游测试脚本,功能如下: 测试手游开始结束整个流程 穷举算法,遍历各种情况 利用图形化识别函数,直接点击图片 代码如下: //------------------- ...

最新文章

  1. python numpy 数据类型为python对象-python numPy模块 与numpy里的数据类型、数据类型对象dtype...
  2. encodeURIComponent编码2次
  3. JS——样式类的添加
  4. workaround: 从product category移除settype时绕过check
  5. JS call()和apply()方法和区别
  6. 微软官方Windows主题 英国之美 高分辨率的壁纸
  7. dbforge studio for oracle,dbForge Studio for Oracle(数据库管理软件)官方版
  8. 客户端更新功能实现_exlive1.0|监控客户端功能更新
  9. Python中[index for index, value in enumerate(a) if value > 3]
  10. 面试—每日一题(5)
  11. ​ [RHEL7.1]重新封装系统(制作模板)
  12. Macbook使用技巧:如何在外部显示器上获得4K 60 FPS
  13. mac下selenium+python环境搭建
  14. Dropdownlist插入值!
  15. 重金求购一份回合制手游源码
  16. 词频分析与词频统计在线工具---如何统计文章词频,从而更快了解一篇文章?今天跟大家介绍一个词频分析在线工具
  17. 【转自人人】本科生如何发表论文
  18. 在blog中添加attachments功能 (修改系统Control)
  19. python电影爬虫背景介绍_Python爬虫入门教程01之爬取豆瓣Top电影
  20. u8信息服务器,u8服务器和数据库详细

热门文章

  1. 企业网站建设常用CMS建站系统推荐
  2. 谈心-弱之胜强,柔之胜刚
  3. Badboy提示脚本错误解决方法
  4. 湿敏电阻CM-R / HR202应用 原理图 IO输出交流 AD采样
  5. ERP服务商核心竞争力
  6. [NLP论文阅读]Learned in Translation: Contextualized Word Vectors
  7. 深度学习敲门砖——神经网络
  8. 堆中的off-by-one :ASIS CTF 2016:b00ks
  9. 数据库原理与应用实验3--(数据库的简单查询和连接查询)
  10. 2022年高教社杯全国大学生数学建模竞赛-【比赛规则篇】比赛规则及比赛指导