目录
一、直接跑demo看效果
二、效果引发的思考----盲猜
三、配置一下库----AspectJ
四、动手修改注解试试?
五、道友们,我想试试批量追踪拦截
六、 21世纪了-----智能批量拦截
七、 表达式通配符之----模糊过滤
八、表达式之---再次挖掘
九、还有更有意思的---指哪打哪(指哪拦截哪?)
十、又盲推了一波
十一、真正的拦截
十二、拦截的,到底是个啥玩意
十三、我想尝试一览全貌
十四、示例源码
AOP是Aspect Oriented Programming的缩写,中文为面向切面编程。
那么啥是Aop?在这里,我暂时先不提AOP 的相关的概念,那些什么 Joinpoint(连接点) ,Pointcut(切入点),Advice(通知),Weaving(织入),Aspect(切面),听着让人懵。我打算先演示一下,AOP 能做些什么。ok,直入主题。
一、直接跑demo看效果
新建一个 MainActivity ,代码如下
package com.kabun.aopdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initData();
setContentView(R.layout.activity_main);
}
private void initData() {
GyLog.d("initData");
}
}
上面的代码只有一个功能,就是把 initData 通过日志打印出来。如果说,现在我想在 initData() 这个方法调用之前,增加并调用一个方法 beforeInitData(), 我应该怎么做呢?我是不是应该要在代码里添加一个方法 beforeInitData() ,并在 initData() 之前调用它,例如这样
package com.kabun.aopdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
beforeInitData();
initData();
setContentView(R.layout.activity_main);
}
private void beforeInitData() {
GyLog.d("beforeInitData");
}
private void initData() {
GyLog.d("initData");
}
}
但是,如果实现以上同样的效果,AOP 是怎么做到的呢?首先,咱们新建一个类 SectionAspect ,类的代码如下
package com.kabun.aopdemo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class SectionAspect {
@Before("execution(* com.kabun.aopdemo.MainActivity.initData(..))")
public void beforeInitData() {
GyLog.d("beforeInitData");
}
}
运行一下,发现 类 SectionAspect 里的 beforeInitData() 被调用了,也就是说,实现了和刚刚在 MainActivity 里面同样的效果
2021-10-01 17:21:27.213 9830-9830/com.kabun.aopdemo D/AopDemo:SectionAspect.beforeInitData(L:25): beforeInitData 2021-10-01 17:21:27.214 9830-9830/com.kabun.aopdemo D/AopDemo:MainActivity.initData(L:27): initData
二、效果引发的思考----盲猜
那么为啥呢?为啥 SectionAspect 里的 beforeInitData() 会在 MainActivity 里的 initData() 之前调用呢?这里面其实隐含了两个需求:
1、beforeInitData() 被调用了
2、beforeInitData() 在 MainActivity 里的 initData() 之前调用
那么,我们先看下 SectionAspect 里有什么东西先。先看这个类的修饰符 public 上方有个注解:@Aspect ,慢着, Aspect 是个啥?中文翻译过来就是方面、层面的意思。是不是感觉和今天的主题遥遥呼应?(毕竟都有“面”啊)啧啧。
ok,直接再看下一个东西,也是个注解: @Before ,就是在啥啥啥之前的意思,嗯?是不是有感觉和刚刚的需求 (在 initData() 这个方法调用之前,增加并调用一个方法 )又遥遥呼应了?
行, 再看 @Before 后面接着的一串东西: ("execution(* com.kabun.aopdemo.MainActivity.initData(..))") ,execution ? 有点执行的意思对吧 同时,你会发现,com.kabun.aopdemo.MainActivity.initData 这个是不是方法 initData 的全路径呢?这就是一种什么感觉呢?感觉给人意思就是,通过这串全路径内容,程序知道了 initData 的具体位置,毕竟已经精确到类里面了,当然,类里面可能有参数不同但是名字相同的方法,排除下调用错了方法的可能,但这已经是后面才要了解的内容了。
三、配置一下库----AspectJ
好像缺了介绍 AspectJ 毕竟这是属于第三方的东西,需要导入的。那这个东西在哪里获取呢?点击链接 AspectJ Downloads | The Eclipse Foundation
我目前测试示例使用的是 1.8.10 版,然后点击 aspectj-1.8.8.jar (~15M) 下载即可。
下载完之后,打开 jar 包,一路点击下一步,自动安装完毕后。这个东西默认安装在C盘,默认情况下找到如下的目录
记住图中红框中的这货,等下要用到它
将上图红框的 aspectjrt.jar 文件放入 libs 目录内,然后在 app 目录下的 build.gradle 文件下,写入以下内容
plugins {
id 'com.android.application'
}
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
android {
compileSdkVersion 30
defaultConfig {
applicationId "com.kabun.aopdemo"
minSdkVersion 22
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.8",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
log.debug "ajc args: " + Arrays.toString(args)
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation files('libs/aspectjrt.jar')
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
接着还要在项目 project 下的 build.gradle 里补充依赖
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.1"
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
然后构建一下,Aspect 库就导入项目了
四、动手修改注解试试?
回到刚才那个引发思考的各个点,我们已经在猜测 SectionAspect 中的注解可能有关系的各个部分。那么,现在我们可以尝试去修改一下代码,看看效果。我们首先尝试改一下beforeInitData() 执行顺序,那怎么呢?我们刚猜测是注解 @Before 使得 beforeInitData() 方法在 MainActivity 的 initData() 方法前执行。那么,改一下这个,起码看看还有没有其他的注解可以选
如上图所示,我们可以轻易发现在before的前面有after这么个东西,存在的就是合理的,既然看到了,那就得试试,将 before 改成 after ,然后再跑一下
从日志里,可以看出,在将 before 改成 after 后,beforeInitData() 方法在 MainActivity 的 initData() 方法后执行了。有意思是吧?当然还有其他的注解可以选择尝试,但在这里不一一展示了,大家可以自己尝试一波。在这里我们也许可以猜测到的是,无论是 before 还是 after 注解,它们都可以改变方法的执行顺序,也可以叫方法执行的时机,就是方法在什么时候执行。
接下来,将目标转移到 after(负责方法执行时机)这个注解后面的那串东西,让我们尝试更改它。这串东西在刚才,我们对它的作用是定位在负责跟踪在哪个方法前后执行。例如现在
是在跟踪 com.kabun.aopdemo.MainActivity.initData() 这个方法。那么,问题来了,假设我把initdata这个方法改成 initData(String a),多了一个参数呢?再跑下看看日志
很明显,beforeInitData() 这个方法没被调用,网上查了下才知道,原来啊,如果你要追踪的这个目标方法如果有参数的话,理论上你也应该要在注解里标明相应的参数,不然匹配不上。但问题 是,怎么改?直接照搬?尝试发现结果如下:
不行的,因为不用传参数名,只要传类型即可,像这样就可以了
至于为啥,语法规定呗
我们再换个角度思考,如果我们要追踪 MainActivity 的onCreate 方法呢?直接改成这样
com.kabun.aopdemo.MainActivity.onCreate(Bundle) ?跑一下嘛
结果明显,跑不通,为啥呢?oncreate 方法特殊在于首先它是一个重写的方法,然后参数Bundle不是java 的基本数据类型,这是我暂时能想到的不同的地方,但是,是不是因为这两个地方影响了我们找到它,还不确定。所以,我们另想出路。
五、道友们,我想试试批量追踪拦截
先抛砖引玉一下, 大家想一想一个问题,如果某天我们想要追踪MainActivity 里面的所有方法呢?意思是在 MainActivity 的所有方法后,都执行 SectionAspect 的 beforeInitData() 方法。按照之前的套路的话,可能得要依葫芦画瓢,一个方法对应一个注解方法....
@Aspect
public class SectionAspect {
@After("execution(* com.kabun.aopdemo.MainActivity.initData())")
public void afterInitData() {
GyLog.d("afterInitData");
}
@After("execution(* com.kabun.aopdemo.MainActivity.initData1())")
public void afterInitData1() {
GyLog.d("afterInitData1");
}
@After("execution(* com.kabun.aopdemo.MainActivity.initData2())")
public void afterInitData2() {
GyLog.d("afterInitData2");
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initData1();
initData2();
}
private void initData() {
GyLog.d("initData");
}
private void initData1() {
GyLog.d("initData1");
}
private void initData2() {
GyLog.d("initData2");
}
}
日志打印如下
2021-10-02 10:35:37.964 2445-2445/com.kabun.aopdemo D/AopDemo:MainActivity.initData(L:24): initData 2021-10-02 10:35:37.964 2445-2445/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData 2021-10-02 10:35:37.964 2445-2445/com.kabun.aopdemo D/AopDemo:MainActivity.initData1(L:27): initData1 2021-10-02 10:35:37.964 2445-2445/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData1(L:26): afterInitData1 2021-10-02 10:35:37.964 2445-2445/com.kabun.aopdemo D/AopDemo:MainActivity.initData2(L:30): initData2 2021-10-02 10:35:37.964 2445-2445/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData2(L:31): afterInitData2
可以发现, SectionAspect 收到注解after的影响下,确实每个方法都相对应地 MainActivity 相对应的方法后面运行了。但是这样的话,得多心累啊....
六、21世纪了-----智能批量拦截
于是乎,经过网上的一番搜索,终于得知,原来这个东西有点类似正则表达式之类的原理,里面也是有类似通配符的东西可以使用的。例如我们上面讨论的需求,是要在每个MainActivity 调用过的方法之后,通过Aspect 去自动调用 SectionAspect 的 afterInitData() 方法,意思就是说,下面这行代码可以实现和上面日志一样的效果:
@Aspect
public class SectionAspect {
@After("execution(* com.kabun.aopdemo.MainActivity.*())")
public void afterInitData() {
GyLog.d("afterInitData");
}
}
如上的代码所示,我只写了一个 afterInitData 方法,关键是看注解那里,我将之前的
@After("execution(* com.kabun.aopdemo.MainActivity.initData())")
改成了现在的
@After("execution(* com.kabun.aopdemo.MainActivity.*())")
大家留意到了么,最后的initData 被我用星号 * 代替了,星号在计算机里一般可以用来用作通配符,就是这个星号代表全部方法的意思,毕竟一般星号用来代表全部的意思嘛
然后,看运行结果
你看,这效果不就出来了么,(不过,事情好像并没有完全如我们想象的去发生,afterInitData 方法只被调用了3次,明显oncreate方法后面没有触发它,为啥呢,后面讲解),看这个星号的作用还是不错的
七、表达式通配符之----模糊过滤
你以为这就完了?哈哈哈,当然不!~ 既然星号是可以代表所有的意思,但是,如果我在星号前后加点东西呢?例如我先假设星号是通配的意思的话,那么通过以往的经验去联想, init* 是否可以代表以 init 字符串开头的方法名呢?例如 initData 这个方法,我试试改下代码运行下:
@Aspect
public class SectionAspect {
@After("execution(* com.kabun.aopdemo.MainActivity.init*())")
public void afterInitData() {
GyLog.d("afterInitData");
}
}
运行效果:
2021-10-02 11:24:53.692 3994-3994/com.kabun.aopdemo D/AopDemo:MainActivity.initData(L:34): initData 2021-10-02 11:24:53.693 3994-3994/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData 2021-10-02 11:24:53.693 3994-3994/com.kabun.aopdemo D/AopDemo:MainActivity.initData1(L:37): initData1 2021-10-02 11:24:53.693 3994-3994/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData 2021-10-02 11:24:53.693 3994-3994/com.kabun.aopdemo D/AopDemo:MainActivity.initData2(L:40): initData2 2021-10-02 11:24:53.693 3994-3994/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData
你看,效果如初,不信,再试试改成这样
@Aspect
public class SectionAspect {
@After("execution(* com.kabun.aopdemo.MainActivity.*1())")
public void afterInitData() {
GyLog.d("afterInitData");
}
}
我想要的是,在 MainActivity 中 以字符串 “1” 结尾的方法名的方法,那么在 MainActivity 里面,只有 initData1() 这个方法符合描述,那么,我们可以先猜测下运行结果, SectionAspect 里的 afterInitData 日志只会打印一次,而且是打印在 MainActivity 里面的 initData1() 执行之后,然后,我们跑一下看下效果:
see? 至于其他的通配效果不在这里一一论证,留给大家去玩,然后,我想问的是,刚我们在尝试在MainActivity 里的 onCreate 方法后,通过Aspect 的特性调用 SectionAspect 里的 afterInitData 方法时,通过如下的表达式
@After("execution(* com.kabun.aopdemo.MainActivity.onCreate(Bundle) )")
无法触发 afterInitData 被调用,现在我们知道有星号这个通配东西了,尝试应用在解决 onCreate 方法后无法触发方法调用的问题上,代码如下:
@Aspect
public class SectionAspect {
@After("execution(* com.kabun.aopdemo.MainActivity.on*())")
public void afterInitData() {
GyLog.d("afterInitData");
}
}
同时改一下 MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initData1();
initData2();
GyLog.d("after onCreate");
}
@Override
protected void onResume() {
super.onResume();
GyLog.d("after onResume");
}
@Override
protected void onStart() {
super.onStart();
GyLog.d("after onStart");
}
private void initData() {
GyLog.d("initData");
}
private void initData1() {
GyLog.d("initData1");
}
private void initData2() {
GyLog.d("initData2");
}
}
首先我们的表达式的预期效果是什么?
@After("execution(* com.kabun.aopdemo.MainActivity.on*())")
是不是欲求在 MainActivity 里面的所有以 on字符串 开头的方法名的方法,的后面触发调用 SectionAspect 里的 afterInitData 方法?目前来看,我们的 MainActivity 里面的所有以 on字符串 开头的方法名的方法一共有3个,分别是 oncreate,onresume,onstart ,那么,按道理来说,日志的打印的大概顺序应该是 每个生命周期被回调之后,应该跟着 afterInitData 这个方法的打印,我们跑一下看下效果:
图像看不清的话,看如下日志片段
2021-10-02 11:49:34.338 4837-4837/com.kabun.aopdemo D/AopDemo:MainActivity.initData(L:34): initData 2021-10-02 11:49:34.338 4837-4837/com.kabun.aopdemo D/AopDemo:MainActivity.initData1(L:37): initData1 2021-10-02 11:49:34.338 4837-4837/com.kabun.aopdemo D/AopDemo:MainActivity.initData2(L:40): initData2 2021-10-02 11:49:34.338 4837-4837/com.kabun.aopdemo D/AopDemo:MainActivity.onCreate(L:20): after onCreate 2021-10-02 11:49:34.339 4837-4837/com.kabun.aopdemo D/AopDemo:MainActivity.onStart(L:31): after onStart 2021-10-02 11:49:34.339 4837-4837/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData 2021-10-02 11:49:34.340 4837-4837/com.kabun.aopdemo D/AopDemo:MainActivity.onResume(L:26): after onResume 2021-10-02 11:49:34.340 4837-4837/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData
大家会发现,onresume 和 onstart 后面都成功触发了 afterInitData 这个方法的打印了,惟独oncreate 这个方法后面没有 afterInitData 这个方法的打印.......
oncreate 你牛啊你!
八、表达式之---再次挖掘
ok,冷静下,想一下,看下 oncreate,onresume,onstart 的区别。之前我们猜测,oncreate 可能在于它的方法是带有重写性质,然后是 它自带有参数。那么目前,我们可以排除重写带来的疑惑了,毕竟 onresume,onstart 也是有重写 @Override 这个注解,不照样蹦跶??所以....另谋出路吧...
《剑来》有云:遇事不决,可问春风,这不,我可问百度!于是乎,经过一番天人搜索,终于找到些端倪。
原来啊,下面的这个字符串最后的方法括号,里面还可以有作为的
@After("execution(* com.kabun.aopdemo.MainActivity.onCreate(Bundle))")
鉴于我现在推测的主要方向是oncreate 的参数导致我代码无法正常触发调用,所以,我改成不限制参数的数量以及类型,就是我不管它有没有传参,以及即使有传参数的话,我也不管它传了什么参数了,代码如下:
@After("execution(* com.kabun.aopdemo.MainActivity.onCreate(..))")
如上,把 oncreate 的参数 bundle 改成 两个小数点,两个小数点有什么作用呢?作用就是我刚才说的不管它有没有传参,以及即使有传参数的话,我也不管它传了什么参数 ,也就是无限制。
然后,我们试试跑一下看看效果:
老泪纵横啊~,终于成功在 oncreate 后面触发了 afterInitData 的调用了!
那么,活学活用,咱们可以尝试结合以上,解决在这之前的那个无法通过Aspect 触发所有方法调用的问题。需求就是之前说的,在MainActivity 内每个方法调用之后,都触发一次 afterInitData 这个方法 的执行。那么, 我们是不是只要这样写代码就好了?:
@Aspect
public class SectionAspect {
@After("execution(* com.kabun.aopdemo.MainActivity.*(..))")
public void afterInitData() {
GyLog.d("afterInitData");
}
}
星号代表MainActivity 的所有方法,方法内的参数用两个点代表无限制,从方法名和参数两个方面放任执行,然后,我们改下MainActivity的代码,方便查看效果:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initData1();
initData2();
GyLog.d("after onCreate");
}
@Override
protected void onResume() {
super.onResume();
GyLog.d("after onResume");
}
@Override
protected void onStart() {
super.onStart();
GyLog.d("after onStart");
}
private void initData() {
GyLog.d("initData");
}
private void initData1() {
GyLog.d("initData1");
}
private void initData2() {
GyLog.d("initData2");
}
}
那么,我们可以先猜测一下代码运行出来的效果,应该是 MainActivity 的每行日志打印出来后,后面都应该跟着一行 afterInitData 方法的打印,好啦,看下运行效果:
2021-10-02 13:56:34.344 6090-6090/com.kabun.aopdemo D/AopDemo:MainActivity.initData(L:35): initData 2021-10-02 13:56:34.345 6090-6090/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData 2021-10-02 13:56:34.345 6090-6090/com.kabun.aopdemo D/AopDemo:MainActivity.initData1(L:38): initData1 2021-10-02 13:56:34.345 6090-6090/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData 2021-10-02 13:56:34.345 6090-6090/com.kabun.aopdemo D/AopDemo:MainActivity.initData2(L:41): initData2 2021-10-02 13:56:34.346 6090-6090/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData 2021-10-02 13:56:34.346 6090-6090/com.kabun.aopdemo D/AopDemo:MainActivity.onCreate(L:20): after onCreate 2021-10-02 13:56:34.346 6090-6090/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData 2021-10-02 13:56:34.348 6090-6090/com.kabun.aopdemo D/AopDemo:MainActivity.onStart(L:31): after onStart 2021-10-02 13:56:34.348 6090-6090/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData 2021-10-02 13:56:34.349 6090-6090/com.kabun.aopdemo D/AopDemo:MainActivity.onResume(L:26): after onResume 2021-10-02 13:56:34.349 6090-6090/com.kabun.aopdemo D/AopDemo:SectionAspect.afterInitData(L:21): afterInitData
运行效果的确像我们刚推测那样。good
九、还有更有意思的---指哪打哪(指哪拦截哪?)
目前,我们知道的就是,如果想要在 MainActivity 中,让某个方法的执行前或者执行后,相应的触发某个类里面的方法,可以通过如下的代码:
@After("execution(* com.kabun.aopdemo.MainActivity.*(..))")
其中,可以定义的地方是:
1、开头的注解既可以是 after 也可以是 before
2、execution 后面跟着的表达式 ,用于过滤或者说规定 MainActivity 里面的哪些方法,这些方法的参数是怎样的,从方法名(*)到方法的参数(..)去过滤定位
其实,这种写法也挺蹩脚的,无他的,因为上面表达式的语法限制,它的灵活性其实不高,想想,如果一个类里有十几个方法,现在要求在其中几个方法的执行之前,检查当前网络的可用性,例如登录登出什么的业务功能,不是可以用到吗?但是,蛋疼的是,假设这些个需要检查网络的方法的方法名和参数都比较傲娇,可能一条注解表达式@After("execution(*com.kabun.aopdemo.MainActivity.*(..))") 满足不了的话,要写好几条的话,那岂不是有点憋屈?所以,要想个办法将 Aspect 触发的方法和 MainActivity 的方法名以及参数脱离关系。就是说,当 Aspect 触发方法的条件,跟 MainActivity 里面的方法名以及参数无关。这才低耦合嘛~!
那么问题来了?咋整?事不宜迟,直接上示例demo!
新建一个注解接口
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNet {
}
新建一个Aspect 注解的类
@Aspect
public class CheckNetSectionAspect {
@Pointcut("@annotation(com.kabun.aopdemo.CheckNet)")
public void beforeonCreate() {
GyLog.d("beforeOnCreate");
}
@Before("beforeonCreate()")
public void checkNetBehavior(){
GyLog.d("checkNetBehavior");
}
}
更改下 MainActivity 的代码,主要是加了个checknet注解在oncreate上面
public class MainActivity extends AppCompatActivity {
@CheckNet
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GyLog.d("after onCreate");
}
}
看下运行效果:
2021-10-02 17:31:36.409 8202-8202/com.kabun.aopdemo D/AopDemo:CheckNetSectionAspect.checkNetBehavior(L:24): checkNetBehavior 2021-10-02 17:31:36.480 8202-8202/com.kabun.aopdemo D/AopDemo:MainActivity.onCreate(L:19): after onCreate
是不是觉得挺神奇的?就这样,CheckNetSectionAspect 里面的 checkNetBehavior方法就被调用了!来来来,我们看下究竟为啥这么神奇。
首先,我打算先从 MainActivity 的 @CheckNet 入手,怎么入手呢?如上面的日志打印里面我们可以知道,CheckNetSectionAspect 里面的 checkNetBehavior 打印在 MainActivity 的 oncreate 方法前面。那么现在我打算更改一下代码,把注解 @CheckNet 放到onresume上面
,如下:
如上图所示,CheckNetSectionAspect 里面的 checkNetBehavior 打印在 MainActivity 的 onresume方法前面,我们只是将注解移动一下位置而已,方法的触发时机就变了。如果放到刚才要单独地通过注解@Before后面的一串表达式去修改方法的触发时机的话,就要更改里面的内容了。
由此可以看出它的便捷性,指哪打哪。当然也不是那么的随心所欲的,不信,咱将注解放到MainActivity的上面:
编译器直接不干了,标红了,提示说 '@CheckNet' not applicable to type
因为类型对不上啊,毕竟,看看checknet 的注解定义
它这里说了它的目标 Target 得是 ElementType 元素类型的 METHOD 方法,说白了就是,它只能放到方法上面用。如果要在类上尝试的话,也行,代码改成这样:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNet {
}
编译器立刻听话了:
不过,这个暂时不在讨论范围之内。刚才尝试将 checknet 修改玩了一下,现在换个目标 ,看CheckNetSectionAspect 这个类里面的内容。我们看下:
@Aspect
public class CheckNetSectionAspect {
@Pointcut("@annotation(com.kabun.aopdemo.CheckNet)")
public void beforeonCreate() {
GyLog.d("beforeOnCreate");
}
@Before("beforeonCreate()")
public void checkNetBehavior(){
GyLog.d("checkNetBehavior");
}
}
唯一熟悉的就是注解 @Before 了,注解旁边直接就是一个方法 beforeOnCreate 了,给人感觉是在 方法 beforeOnCreate 之前执行 checkNetBehavior 方法。接着就是看新东西---注解:
@Pointcut("@annotation(com.kabun.aopdemo.CheckNet)")
@Pointcut 是个啥?中文的意思是切点,后面的 annotation 刚刚好跟着的是 com.kabun.aopdemo.CheckNet ,这不就是注解CheckNet 的全路径吗?果然够精准定位的
十、又盲推了一波
所以,这里面的逻辑到底是咋回事呢? CheckNetSectionAspect 能跟 MainActivity 扯上关系的中介只能是 @CheckNet 了,而在 方法 beforeonCreate 上,刚好驻扎着注解CheckNet 的全路径,而这个全路径是以参数的形式入住 注解 @Pointcut 里面的。所以说,切点 Pointcut 其实莫非是有点类似 注解 @Before 那样子工作?只是 @Before 要拦截在方法前执行任务的话,得先告诉@Before 要拦截的在什么方法之前执行,所以,就有了 @Before("大哥请将您想拦截的方法地址写在这里,我这就去拦截它") ? 相类似的角度看注解 @Pointcut 试试 ?但很明显,这里的注解 @Pointcut 有个不同点,它并不是要拦截自己参数里面的那个东西,而是干嘛呢?就像是读取一个特殊处理标志位一样,而这个特殊处理标志位就是 注解CheckNet ,至于注解CheckNet在哪?看传进去的全路径(com.kabun.aopdemo.CheckNet),而 @annotation 的作用可能就是告诉程序,这个传进去的CheckNet 玩意是个注解? (以上纯属推测,或者是用便于理解的角度去描述它的可能性)
然后,我又有个大胆的想法了?既然都能做到在方法前拦截它先执行其他方法了,那么,能否再过分一点,做成真正的拦截呢?意思是,例如现在是在 MainActivity 的 onResume 方法前拦截,但是onResume还是会继续执行,现在企图搞掉它,不让它正常执行,中不中?来,走一个试试!
十一、真正的拦截
怎么做呢?如下:
@Aspect
public class CheckNetSectionAspect {
@Pointcut("@annotation(com.kabun.aopdemo.CheckNet)")
public void beforeonCreate() {
GyLog.d("beforeOnCreate");
}
@Before("beforeonCreate()")
public void checkNetBehavior() {
GyLog.d("checkNetBehavior");
}
@Around("beforeonCreate()")
public void AroundBeforeonCreate() throws Throwable {
GyLog.d("AroundBeforeOnCreate");
}
}
注意到这个 @Around 中文是环绕,围绕的意思,多少有点把方法拉进来,随你处置的意思
@Around("beforeonCreate()")
当然,这个注解也确实有这个牛逼能力,不然跑一下看看效果?:
2021-10-03 00:19:37.168 11303-11303/com.kabun.aopdemo D/AopDemo:MainActivity.onCreate(L:19): after onCreate 2021-10-03 00:19:37.170 11303-11303/com.kabun.aopdemo D/AopDemo:CheckNetSectionAspect.checkNetBehavior(L:26): checkNetBehavior 2021-10-03 00:19:37.170 11303-11303/com.kabun.aopdemo D/AopDemo:MainActivity.onResume_aroundBody1$advice(L:31): AroundBeforeonCreate
如日志所示,onResume的确没有被调用了.......那可以恢复onResume的调用吗?当然可以!把 AroundBeforeonCreate 这个方法删掉吗?这个倒不至于.... 可以将方法改成这样:
@Around("beforeonCreate()")
public void AroundBeforeonCreate(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
GyLog.d("before AroundBeforeOnCreate");
proceedingJoinPoint.proceed();
GyLog.d("after AroundBeforeOnCreate");
}
在代码里,我添加了一个参数 ProceedingJoinPoint,再看下运行效果:
2021-10-03 00:31:49.416 11435-11435/com.kabun.aopdemo D/AopDemo:MainActivity.onCreate(L:19): after onCreate 2021-10-03 00:31:49.417 11435-11435/com.kabun.aopdemo D/AopDemo:CheckNetSectionAspect.checkNetBehavior(L:26): checkNetBehavior 2021-10-03 00:31:49.417 11435-11435/com.kabun.aopdemo D/AopDemo:MainActivity.onResume_aroundBody1$advice(L:31): before AroundBeforeonCreate 2021-10-03 00:31:49.418 11435-11435/com.kabun.aopdemo D/AopDemo:MainActivity.onResume_aroundBody0(L:27): after onResume 2021-10-03 00:31:49.418 11435-11435/com.kabun.aopdemo D/AopDemo:MainActivity.onResume_aroundBody1$advice(L:33): after AroundBeforeOnCreate
你会发现,onResume 这个方法的执行是在于 “before AroundBeforeOnCreate” 这行日志之后,“after AroundBeforeOnCreate” 这行日志之前执行的。而且,这也刚好是 AroundBeforeonCreate 方法里,被两行日志包裹的样子-----莫非这就是传说中的环绕,围绕?Around? 6666
十二、拦截的,到底是个啥玩意
而且,大家发现没,这里面有个新东西叫 ProceedingJoinPoint 看着有点像 继续执行连接点的意思(我就是直接翻译过来的)。先不看文档,单纯从以下的代码来讨论,
proceedingJoinPoint.proceed();
在我们刚才没写这行代码的时候,onresume明显没被调用执行,所以,我们可以初步推测这行代码的直接妙用在于,能控制 onresume 的执行与否。那么问题来了,我尝试吧代码改成before注解的方法代码改一下:
@Before("beforeonCreate()")
public void checkNetBehavior(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
GyLog.d("before checkNetBehavior");
proceedingJoinPoint.proceed();
GyLog.d("after checkNetBehavior");
}
// @Around("beforeonCreate()")
// public void AroundBeforeonCreate(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// GyLog.d("before AroundBeforeOnCreate");
// proceedingJoinPoint.proceed();
// GyLog.d("after AroundBeforeOnCreate");
// }
那么,想一下会发生,看下运行结果:
Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object org.aspectj.lang.ProceedingJoinPoint.proceed()' on a null object reference
at com.kabun.aopdemo.CheckNetSectionAspect.checkNetBehavior(CheckNetSectionAspect.java:27)
at com.kabun.aopdemo.MainActivity.onResume(MainActivity.java:26)
at android.app.Instrumentation.callActivityonResume(Instrumentation.java:1269)
at android.app.Activity.performResume(Activity.java:6807)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3411)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3474)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2737)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1482)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6145)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:892)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:782)
可以看到报错了,ProceedingJoinPoint 是null ,也就是说,如果使用注解before的话,人家压根没给方法传 ProceedingJoinPoint ,没得玩。那么,接着再看下注解Around 情况下,传进来的 ProceedingJoinPoint 是个啥?:
@Around("beforeonCreate()")
public void AroundBeforeonCreate(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
GyLog.d("before AroundBeforeOnCreate");
GyLog.e("proceedingJoinPoint.toString ->"+proceedingJoinPoint.toString());
GyLog.e("proceedingJoinPoint.getTarget ->"+proceedingJoinPoint.getTarget());
GyLog.e("proceedingJoinPoint.getSignature ->"+proceedingJoinPoint.getSignature());
GyLog.e("proceedingJoinPoint.getKind ->"+proceedingJoinPoint.getKind());
GyLog.e("proceedingJoinPoint.getArgs.length ->"+proceedingJoinPoint.getArgs().length);
GyLog.e("proceedingJoinPoint.getSourceLocation ->"+proceedingJoinPoint.getSourceLocation());
GyLog.e("proceedingJoinPoint.getStaticPart ->"+proceedingJoinPoint.getStaticPart());
GyLog.e("proceedingJoinPoint.getThis ->"+proceedingJoinPoint.getThis());
GyLog.e("proceedingJoinPoint.getClass ->"+proceedingJoinPoint.getClass());
GyLog.e("proceedingJoinPoint.proceed ->"+ proceedingJoinPoint.proceed());
GyLog.d("after AroundBeforeOnCreate");
}
为了贪心,我决定一次性把 ProceedingJoinPoint 的所有东西打印出来瞅瞅:
2021-10-03 03:20:04.528 15761-15761/com.kabun.aopdemo D/AopDemo:MainActivity.onCreate(L:19): after onCreate 2021-10-03 03:20:04.530 15761-15761/com.kabun.aopdemo D/AopDemo:CheckNetSectionAspect.checkNetBehavior(L:29): before checkNetBehavior 2021-10-03 03:20:04.531 15761-15761/com.kabun.aopdemo D/AopDemo:MainActivity.onResume_aroundBody1$advice(L:37): before AroundBeforeonCreate 2021-10-03 03:20:04.531 15761-15761/com.kabun.aopdemo E/AopDemo:MainActivity.onResume_aroundBody1$advice(L:38): proceedingJoinPoint.toString ->execution(void com.kabun.aopdemo.MainActivity.onResume()) 2021-10-03 03:20:04.531 15761-15761/com.kabun.aopdemo E/AopDemo:MainActivity.onResume_aroundBody1$advice(L:39): proceedingJoinPoint.getTarget ->com.kabun.aopdemo.MainActivity@c74ce62 2021-10-03 03:20:04.531 15761-15761/com.kabun.aopdemo E/AopDemo:MainActivity.onResume_aroundBody1$advice(L:40): proceedingJoinPoint.getSignature ->void com.kabun.aopdemo.MainActivity.onResume() 2021-10-03 03:20:04.531 15761-15761/com.kabun.aopdemo E/AopDemo:MainActivity.onResume_aroundBody1$advice(L:41): proceedingJoinPoint.getKind ->method-execution 2021-10-03 03:20:04.531 15761-15761/com.kabun.aopdemo E/AopDemo:MainActivity.onResume_aroundBody1$advice(L:42): proceedingJoinPoint.getArgs.length ->0 2021-10-03 03:20:04.531 15761-15761/com.kabun.aopdemo E/AopDemo:MainActivity.onResume_aroundBody1$advice(L:43): proceedingJoinPoint.getSourceLocation ->MainActivity.java:28 2021-10-03 03:20:04.531 15761-15761/com.kabun.aopdemo E/AopDemo:MainActivity.onResume_aroundBody1$advice(L:44): proceedingJoinPoint.getStaticPart ->execution(void com.kabun.aopdemo.MainActivity.onResume()) 2021-10-03 03:20:04.531 15761-15761/com.kabun.aopdemo E/AopDemo:MainActivity.onResume_aroundBody1$advice(L:45): proceedingJoinPoint.getThis ->com.kabun.aopdemo.MainActivity@c74ce62 2021-10-03 03:20:04.532 15761-15761/com.kabun.aopdemo E/AopDemo:MainActivity.onResume_aroundBody1$advice(L:46): proceedingJoinPoint.getClass ->class org.aspectj.runtime.reflect.JoinPointImpl 2021-10-03 03:20:04.532 15761-15761/com.kabun.aopdemo D/AopDemo:MainActivity.onResume_aroundBody0(L:29): after onResume 2021-10-03 03:20:04.532 15761-15761/com.kabun.aopdemo E/AopDemo:MainActivity.onResume_aroundBody1$advice(L:47): proceedingJoinPoint.proceed ->null 2021-10-03 03:20:04.532 15761-15761/com.kabun.aopdemo D/AopDemo:MainActivity.onResume_aroundBody1$advice(L:48): after AroundBeforeonCreate
看看都是些啥:
1、toString ->execution(void com.kabun.aopdemo.MainActivity.onResume())
onResume的所在文件目录以及它的返回值类型为空
2、getTarget ->com.kabun.aopdemo.MainActivity@c74ce62
目标 是 MainActivity 后面连activity 的内存地址都弄出来了
3、getSignature ->void com.kabun.aopdemo.MainActivity.onResume()
Signature是签名的意思,目前来看它的签名就是 onResume() 的特征以及路径
4、getKind ->method-execution
类型,这个类型是方法---执行点??(不懂)
5、getArgs.length ->0
参数长度是0,这个应该是onResume 的参数描述,如果是onCreate的话,就是1,但可能是因为 onCreate(Bundle savedInstanceState) 里面的 Bundle 不被 AspectJ 认识,会返回null,大家有兴趣可以试试
6、getSourceLocation ->MainActivity.java:28
这个直接把 onResume 所在 MainActivity 的第几行也打印出来了
7、getStaticPart ->execution(void com.kabun.aopdemo.MainActivity.onResume())
这个打印出的东西内容和 第1点的 toString 出来的东西一样
8、getThis ->com.kabun.aopdemo.MainActivity@c74ce62
这个也 和 第2点打印的内容一样,把MainActivity 后 的内存地址都弄出来了
9、getClass ->class org.aspectj.runtime.reflect.JoinPointImpl
这个是把 ProceedingJoinPoint 的具体实现类给打印出来了,是的,大家翻下ProceedingJoinPoint 的源码就可以知道 ProceedingJoinPoint 只是一个接口,具体的实现类就是JoinPointImpl,而 ProceedingJoinPoint 又是继承自 JoinPoint,也就是文章中一开头提及的 JoinPoint(连接点)的,同时,以上8点内容都是来自JoinPointImpl里面的字段。
到这里,从这9个打印出来的内容,大概可以推测,将注解@CheckNet 放到onResume方法上面后,经过Aspect 提前布局的一刀,把 @CheckNet 引到注解 @Pointcut 也事先设置好的标志切点,响应了之后把执行逻辑丢进了@Around 注解的方法里面去,当然如果想知道拦截到的执行逻辑到底是啥,可以将方法里加多一个参数 ProceedingJoinPoint,接收并处理这些内容,像getThis这些,就可以直接强转成 Context 类型,Context 可是上下文,可以为所欲为了,例如我想打印个吐司:
对吧?因为这个上下文拿到之后可以做挺多事情的,那么大家自己可以尝试拿去玩了~
十三、我想尝试一览全貌
其实,由此至终都我甚少提及里面的涉及的一些专业名词,例如 Joinpoint(连接点) ,Pointcut(切入点)等等之类的。因为这东西一开始说这个的话,我压根就没能把这玩意跟实际事物或者说场景对应上来,就是一个字--懵!那么,现在的话,说连接点的话,会想到它的子类接口 ProceedingJoinPoint ,然后会想到这货在@Around 注解的方法参数里出现过,它的出现也带来了全村的希望,包括上下文,方法能否继续执行,然后由此又联想到注解@Pointcut ,如果不是它把要留意的要拦截的(俗话说要切的)点给的读取了,程序也不会知道跑哪里要拐弯,哦,看到 @CheckNet 的话要注意下,给它砍一刀,顺便扔给@Pointcut注解的方法,但是@Pointcut 毕竟只是一个诱饵,一个切点,把刀瞄准到这个切点上,一刀过去,切面就出来了,注解@Aspect 就觉醒了,Aspect 就开始分配通知了,谁谁哪个家伙要使用这玩意的,什么@Before @After @Around 按顺序一个个来处理 beforeonCreate() 这个东西,这些 @Before @After @Around 在AOP的世界里,叫 Advice (通知) 。 而before因为是在方法执行前触发的,因此也被叫做 前置通知,after 是在方法执行后触发的,因此叫 后置通知,然后是 around 它叫环绕通知,至于为啥叫环绕 估计大家应该还没忘记吧 。
当然,其实通知不止这些的,同时不止是 有Advice 通知,还有Pointcut(切入点) ,Joinpoint(连接点)这些专业名词以及其对应的概念,每个概念下面还有一些细分出来的用法,像文章开头演示的:
@After("execution(* com.kabun.aopdemo.MainActivity.*(..))")
这个后置通知里面的 execution 里面其实是一个表达式,而它自己是个切入点指示符,那么切入点指示符是什么呢?用来指示切入点表达式目的,这东西在网上一搜还是可以搜索到不少东西的,这里我直接复制网上一个简书的大佬简书唠嗑008的文章内容,其实我是学习了他的文章再写的文章,当然还有其他大佬的文章,在这里不一一举例,大家可以网上自己搜索:
execution:用于匹配方法执行的连接点 within:限制链接点匹配指定的类型 this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配 target:限制链接点匹配目标对象为指定类型的类 @within:匹配所有使用了xx注解的类(注意是类) @annotation: 匹配使用了xx注解的方法(注意是方法)
如上面所看到的,注解After 里面是可以填这么多切入点指示符的,然后,每个切入点指示符下面又是一堆表达式,像切入点指示符execution 表达式的语法:
execution():切入点指示符; 第一个*:表示任意方法返回类型; *..:表示省略了MainActivity的包名,当然这里也可以写出完整包名; on*表示MainActivity中所有以on开头的方法; (..):表示方法的参数可以是任意类型且个数任意
当然,这些语法差不多我在示例中已经演示过了,现在更多是归纳总结它们的概念以及用法。
十四、示例源码
下面是源代码:
package com.kabun.aopdemo;
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;
public class SectionAspect {
@After("execution(* com.kabun.aopdemo.MainActivity.*(..))")
public void afterInitData() {
GyLog.d("afterInitData");
}
}
package com.kabun.aopdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GyLog.d("after onCreate");
}
@CheckNet
@Override
protected void onResume() {
super.onResume();
GyLog.d("after onResume");
}
}
package com.kabun.aopdemo;
import android.content.Context;
import android.widget.Toast;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class CheckNetSectionAspect {
@Pointcut("@annotation(com.kabun.aopdemo.CheckNet)")
public void beforeonCreate() {
GyLog.d("beforeOnCreate");
}
@Before("beforeonCreate()")
public void checkNetBehavior() {
GyLog.d("before checkNetBehavior");
}
@Around("beforeonCreate()")
public void AroundBeforeonCreate(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
GyLog.d("before AroundBeforeOnCreate");
GyLog.e("proceedingJoinPoint.toString ->" + proceedingJoinPoint.toString());
GyLog.e("proceedingJoinPoint.getTarget ->" + proceedingJoinPoint.getTarget());
GyLog.e("proceedingJoinPoint.getSignature ->" + proceedingJoinPoint.getSignature());
GyLog.e("proceedingJoinPoint.getKind ->" + proceedingJoinPoint.getKind());
GyLog.e("proceedingJoinPoint.getArgs.length ->" + proceedingJoinPoint.getArgs().length);
GyLog.e("proceedingJoinPoint.getSourceLocation ->" + proceedingJoinPoint.getSourceLocation());
GyLog.e("proceedingJoinPoint.getStaticPart ->" + proceedingJoinPoint.getStaticPart());
GyLog.e("proceedingJoinPoint.getThis ->" + proceedingJoinPoint.getThis());
GyLog.e("proceedingJoinPoint.getClass ->" + proceedingJoinPoint.getClass());
// GyLog.e("proceedingJoinPoint.proceed ->" + proceedingJoinPoint.proceed());
Context context = (Context) proceedingJoinPoint.getThis();
Toast.makeText(context, "嘎嘎嘎嘎嘎", Toast.LENGTH_LONG).show();
GyLog.d("after AroundBeforeOnCreate");
}
}
package com.kabun.aopdemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNet {
}
至于 GyLog 就贴出来了,只是个方便打印日志的工具而已
感谢您的收看,道友们下次见
(不一定写完了,甚至还会更新)
参考文档:
Android aop切点表达式(execution) - 简书
AOP面向切面编程 - 淘宝京东网络处理 - 简书



