一、使用
-
添加依赖,配置build脚本
//1.全局 dependencies { classpath 'org.aspectj:aspectjtools:1.8.9' //aspectJ classpath 'org.aspectj:aspectjweaver:1.8.9' } //2.模块中 dependencies{ api 'org.aspectj:aspectjrt:1.8.9' } import org.aspectj.bridge.IMessage import org.aspectj.bridge.MessageHandler import org.aspectj.tools.ajc.Main 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.5", "-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; } } } }
-
实现切入
//1.注解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LoginFilter { int loginDefine() default 0; } //2.对应的Aspect处理业务类,可以简单理解为拦截后怎么处理,放行或者其他等等 @Aspect public class LoginFilterAspect { private static final String TAG = "LoginFilterAspect"; //告诉代码注入工具,在LoginFilter的所有方法注入该userIntercept方法 @Pointcut("execution(@com.example.dailytest.filter.LoginFilter * *(..))") public void userIntercept(){ } //在activity的所有on开头的方法执行后执行该方法 @After("execution(* android.app.Activity.on**(..))") public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable{ String key = joinPoint.getSignature().toString(); Log.d(TAG,"onActivityMethodBefore " + key +"\n"+joinPoint.getThis()); } //在方法前各插入代码,也就是在proceed()放行前、放行后都可以处理一些逻辑 @Around("userIntercept()&&@annotation(userFilter)") public void userFilter(ProceedingJoinPoint joinPoint, LoginFilter userFilter) throws Throwable{ Log.d(TAG,"userFilter " + joinPoint.getSignature().toString() +"\n"+joinPoint.getThis()); Log.d(TAG,"userFilter " + userFilter.loginDefine() +"\n"+joinPoint.getThis()); //放行 joinPoint.proceed(); //放行后 Log.i(TAG,"放行后执行的操作"); } } //3.简单操作,运行 public class MainActivity extends AppCompatActivity { private static final String TAG = "LoginFilterAspect"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); test(); } @LoginFilter(loginDefine = 1) private void test() { Log.d(TAG, "test"); } }
二、注解含义及原理分析
-
基本概念
- Join Points连接点 (你想把新的代码插在程序的哪个地方,是插在构造方法中,还是插在某个方法调用前,或者是插在某个方法中,这个地方就是Join Points)
- Pointcuts切入点 (告诉代码注入工具,在何处注入一段特定代码的表达式。在那些Join Points应用一个特定的Advice,切入点可以选择唯一一个,比如执行某一个方法)
- Advice通知 (如何注入到class文件中的代码,例如before、after或者around)
- Aspect切面 (Pointcut和Advice的组合,例如,我们在应用中通过定义一个Pintcut和给定advice,添加一个日志切面)
- Weaving织入 (注入代码advices 到目标位置joint pints的过程)
-
注解
-
@Aspect 表示这是一个切面类
-
@Before 表示可以在方法前插入代码
-
@After 表示可以在方法后插入代码
-
@Around 表示可以在方法前,方法后插入代码
-
execution(表达式) 如 execution(* android.app.Activity.on**(…)) 第一个表示返回值, * 是通配符(任意类型),可以通过&&、||、!来进行条件组合,第二个中(…)代表任意个数任意类型参数,可以指定。例如execution(@com.example.dailytest.filter.LoginFilter * *(…))") 指定返回值是LoginFilter,方法和参数任意
-
再例如
execution(* *..Activity+.*(..) || execution(* * *..Fragment+.*(..) ) )
截取任何包中任何Activitiy和Fragment结尾的所有方法
-
call / execution 区别
Call(Before) Pontcut{ Pointcut Method } Call(After) Pointcut{ Execution(Before) Pointcut Method Execution(After) }
-
-
接下来分析一下实现切入,这个过程是怎么做到切入的
-
MainActivity.class源码
public class MainActivity extends AppCompatActivity { private static final String TAG = "LoginFilterAspect"; public MainActivity() { } protected void onCreate(Bundle savedInstanceState) { JoinPoint var2 = Factory.makeJP(ajc$tjp_0, this, this, savedInstanceState); try { super.onCreate(savedInstanceState); this.setContentView(2131296284); this.test(); } catch (Throwable var5) { LoginFilterAspect.aspectOf().onActivityMethodBefore(var2); throw var5; } LoginFilterAspect.aspectOf().onActivityMethodBefore(var2); } @LoginFilter( loginDefine = 1 ) private void test() { JoinPoint var1 = Factory.makeJP(ajc$tjp_1, this, this); LoginFilterAspect var10002 = LoginFilterAspect.aspectOf(); ProceedingJoinPoint var10003 = (ProceedingJoinPoint)var1; Annotation var10004 = ajc$anno$0; if (ajc$anno$0 == null) { var10004 = ajc$anno$0 = MainActivity.class.getDeclaredMethod("test").getAnnotation(LoginFilter.class); } test_aroundBody1$advice(this, var1, var10002, var10003, (LoginFilter)var10004); } static { ajc$preClinit(); } }
-
首先看onCreate方法,它确保会执行
LoginFilterAspect.aspectOf().onActivityMethodBefore(var2);
,对应于切面类中我们使用的@After注解,含义就是在onCreate方法执行完后再执行该切面方法 -
再看test方法,使用了@LoginFilter注解,对应于切面内@Around注解,含义是在该test方法前后都执行,也就是目标程序插入了Aspect所在的包获取了引用,再通过在目标来里面加入闭包实现
-
-
接下来将Aspect的execution换成call,源码如下
public class MainActivity extends AppCompatActivity { private static final String TAG = "LoginFilterAspect"; public MainActivity() { } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296284); JoinPoint var3 = Factory.makeJP(ajc$tjp_0, this, this); LoginFilterAspect var10003 = LoginFilterAspect.aspectOf(); ProceedingJoinPoint var10004 = (ProceedingJoinPoint)var3; Annotation var10005 = ajc$anno$0; if (ajc$anno$0 == null) { var10005 = ajc$anno$0 = MainActivity.class.getDeclaredMethod("test").getAnnotation(LoginFilter.class); } test_aroundBody1$advice(this, this, var3, var10003, var10004, (LoginFilter)var10005); } @LoginFilter( loginDefine = 1 ) private void test() { Log.d("LoginFilterAspect", "test"); } static { ajc$preClinit(); } }
从源码中得以证实call和execution得区别
三、通过切面编程,是不是就可以自定义一些类来检测系统性能情况呢?答案是肯定可以的,这里不再累赘,直接仿造上面实现就可以,对自己写的方法加个注解就好了。