无感数据埋点(自定义注解+aop+异步)
前段时间,由于业务上需求,需要对客户端的请求数据进行埋点,以方便后续处理分析用户的访问数据喜好。埋点业务尽可能是异步解耦,即对主流程业务无影响。经过一番思考,决定采用自定义注解+aop+异步方式进行处理。
-
元注解
实现自定义注解,先了解下元注解。元注解是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注释。常用的元注解有4种:
@Target:描述了注解修饰的对象范围,取值在java.lang.annotation.ElementType
定义,常用的包括:
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述方法变量
- TYPE:用于描述类、接口或enum类型
@Retention: 表示注解保留时间长短。取值在java.lang.annotation.RetentionPolicy
中,取值为:
- SOURCE:在源文件中有效,编译过程中会被忽略
- CLASS:随源文件一起编译在class文件中,运行时忽略
- RUNTIME:在运行时有效
只有定义为RetentionPolicy.RUNTIME
时,才能通过注解反射获取到注解。
@Documented:用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。
@Inherited:表示注释类型是自动继承的,该注释除类之外的任何内容均无效,即父和子均为类才能有作用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import java.lang.annotation.*; 2 3 @Retention(RetentionPolicy.RUNTIME) 4 @Target({ElementType.METHOD, ElementType.TYPE}) 5 @Documented 6 public @interface SysLog { 7 8 String domain() default "strategy"; 9 10 String platform() default "strategy-service"; 11 12 String action() default ""; 13 14 }
实现AOP切面拦截:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import com.alibaba.fastjson.JSONObject; 2 import com.shimao.iot.strategy.service.strategyservice.service.DataPointLogService; 3 import lombok.extern.slf4j.Slf4j; 4 import org.aspectj.lang.JoinPoint; 5 import org.aspectj.lang.annotation.AfterReturning; 6 import org.aspectj.lang.annotation.Aspect; 7 import org.aspectj.lang.annotation.Before; 8 import org.aspectj.lang.annotation.Pointcut; 9 import org.aspectj.lang.reflect.MethodSignature; 10 import org.springframework.stereotype.Component; 11 12 import javax.annotation.Resource; 13 14 @Aspect 15 @Component 16 @Slf4j 17 public class SysLogAspect { 18 19 @Resource 20 private DataPointLogService dataPointLogService; 21 22 23 // 定义切点 24 @Pointcut("@annotation(xxx.dataPoint.SysLog)") 25 public void sysLogAspect() { 26 } 27 28 // 前置通知,拦截controller层请求 29 @Before(value = "sysLogAspect()") 30 public void doBefore(JoinPoint joinPoint) { 31 String methodName = joinPoint.getSignature().getName(); 32 String request = JSONObject.toJSONString(joinPoint.getArgs()[0]); 33 log.info(methodName + " request data:{}", request); 34 SysLog sysLog = ((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(SysLog.class); 35 dataPointLogService.recordLog(sysLog.domain(), sysLog.platform(), sysLog.action(), request); 36 37 } 38 39 //后置通知,输出响应结果数据 40 @AfterReturning(returning = "ret", pointcut = "sysLogAspect()", argNames = "joinPoint,ret") 41 public void doAfterReturning(JoinPoint joinPoint, Object ret) { 42 String className = joinPoint.getTarget().getClass().getName(); 43 String methodName = joinPoint.getSignature().getName(); 44 log.info(className + "." + methodName + " result:{}", JSONObject.toJSONString(ret)); 45 } 46 47 }
定义异步接口记录处理埋点数据:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public interface DataPointLogService { 2 3 void recordLog(String domain, String platform, String action, String content); 4 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @Service 2 public class DataPointLogServiceImpl implements DataPointLogService { 3 4 5 @Override 6 @Async 7 public void recordLog(String domain, String platform, String action, String content) { 8 PointDataUtils.addLogData("", "", domain, platform, action, content, ""); 9 } 10 }
controller层通过注解接入埋点业务: