AOP 与 OOP 有什么区别,谈谈 AOP 的原理是什么,腾讯 T2 大牛亲自讲解
二、AOP 代码注入时机
代码注入主要注解机制,根据注解时机的不同,主要分为运行时、加载时和编译时。
运行时:你的代码对增强代码的需求很明确,比如,必须使用动态代理(这可以说并不是真正的代码注入)。 加载时:当目标类被 Dalvik 或者 ART 加载的时候修改才会被执行。这是对 Java 字节码文件或者 Android 的 dex 文件进行的注入操作。 编译时:在打包发布程序之前,通过向编译过程添加额外的步骤来修改被编译的类。aspect 切面编程正是运用到编译时
###三、AOP 的几种实现方式
Java 中的动态代理,运行时动态创建 Proxy 类实例
APT,注解处理器,编译时生成 .java 代码
Javassist for Android:一个移植到 Android 平台的非常知名的操纵字节码的 java 库,对 class 字节码进行修改
AspectJ:和 Java 语言无缝衔接的面向切面的编程的扩展工具(可用于 Android)。
###四,Android 中使用 AspectJ
代表项目:Hugo(打印每个方法的执行时间) sa-sdk-android(全埋点技术)
#####(1)原理
AspectJ 意思就是 Java 的 Aspect,Java 的 AOP。它的核心是 ajc(编译? aspectjtools)和 weaver(织入? aspectjweaver)。
ajc 编译?:基于 Java 编译?之上的,它是用来编译.aj 文件,aspectj 在 Java 编译?的基础上增加了一些它自己的关键字和方法。因此,ajc 也可以编译 Java 代码。
weaver 织入?:为了在 java 编译?上使用 AspectJ 而不依赖于 Ajc 编译?,aspectJ 5 出现了 @AspectJ,使用注释的方式编写 AspectJ 代码,可以在任何 Java 编译?上使用。 由于 AndroidStudio 默认是没有 ajc 编译?的,所以在 Android 中使用 @AspectJ 来编写。它在代码的编译期间扫描目标程序,根据切点(PointCut)匹配,将开发者编写的 Aspect 程序编织(Weave)到目标程序的.class 文件中,对目标程序作了重构(重构单位是 JoinPoint),目的就是建立目标程序与 Aspect 程序的连接(获得执行的对象、方法、参数等上下文信息),从而达到 AOP 的目的。
#####(2)AspectJ 术语
切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。其实就是共有功能的实现。如日志切面、权限切面、事务切面等。
通知(Advice):是切面的具体实现。以目标方法为参照点,根据放置的地方不同,可分为
前置通知(Before)、
后置通知(AfterReturning)、
异常通知(AfterThrowing)、
最终通知(After)
环绕通知(Around)5 种。
在实际应用中通常是切面类中的一个方法,具体属于哪类通知由配置指定的。
切入点(Pointcut):用于定义通知应该切入到哪些连接点上。不同的通知通常需要切入到不同的连接点上,这种精准的匹配是由切入点的正则表达式来定义的。?连接点(JoinPoint):就是程序在运行过程中能够插入切面的地点。例如,方法调用、异常抛出或字段修改等。
目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。这些对象中已经只剩下干干净净的核心业务逻辑代码了,所有的共有功能等代码则是等待 AOP 容器的切入。
AOP 代理(AOP Proxy):将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象的功能等于目标对象的核心业务逻辑功能加上共有功能。代理对象对于使用者而言是透明的,是程序运行过程中的产物。
编织(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译期、类装载期及运行期,当然不同的发生点有着不同的前提条件。譬如发生在编译期的话,就要求有一个支持这种 AOP 实现的特殊编译器(如 AspectJ 编译器);
发生在类装载期,就要求有一个支持 AOP 实现的特殊类装载器;只有发生在运行期,则可直接通过 Java 语言的反射机制与动态代理机制来动态
实现(如摇一摇)。
**引入(Introduction):**添加方法或字段到被通知的类。
######(3)在 Android 项目中使用 AspectJ
gradle 配置的方式:引入 AspectJ 是有点复杂的,需要引入大量的 gradle 命令配置有点麻烦,在 build 文件中添加了一些脚本,文章出处:[https://fernandocejas.com/2014/08/03/aspect-oriented-programming-in-android/](
)
使用 gradle 插件(也是对 gradle 命令进行了包装):Jake Wharton 大神的 hugo 项目(一款日志打印的插件)
上海沪江团队的 gradle_plugin_android_aspectjx 一个基于 AspectJ 并在此基础上扩展出来可应用于 Android 开发平台的 AOP 框架,可作用于 java 源码,class 文件及 jar 包,同时支持 kotlin 的应用。
AOP 的用处非常广,从 spring 到 Android,各个地方都有使用,特别是在后端,Spring 中已经使用的非常方便了,而且功能非常强大,但是在 Android 中,AspectJ 的实现是略阉割的版本,并不是所有功能都支持,但对于一般的客户端开发来说,已经完全足够用了。
#####(4)以 AspectJX 接入说明
首先,需要在项目根目录的 build.gradle 中增加依赖:
buildscript {repositories {jcenter()}dependencies {classpath 'com.android.tools.build:gradle:2.3.3'classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'}}
然后 module 项目的 build.gradle 中加入 AspectJ 的依赖:
apply plugin: 'android-aspectjx'dependencies {compile 'org.aspectj:aspectjrt:1.8.+'}
aspectjx {//排除所有 package 路径中包含android.support
的 class 文件及库(jar 文件)exclude 'org.apache.httpcomponents'exclude 'android.support'}
我们通过一段简单的代码来了解下基本的使用方法和功能,新建一个 AspectTest 类文件,代码如下:
@Aspectpublic class AspectTest {
private static final String TAG = "xuyisheng";
@Before("execution(* android.app.Activity.on**(..))")public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {String key = joinPoint.getSignature().toString();Log.e(TAG, "onActivityMethodBefore: " + key);}
@After("execution(* android.app.Activity.on**(..))")public void onActivityMethodAfter(JoinPoint joinPoint) throws Throwable {String key = joinPoint.getSignature().toString();Log.e(TAG, "onActivityMethodAfter: " + key);}
@Around("execution(* android.app.Activity.on**(..))")public void onActivityMethodAfter(ProceedingJoinPoint joinPoint) throws Throwable {String key = joinPoint.getSignature().toString();Log.e(TAG, "onActivityMethodBefore: " + key);joinPoint.proceed();Log.e(TAG, "onActivityMethodAfter: " + key);}}
在类的最开始,我们使用 @Aspect 注解来定义这样一个 AspectJ 文件,编译器在编译的时候,就会自动去解析,并不需要主动去调用 AspectJ 类里面的代码。
(5)编织速度优化建议
尽量使用精确的匹配规则,降低匹配时间。
评论