AOP是什么
概念:AOP是Aspect Oriented Programming的缩写,即『面向切面编程』;切面编程,就是在你项目原有的功能基础上,通过AOP去添加新的功能,这些功能是建立在原有功能的基础上的,而且原有的功能并不知道你已经添加了新的功能;AOP就是在某一个类或方法执行前后打个标记,声明在执行到这里之前要先执行什么,执行完这里之后要接着执行什么。插入了新的执行方法。
AOP和OOP的不同
OOP,即『面向对象编程』,它提倡的是将功能模块化,对象化,而AOP的思想,则不太一样,它提倡的是针对同一类问题的统一处理,当然,我们在实际编程过程中,不可能单纯的安装AOP或者OOP的思想来编程,很多时候,可能会混合多种编程思想,大家也不必要纠结该使用哪种思想,取百家之长,才是正道。
AOP的使用场景
主要用于不想侵入原有代码的场景中,例如SDK需要无侵入的在宿主中插入一些代码,做日志埋点、性能监控、数据校验、持久化、动态权限控制、甚至是代码调试等等。
什么是AspectJ
AspectJ实际上是对AOP编程思想的一个实践,当然,除了AspectJ以外,还有很多其它的AOP实现,例如ASMDex,但目前最好、最方便的,依然是AspectJ。
原理图
图片来自https://www.jianshu.com/p/0fa8073fd144
工作原理.png
首先谈谈AspectJ相关的几个概念点
- Join Points(连接点) Join Points可以看作是程序运行时的一个执行点,比如:一个函数的调用可以看作是个Join Points,如Log.d()这个函数,d()可以看作是个Join Points,而调运d()的函数也可以认为是一个Join Points;设置一个变量,或者读取一个变量也可以是个Join Points;for循环也可以看作是Join Points。可以是函数,构造方法等
Join Points.png
- Pointcuts(切入点) 一个程序会有多个Join Points,即使同一个函数,也还分为call和execution类型的Join Points,但并不是所有的Join Points都是我们关心的,Pointcuts就是提供一种使得开发者能够选择自己需要的JoinPoints的方法;或者说是程序中可能作为代码注入目标的特定的点,例如一个方法调用或者方法入口。
image.png 以上的 Signature 都是由一段表达式组成,且每个关键词之间都有“空格”,下面是对关键词的解释:
image.png
- Advice Advice,也就是具体的插入点。典型的 Advice 类型有 before、after 和 around,分别表示在目标方法执行之前、执行后和完全替代目标方法执行的代码。
image.png 看个例子:
代码语言:javascript复制@Before("execution(* android.app.Activity.on**(..))")
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
}
这里会分成几个部分,我们依次来看: --- @Before:Advice,也就是具体的插入点 --- execution:处理Join Point的类型,例如call、execution --- (* android.app.Activity.on(..)):这个是最重要的表达式,第一个『』表示返回值,『』表示返回值为任意类型,后面这个就是典型的包名路径,其中可以包含『』来进行通配,几个『』没区别。同时,这里可以通过『&&、||、!』来进行条件组合。()代表这个方法的参数,你可以指定类型,例如android.os.Bundle,或者(..)这样来代表任意类型、任意个数的参数。 --- public void onActivityMethodBefore:实际切入的代码。
应用
在Android项目中使用AspectJ
在android中配置aspectj是特别麻烦的,目前市场上流行的一款在Android使用的插件 gradle_plugin_android_aspectjx
如何在Android studio配置gradle_plugin_android_aspectjx的插件
- 项目根目录的build.gradle中增加依赖:classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
buildscript {
repositories {
google()
jcenter(){
url 'http://jcenter.bintray.com/'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1' //添加这一行
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter(){
url 'http://jcenter.bintray.com/'
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
- 在主项目或者库的build.gradle中增加AspectJ的依赖:api 'org.aspectj:aspectjrt:1.8.9' //相关api的引入
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api 'org.aspectj:aspectjrt:1.8.9'
...
}
- 在主项目的build.gradle中添加:apply plugin: 'android-aspectjx'
apply plugin: 'com.android.application'
apply plugin: 'android-aspectjx'
- Aspect的实现类
package com.jason.aspectj;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
import com.jason.aspectj.tools.StopWatch;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
/**
* Description:AspectActivityTest
*
* @author Chenby
* @create 2019/10/10 14: 25
*/
@Aspect
public class AspectActivityTest {
/**
* 重要的概念知识
*/
//Join Points,简称JPoints,是AspectJ的核心思想之一,它就像一把刀,把程序的整个执行过程切成了一段段不同的部分
//Advice,Advice其实是最好理解的,也就是我们具体插入的代码,以及如何插入这些代码
//@Before:Advice,也就是具体的插入点, 常见的有:Before,After,Around
//execution:处理Join Point的类型,例如call、execution
//execution是在被切入的方法中,call是在调用被切入的方法前或者后
//Call(Before)
//Pointcut{
// Pointcut Method
//}
//Call(After)
//Pointcut{
// execution(Before)
// Pointcut Method
// execution(After)
//}
//(* android.app.Activity.on**(..)):这个是最重要的表达式,第一个『*』表示返回值,『*』表示返回值为任意类型,后面这个就是典型的包名路径,
// 其中可以包含『*』来进行通配,几个『*』没区别。同时,这里可以通过『&&、||、!』来进行条件组合。()代表这个方法的参数,你可以指定类型,
// 例如android.os.Bundle,或者(..)这样来代表任意类型、任意个数的参数。
private static final String TAG = "Chenby";
/**
* advice 采用before的例子, Joint Point的类型是execution Before 表示在方法之后插入代码
*
* @throws Throwable
*/
@Before("execution(* android.app.Activity.on**(..))")
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
String key = joinPoint.getSignature().toString();
Log.d(TAG, "onActivityMethodBefore: " key);
}
/**
* advice 采用after的例子, Joint Point的类型是execution After 表示在方法之后插入代码
*
* @throws Throwable
*/
@After("execution(* android.app.Activity .on*(android.os.Bundle))")
public void onActivityMethodAfter(JoinPoint joinPoint) throws Throwable {
String key = joinPoint.getSignature().toString();
Log.d(TAG, "onActivityMethodAfter: " key);
}
/**
* advice 采用 Around的例子, Joint Point的类型是execution, Around表示在方法前后各插入代码
*
* @throws Throwable
*/
@Around("execution(* com.cby.mvvmdemo.ui.mine.MinePageFragment.testAOP(*))")
public void onFragmentMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String key = proceedingJoinPoint.getSignature().toString();
Log.d(TAG, "onFragmentMethodAround: " key);
//表示原有方法的执行
proceedingJoinPoint.proceed();
Log.d(TAG, "onFragmentMethodAround: " key);
}
@Around("execution(* com.cby.mvvmdemo.ui.mine.MinePageFragment.testAOP(android.content.Context))")
public void onMethodAroundToast(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Context mContext = null;
//proceedingJoinPoint.getThis()可以获取到调用该方法的对象
if (proceedingJoinPoint.getThis() instanceof Context) {
mContext = (Context) proceedingJoinPoint.getThis();
} else {
//proceedingJoinPoint.getArgs()可以获取到方法的所有参数
for (Object context : proceedingJoinPoint.getArgs()) {
if (context instanceof Context) {
mContext = (Context) context;
break;
}
}
}
if (mContext == null) {
return;
}
proceedingJoinPoint.proceed();
Toast.makeText(mContext.getApplicationContext(), "Test show toast for aop!", Toast.LENGTH_SHORT)
.show();
}
private static final String POINTCUT_METHOD =
"execution(@com.jason.aspectj.custom.DebugTool * *(..))";
private static final String POINTCUT_CONSTRUCTOR =
"execution(@com.jason.aspectj.custom.DebugTool *.new(..))";
@Pointcut(POINTCUT_METHOD)
public void methodAnnotatedWithDebugTrace(){}
@Pointcut(POINTCUT_CONSTRUCTOR)
public void constructorAnnotatedDebugTrace() {}
@Around("methodAnnotatedWithDebugTrace() || constructorAnnotatedDebugTrace()")
public Object onDebugToolMethodAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
final StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed();
stopWatch.stop();
Log.i(className, buildLogMessage(methodName, stopWatch.getTotalTimeMillis()));
return result;
}
/**
* Create a log message.
*
* @param methodName A string with the method name.
* @param methodDuration Duration of the method in milliseconds.
* @return A string representing message.
*/
private static String buildLogMessage(String methodName, long methodDuration) {
StringBuilder message = new StringBuilder();
message.append("Chenby --> ");
message.append(methodName);
message.append(" --> ");
message.append("[");
message.append(methodDuration);
message.append("ms");
message.append("]");
return message.toString();
}
@AfterReturning(value = "execution(* com.cby.mvvmdemo.z_test.activities.AopTestActivity.AfterReturning*(..))", returning = "num")
public void testAspectAfterReturning(int num) {
Log.e(TAG, "AfterReturning-num:" num);
}
}
/**
* proceed(ibject[] args)的使用
*/
@Around("execution(* com.jason.aspectj*.many*(..))")
public Object process(ProceedingJoinPoint point) throws Throwable {
System.out.println("@Around:执行目标方法之前...");
//访问目标方法的参数:
Object[] args = point.getArgs();
if (args != null && args.length > 0 && args[0].getClass() == String.class) {
args[0] = "改变后的参数1";
}
//用改变后的参数执行目标方法
Object returnValue = point.proceed(args);
System.out.println("@Around:执行目标方法之后...");
System.out.println("@Around:被织入的目标对象为:" point.getTarget());
return "原返回值:" returnValue ",这是返回结果的后缀";
}
- 自定义的注解
package com.jason.aspectj.custom;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Description:DebugTool :自定义AOP的注解
*
* @author Chenby
* @create 2019/10/10 16: 31
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface DebugTool {
}
结语
以上是借鉴了网上的文章和自己的一些理解关于AOP的AspectJ的编程的想法,如有错误欢迎评论留言指出
参考文献
https://www.jianshu.com/p/0fa8073fd144 https://blog.csdn.net/eclipsexys/article/details/54425414 https://www.cnblogs.com/yxx123/p/6665736.html