动态代理实现数据埋点

文章介绍了如何使用Java的设计模式中的代理模式来实现在不修改原有业务代码的情况下,无缝加入数据埋点功能。通过实现InvocationHandler接口,动态创建代理对象,结合反射获取参数信息,并利用RocketMQ进行异步发送,确保业务系统的低侵入性。在实现过程中,遇到了对象注入问题,通过ApplicationContextAware接口解决了这个问题。
摘要由CSDN通过智能技术生成

设计模式中有常用的模式:代理模式。学术一些来讲,就是为某些对象的某种行为提供一个代理对象,并由代理对象完全控制该行为的实际执行。

数据埋点通俗来说就是基于业务需求或产品需求对用户在应用内产生行为的每一个事件对应的页面和位置植入相关代码,以便相关人员追踪用户行为和应用使用情况。

功能需求:在原有业务代码层面无侵入的加入埋点代码

直接上代码:

第一步  去实现InvocationHandler 接口,重写invoke方法,addPoint方法涉及俩个参数(一个是args是原来方法需要的参数,另一个是埋点接口需要的事件参数,具体要哪些参数跟着需求来)。

public class OperationDataPointProxy implements InvocationHandler {

  private Object target ;

  public OperationDataPointProxy(Object target) {
    this.target = target;
  }

  public <T> T getProxy(){
    return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    log.info("埋点代理--------》"+args);
    OperationDataPointService service = new OperationDataPointServiceImpl();
    service.addPoint(args, JSON.toJSONString(args));
    log.info(JSON.toJSONString(args));
    Object result = method.invoke(target, args);
    return result;
  }
}

 第二步  通过反射获取args里所有的属性和值,使用rocketmq异步发送,这样可以做到各个业务系统都可以直接使用。

这个过程出现一些问题,在上面的代码中,去new  了OperationDataPointServiceImpl就导致下面会出现注入对象为null的问题,原因不懂可以查,解决是通过实现ApplicationContextAware接口手动注入

@Override
  public void addPoint(Object[] args,String eventParam) {
    try {
      OperationDataPointMessage dataPointMessage = new OperationDataPointMessage() ;

      for (Object arg : args) {
        Class<?> aClass = arg.getClass();
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
          field.setAccessible(true);
          if ("applicationType".equals(field.getName())){
            log.info(field.get(arg)+"");
            dataPointMessage.setApplicationType((Integer) field.get(arg)); // 应用类型
          }
          if ("routUrl".equals(field.getName())){
            log.info(field.get(arg)+"");
            dataPointMessage.setRoutUrl((String) field.get(arg)); // url
          }
          if ("clickEvent".equals(field.getName())){
            log.info(field.get(arg)+"");
            dataPointMessage.setClickEvent((String) field.get(arg)); // 触发时机
          }
        }
      }
      dataPointMessage.setEventChName(OperationDataPointEventEnum.getItem(dataPointMessage.getEventType()).getDesc()); // 事件中文名称
      dataPointMessage.setEventEnName(OperationDataPointEventEnum.getItem(dataPointMessage.getEventType()).getEnDesc()); // 事件英文文名称
      dataPointMessage.setEventParam(eventParam); // 事件参数变量
      dataPointMessage.setEventVersion("V1.0"); // 版本
      UserInfoDTO userInfoDTO = AccountUserInfoThreadLocal.get();
      dataPointMessage.setCreateName(userInfoDTO.getUserName()); // 创建者
      dataPointMessage.setCreateId(String.valueOf(userInfoDTO.getUserId())); // 创建者id
      dataPointMessage.setGmtCreated(LocalDateTime.now()); // 创建时间
      rocketMQTemplate.asyncSend(OperationDataPointMessage.topic,dataPointMessage, new SendCallback() {
        @Override
        public void onSuccess(SendResult sendResult) {
          log.info("MQ接收埋点消息是否成功:{}", sendResult.getSendStatus());
        }

        @Override
        public void onException(Throwable e) {
          log.info("MQ接收埋点消息异常:{}", e.getMessage());
        }
      });
    } catch (Exception e) {
      e.printStackTrace();
    }
  }


@Override
  public void mqAdd(OperationDataPointMessage operationDataPointMessage) {
    OperationDataPointDO dataPointDO = new OperationDataPointDO() ;
    BeanUtils.copyProperties(operationDataPointMessage,dataPointDO);
    operationDataPointMapper.insert(dataPointDO) ;
  }

 手动注入使用是在之前需要注入的位置通过GetBeanUtil.getBean手动注入

@Component
public class GetBeanUtil implements ApplicationContextAware {
  protected static ApplicationContext applicationContext;

  @Override
  public void setApplicationContext(ApplicationContext arg0) throws BeansException {
    if (applicationContext == null) {
      applicationContext = arg0;
    }

  }

  public static Object getBean(String name) {
    //name表示其他要注入的注解name名
    return applicationContext.getBean(name);
  }

  /**
   * 拿到ApplicationContext对象实例后就可以手动获取Bean的注入实例对象
   */
  public static <T> T getBean(Class<T> clazz) {
    return applicationContext.getBean(clazz);
  }
}

第三步  

@Component
@Slf4j
@RocketMQMessageListener(topic = OperationDataPointMessage.topic, consumerGroup = "operation_data_point")
public class OperationDataPointConsumer implements RocketMQListener<OperationDataPointMessage> {

  @Autowired
  private OperationDataPointService operationDataPointService;

  @Override
  public void onMessage(OperationDataPointMessage operationDataPointMessage) {
    log.info("埋点mq接收消息为:{}", JSON.toJSONString(operationDataPointMessage));
    operationDataPointService.mqAdd(operationDataPointMessage) ;
  }
}

第四步  使用  

在原来业务代码调用的地方通过埋点代理类代理该业务的实现

public ResultModel test(@RequestBody UserRequest userRequest) {
    UserInfoDTO userInfoDTO = AccountUserInfoThreadLocal.get();
    CustomerFinanceService proxy = new OperationDataPointProxy(new TestServiceImpl()).getProxy();
    return ResultModel.success(proxy.test(userRequest,userInfoDTO));
  }

才疏学浅,不对的地方大佬们多指点。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值