AOP实现检测方法耗时
写文章之前首先得解释下什么是AOP,AOP(Aspect-Oriented Programming,面向切面编程),它是对OOP(Object-Oriented Programing,面向对象编程)的一种补充,传统的OOP针对业务处理过程的实体及其属性和行为进行高度抽象封装,每个类都是一个独立体,实现特有的功能,互不干扰。而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
AOP主要功能:
日志记录,性能统计,安全控制,事务处理,异常处理等等。
AOP优点:
1,每个事物逻辑位于一个位置,代码不分散,便于维护和升级
2,业务模块更简洁,只包含核心业务代码
今天这边实现的就是一个日志记录切面,比如在某个页面展现时候特别卡顿,延时比较长,我们可能需要测试这个页面中所有方法耗时时间,找出耗时的方法然后做一些异步操作。
需要用到的第三方.jar包:aspectjrt.jar,需要引用到gradle中。
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
也就是说AspectJ编译器在代码的编译阶段植入Pointcut的内容 ,它的性能比较好。
首先,我们自定义一个注解,这个注解是我们暴露在外面的调用的,外面只需要调用这个注解,就会对这个方法进行编译植入我们希望进行切面的操作。
/**
* 用来标识性能监测
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BehaviorTrace {
String value() default "";
String value2() default "";
}
我们在定义一个注解的过程中我们一般需要知道的几个知识点:
1,我们的注解和类一样,使用@interface进行申明
2,指定的注解的运行时机@Retention(RetentionPolicy.RUNTIME),这里我们指定是在运行时,有如下几种参
FF0B337D-2B62-4396-B3B6-299E9C78D4BD.png3,指定注解的类型@Target(ElementType.FIELD),它有如下几种参数:
6662CDEB-8CB8-4F1A-B1D9-CC276A20F964.png然后就是定义我们的注解接收的参数,它可以接受基本的数据类型,String,数组等…
其次,我们定义一个类,这个类需要实现@Aspect注解,它的作用是把当前类标识为一个切面可供容器读取。这个类中我们做两个操作,一个是定义AOP切面规则,另一个是对切面内容的处理。
/**
* Created by 48608 on 2018/3/5.
*/
@Aspect
public class BehaviorTraceAspect {
//定义切面的规则
//1.就在原来应用中哪些注释的地方放到当前切面进行处理
//execution(注释名 注释用的地方)
//com.dcw.aop_aspect.annotation.BehaviorTrace * *(..)代表的是任意定义了BehaviorTrace注解的方法都执行这个切面方法
@Pointcut("execution(@com.dcw.aop_aspect.annotation.BehaviorTrace * *(..))")
public void methodAnnotatedWithBehaviorTrace(){}
//2.对进入切面的内容如何处理
//advice
//@Before() 在切入点之前运行
//@After() 在切入点之后运行
//@Around() 在切入点前后都运行
//围绕methodAnnotatedWithBehaviorTrace()方法进行切面操作
@Around("methodAnnotatedWithBehaviorTrace()")
public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable{
MethodSignature methodSignature=(MethodSignature)joinPoint.getSignature();
String className=methodSignature.getDeclaringType().getSimpleName();
String methodName=methodSignature.getName();
String funName=methodSignature.getMethod().getAnnotation(BehaviorTrace.class).value();
//统计时间
long begin=System.currentTimeMillis();
//插入原有的代码
Object result=joinPoint.proceed();
long duration=System.currentTimeMillis()-begin;
Log.d("dcw",String.format("功能:%s,%s类的%s方法执行了,用时%d ms",funName,className,methodName,duration));
return result;
}
}
其中@Pointcut("execution(@com.dcw.aop_aspect.annotation.BehaviorTrace * *(..))")是定义切面规则,execution是切入点指示符,这里用法很多样灵活,具体可以自己百度。
@ Around代表在切入点joinPoint.proceed()前后都有代码运行,
@ Before是在所拦截方法执行之前执行一段逻辑
@ After是在所拦截方法执行之后执行一段逻辑
其中@ Before,@ After注解方法里面只需要写切入内容,不需要调用joinPoint.proceed(),如果在Before里面做如下操作是会报异常的。
@Before("methodAnnotatedWithBehaviorTrace()")
public Object beforeJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable{
//插入原有的代码
Object result=joinPoint.proceed();
Log.d("dcw","before操作");
return result;
}
注意同一个方法被多个Aspect类拦截可能会报出异常。
具体使用的时候只需要在外部定义一个方法就实现@BehaviorTrace注解就可以,你可以在Log区域打印出这个方法执行的时间。
@BehaviorTrace(value = "摇一摇",value2="haha")
public void mShake(View view){
SystemClock.sleep(new Random().nextInt(100));
}
这就实现了一个依赖于Aspect的切面实现方法耗时检测功能了。