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不一样,
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));
}
}