AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
数据埋点,日志记录,性能统计,安全控制,事务处理,异常处理等等
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
AspectJ在代码的编译期间扫描目标程序,根据切点(PointCut)匹配,将开发者编写的Aspect程序编织(Weave)到目标程序的.class文件中,对目标程序作了重构(重构单位是JoinPoint),目的就是建立目标程序与Aspect程序的连接(获得执行的对象、方法、参数等上下文信息),从而达到AOP的目的。
要引入AspectJ到Android工程中,最重要的就是两个包:
//在buildscript中添加该编织器,gradle构建时就会对class文件进行编织 //在buildscript中添加该工具包,在构建工程的时候执行一些任务:打日志等
classpath 'org.aspectj:aspectjtools:1.8.11' classpath 'org.aspectj:aspectjweaver:1.8.9' 复制代码
//在dependencies中添加该依赖,提供@AspectJ语法
compile 'org.aspectj:aspectjrt:1.8.9' 复制代码
//在app中的gradle
import org.aspectj.bridge.IMessage import org.aspectj.bridge.MessageHandler import org.aspectj.tools.ajc.Main 复制代码
//打印gradle日志
android.applicationVariants.all { variant -> 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)] MessageHandler handler = new MessageHandler(true); new Main().run(args, handler) def log = project.logger 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: case IMessage.INFO: log.info message.message, message.thrown break; case IMessage.DEBUG: log.debug message.message, message.thrown break; } } } } 复制代码
fernandocejas.com/2014/08/03/…
在AOP中切面是标记执行被切点标记方法的逻辑处理。就是切点要处理的具体逻辑在切面这个模块。
它是切面插入执行应用程序的地方,他能被方法调用,也能新增方法,JoinPoint是一个执行链,每一个JoinPoint是一个单独的闭包,在执行的时候将上下文环境赋予闭包执行方法体逻辑。
AspectJ的joinpoint有如下:
method call 函数调用 method execution 函数执行 constructor call 构造函数调用 constructor execution 构造函数执行 field get 获取某个变量 field set 设置某个变量 pre-initialization Object在构造函数中做得一些工作。 initialization Object在构造函数中做得工作 static initialization 类初始化 handler 异常处理,比如try catch(xxx)中,对应catch内的执行 advice execution 里面有3个标记(Around Before After) 复制代码
切点的声明决定需要切割的JoinPoint的集合,Pointcut可以控制你把哪些advice应用于JoinPoint上去,通常通过正则表达式来进行匹配应用,决定了那个jointpoint会获得通知。分为call、execution、target、this、within等关键字等。
within(TypePattem) TypePattern标示package或者类。TypePatter可以使用通配符 withincode(ConstructorSignaturelMethodSignature) 表示某个构造函数或其他函数执行过程中涉及到的JPoint cflow(pointcuts) cflow是call flow的意思,cflow的条件是一个pointcut cflowbelow(pointcuts) 表示调用pointcuts函数时所包含的JPoint,不包括pointcuts这个JPoint本身 this(Type) JPoint的this对象是Type类型 target(Type) JPoint的target对象是Type类型 args(TypeSignature) 用来对JPoint的参数进行条件搜索的 复制代码
(1)类型匹配语法 类型匹配的通配符: *:匹配任何数量字符; ..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。 +:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。 AspectJ使用 且(&&)、或(||)、非(!)来组合切入点表达式。
(2)匹配模式 call(<注解?> <修饰符?> <返回值类型> <类型声明?>.<方法名>(参数列表) <异常列表>?)
精确匹配 //表示匹配 com.sunmi.MainActivity类中所有被@Describe注解的public void方法。 @Pointcut("call(@Describe public void com.sunmi.MainActivity.init(Context))") public void pointCut(){} 单一模糊匹配 //表示匹配 com.sunmi.MainActivity类中所有被@Describe注解的public void方法。 @Pointcut("call(@Describe public void com.sunmi.MainActivity.*(..)) ") public void pointCut(){} //表示匹配调用Toast及其子类调用的show方法,不论返回类型以及参数列表,并且该子类在以com.sunmi开头的包名内 @Pointcut("call(* android.widget.Toast+.show(..)) && (within(com.sunmi..*))") public void toastShow() { } 组合模糊匹配 //表示匹配任意Activity或者其子类的onStart方法执行,不论返回类型以及参数列表,且该类在com.sunmi包名内 @Pointcut("execution(* *..Activity+.onStart(..))&& within(com.sunmi.*)") public void onStart(){} 复制代码
声明一个注解 AopPoint
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AopPoint { String value(); int type() default 0; } 复制代码
在Activity 中,定义了2个按钮的点击事件,在方法上都标记注解,指定了value 和type。
@AopPoint(value = "首页点击",type = 1) public void doFunc1(View view) { SystemClock.sleep(1000); } @AopPoint("分类点击") public void doFunc2(View view) { SystemClock.sleep(1000); } 复制代码
编写一个切面类
@Aspect public class AopAspect { @Pointcut("execution(@com.example.davis.aspectdemo.AopPoint * *(..))") public void aopCut(){ } @Around("aopCut()") public Object dealMethod(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature= (MethodSignature) joinPoint.getSignature(); AopPoint aopPoint=signature.getMethod().getAnnotation(AopPoint.class); String value=aopPoint.value(); int type=aopPoint.type(); TimeTool timeTool=new TimeTool(); timeTool.start(); Object result=joinPoint.proceed(); timeTool.stop(); Log.e("aopCut",value+" 执行时间="+timeTool.getTotalTimeMillis() +" type类型是"+type); return result; } } 复制代码
结果
1、这个是扫描Activity 中onCreate方法的调用
@Aspect public class FuncTAspect { @Before("execution(* android.app.Activity.onCreate(..))") public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable { String key = joinPoint.getSignature().toString(); Log.e("FuncTAspect", "onActivityMethodBefore: " + key+"/n"+joinPoint.getThis()); } } 复制代码
结果只能扫描到onCreate方法
如果把onCreate改成通配符* android.app.Activity.onCreate(..) 改成android.app.Activity.*(..)
结果
2、捕获catch异常
@Aspect public class ExceptionHandleAspect { private static final String TAG = "ExceptionHandleAspect"; /** * 截获空指针异常 * * @param e */ @Pointcut("handler(java.lang.Exception)&&args(e)") public void handle(Exception e) { } /** * 在catch代码执行之前做一些处理 * * @param joinPoint * @param e 异常参数 */ @Before(value = "handle(e)", argNames = "e") public void handleBefore(JoinPoint joinPoint, Exception e) { Log.e(TAG, joinPoint.getSignature().toLongString() + " handleBefore() :" + e.toString()); } } 复制代码
在方法里制造一个Null指针
public void doFunc1(View view) { try { AppItem appItem=null; appItem.number=2; }catch (Exception e){} } 复制代码
结果