记录流水信息(Aop)

391 阅读2分钟

0. 场景

对某一场景进行流水记录,记录某一笔订单的信息;比如:保存订单信息、修改订单信息;

对这种场景可以有多种方法,比如直接在操作后面记录这条记录,直接嵌入原业务代码中

1.问题

因为现在我们知道保存订单信息和修改订单信息的入参DTO的ID可能不大相同,在不相同的情况下如何使用一个AOP去解决这种问题?

2.代码

2.1 模型

// 日志模型
public class LogDo {
    private String desc;
    private Long id;

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}
// 修改订单信息模型
public class UpdateOrder {
    private Long updateId;

    public UpdateOrder(Long updateId) {
        this.updateId = updateId;
    }

    public Long getUpdateId() {
        return updateId;
    }

    public void setUpdateId(Long updateId) {
        this.updateId = updateId;
    }
}
// 保存订单信息模型
public class SaveOrder {
    private Long saveOrderId;

    public SaveOrder(Long saveOrderId) {
        this.saveOrderId = saveOrderId;
    }

    public Long getSaveOrderId() {
        return saveOrderId;
    }

    public void setSaveOrderId(Long saveOrderId) {
        this.saveOrderId = saveOrderId;
    }
}

2.2 service类

@Service
public class OrderService {

    @LogAnnotation(desc = "保存订单", convert = SaveOrderConvert.class)
    public void saveOrder(SaveOrder saveOrder) {
        System.out.println("保存了saveOrder"+"id :" + saveOrder.getSaveOrderId());
    }

    @LogAnnotation(desc = "修改订单", convert = UpdateOrderConvert.class)
    public void updateOrder(UpdateOrder updateOrder) {
        System.out.println("修改了updateOrder"+"id :" + updateOrder.getUpdateId());
    }
}

可以看到这个service类中的保存和修改的方法的InDTO不一样,

image.png

2.3 注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
    /**
     * 业务描述
     * @return
     */
    String desc() default "";

    /**
     * 字段转换器
     * @return
     */
    Class<? extends Convert> convert();
}

在这个注解里面给了一个描述以及一个转换器,这个转换器是我们自己设置的一个接口

2.3.1 Convert接口

/**
 * 对不同的对象进行日志模型id转换
 */
public interface Convert<PARAM> {
    public LogDo convert(PARAM o);
}

2.3.1.1 根据SaveOrder进行转换

根据saveOrder里面的saveOrderId转换成LogDo里面的id

public class SaveOrderConvert implements Convert<SaveOrder> {
    @Override
    public LogDo convert(SaveOrder o) {
        LogDo logDo = new LogDo();
        logDo.setId(o.getSaveOrderId());
        return logDo;
    }
}

2.3.1.2 根据UpdateOrder进行转换

根据UpdateOrder里面的updateId转换成LogDo里面的id

public class UpdateOrderConvert implements Convert<UpdateOrder> {
    @Override
    public LogDo convert(UpdateOrder o) {
        LogDo logDo = new LogDo();
        logDo.setId(o.getUpdateId());
        return logDo;
    }
}

下面这个是AOP的代码

2.4 AOP代码

@Component
@Aspect
public class LogAspect {

    /**
     * 定义切点
     */
    @Pointcut("@annotation(spring.aop.transaction.anno.LogAnnotation)")
    public void poincut() {}

    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 1,
            TimeUnit.SECONDS, new LinkedBlockingDeque<>(10000));
    @Around("poincut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();

        threadPoolExecutor.execute(() -> {
            try {
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                Method method = signature.getMethod();
                LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
                Class<? extends Convert> convert = annotation.convert();
                Convert instance = convert.newInstance();
                LogDo logDo = instance.convert(joinPoint.getArgs()[0]);
                logDo.setDesc(annotation.desc());
                System.out.println("保存日志:"+JSON.toJSON(logDo));
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });
        return result;
    }
}

在这使用一个线程池去进行异步流水日志写入,因为这种流水日志的时效性不需要那么高,只用一个线程去跑这个日志写入的操作即可;

2.5 Main方法

@SpringBootApplication
@EnableAspectJAutoProxy
public class MainApplicaton implements CommandLineRunner {
    public static void main(String[] args) {
        SpringApplication.run(MainApplicaton.class, args);
    }

    @Autowired
    private OrderService orderService;

    @Override
    public void run(String... args) throws Exception {
        orderService.saveOrder(new SaveOrder(1L));

        orderService.updateOrder(new UpdateOrder(2L));
    }
}

2.6 结果

image.png