赞
踩
AOP、OOP是程序中经常涉及的概念。
OOP--面向对象编程,java、c#等都是面向对象的编程语言,主张万物皆对象。
AOP是一种编程思想,对OOP的有效补充,在具体代码中,可以针对任意方法、属性、类等,做一个切面处理。针对被标记(注解标记)的代码,做额外处理。
AspectJ是AOP思想的实现方式,将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
AspectJ埋点上传的原理:在需要埋点的地方,用注解方式,标注一个切入点。后续在@Aspect注解的类中,处理该切入点,处理完成后,将代码织入到原代码中。
@Aspect注解的类,将使用特定的编译器ajc编译,而不再是javac编译。
1-1:在最外层的build中,新增以下依赖:
- dependencies {
- classpath 'com.android.tools.build:gradle:3.6.3'
- //以下为新增依赖
- classpath 'org.aspectj:aspectjtools:1.9.1'
- classpath 'org.aspectj:aspectjweaver:1.9.1'
-
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
1-2:在具体工程的build中,新增以下依赖,以下依赖主要用于日志打印。
此处需注意,根据模块的不同,遍历使用不同的属性:
app模块使用:applicationVariants
library模块使用:libraryVariants
-
- import org.aspectj.bridge.IMessage
- import org.aspectj.bridge.MessageHandler
- import org.aspectj.tools.ajc.Main
- def log = project.logger
-
- //app模块使用:applicationVariants
- //library模块使用:libraryVariants
- android.applicationVariants.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", android.bootClasspath.join(
- File.pathSeparator)]
-
- 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:
- case IMessage.INFO:
- log.info message.message, message.thrown
- break;
- case IMessage.DEBUG:
- log.debug message.message, message.thrown
- break;
- }
- }
- }
- }
本例中共定义了三种注解,分别用于:修饰注解的基础注解、定义切入点注解、参数注解
2-1:BasePoint
该注解,用于埋点的分类。实际埋点可能分为多种类型,如异常埋点、页面进入埋点、点击埋点等。type代表类别名称,typeId代表类别ID。
- @Target(ElementType.ANNOTATION_TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface BasePoint {
- String type();
- String typeId();
- }
2-2:DataPoint
该注解是实际用于切点的埋点,让aspectJ识别。BasePoint也是修饰该注解(避免在实际应用中,需要多次手写type与typeId)。
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- @BasePoint(type = "数据埋点",typeId = "1001")
- public @interface DataPoint {
-
- String value() default "";
- }
2-3:Parameter
该注解用于方法参数的注解。埋点时具体上送的内容,通过该参数注解与参数值,组成key-value形式上送
- @Target(ElementType.PARAMETER)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Parameter {
- String value();
- }
3-1:在具体需要埋点的方法中,在方法上以及方法的参数中加上注解
- @DataPoint
- private static void testPointStaticPrivate(@Parameter("key1") int a, String b) {
- Log.e("MainActivity", "testPointStaticPrivate: "+"方法被执行" );
- }
3-2:新建类,用Aspect注解该类。在该类中收集数据并上传
- @Aspect
- public class AspectManager {
- private static final String TAG = "AspectManager";
-
- @Pointcut("execution(@com.sunny.aaspect.annotation.DataPoint * *(..))")
- public void uploadDataPoint() {
- }
-
- @Around("uploadDataPoint()")
- public Object handleUploadPoint(ProceedingJoinPoint joinPoint) {
-
- //获取到方法的反射对象
- MethodSignature signature = (MethodSignature) joinPoint.getSignature();
- //方法实体
- Method method = signature.getMethod();
- //方法上的所有注解
- Annotation[] annotations = method.getAnnotations();
-
- //获取方法的接收参数的注解
- Annotation[] methodParamsAnnotations = getMethodParamsAnnotations(method);
-
- BasePoint basePoint = null;
- for (Annotation annotation : annotations) {
- basePoint = annotation.annotationType().getAnnotation(BasePoint.class);
- if (basePoint == null) {
- break;
- }
- }
-
- if (basePoint == null) {
- return sourceMethod(joinPoint);
- }
- //获取到基础注解上的类别和类别ID
- String type = basePoint.type();
- String typeId = basePoint.typeId();
-
- //获取到参数上的key-value
- JSONObject paramsData = getParamsData(methodParamsAnnotations, joinPoint.getArgs());
- Log.e(TAG, "上传数据----> type:" + type + " typeId:" + typeId + " params:" + (paramsData != null ? paramsData.toJSONString() : ""));
- return sourceMethod(joinPoint);
- }
-
- private JSONObject getParamsData(Annotation[] methodParamsAnnotations, Object[] args) {
- JSONObject params = null;
- if (methodParamsAnnotations == null || methodParamsAnnotations.length == 0) {
- return null;
- }
- params = new JSONObject();
- int i = 0;
- for (Annotation methodParamsAnnotation : methodParamsAnnotations) {
- if (methodParamsAnnotation instanceof Parameter) {
- params.put(((Parameter) methodParamsAnnotation).value(), JSONObject.toJSONString(args[i++]));
- }
- }
- return params;
- }
-
- /**
- * 原方法必须执行
- */
- private Object sourceMethod(ProceedingJoinPoint joinPoint) {
- try {
- return joinPoint.proceed();
- } catch (Throwable throwable) {
- throwable.printStackTrace();
- }
- return null;
- }
-
- /**
- * 获取方法的接收参数的注解
- */
- private Annotation[] getMethodParamsAnnotations(Method method) {
- //一个参数可能有多个注解 Annotation[]
- Annotation[][] parameterAnnotations = method.getParameterAnnotations();
- if (parameterAnnotations.length == 0) {
- return null;
- }
- Annotation[] result = new Annotation[parameterAnnotations.length];
- int i = 0;
- for (Annotation[] parameterAnnotation : parameterAnnotations) {
- for (Annotation annotation : parameterAnnotation) {
- result[i++] = annotation;
- }
- }
-
- return result;
- }
-
- }
至此,完成了AspectJ形式的埋点上传,不侵入原代码,埋点信息不影响原逻辑且有统一的入口,方便后续的修改和扩展。
后续的扩展,只需要在需要埋点的方法或者页面、异常等地方加入注解,并仿造DataPoint的方式,新建AspectJ处理类即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。