开闭原则正确姿势, 使用AOP优雅的记录日志, 非常的哇塞

这篇具有很好参考价值的文章主要介绍了开闭原则正确姿势, 使用AOP优雅的记录日志, 非常的哇塞。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

👳我亲爱的各位大佬们好😘😘😘
♨️本篇文章记录的为 JDK8 新特性 Stream API 进阶 相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬🙉🙉🙉。
♨️如果文章有什么需要改进的地方还请大佬不吝赐教❤️🧡💛
👨‍🔧 个人主页 : 阿千弟
🔥 相关内容👉👉👉 : 都2023年了,如果不会Lambda表达式、函数式编程?你确定能看懂公司代码?

最近工作中遇到了一个新的需求, 让我把前端发送请求的所有操作统统用日志记录下来, 但是呢前端是h5的页面, 任何一个对页面的操作都记录下来也不太现实, 于是AOP这时候就是一个很好的选择, 但是在操作很多的状态下, 对每一个操作都写一个切面来记录也不太现实, 应优先考虑如何对切面进行抽取与拓展.

因为代码逻辑比较复杂, 所以每次排查问题的时候都非常耗时, 所以这时候用AOP的方式需合的就是在每一个操作的方法之前去加一个注解,通过切面处理这个方法义步的去协助流水表
开闭原则正确姿势, 使用AOP优雅的记录日志, 非常的哇塞

当前场景

为了把保存订单和更新订单的方便区分, 我们分别用不同的实体类entity来表示

SaveOrder

@Data
public class SaveOrder {
    private Long id;
}

UpdateOrder

@Data
public class UpdateOrder {
    private Long OrderId;
}

下面是Service层接口

public interface OrderService {

    Boolean saveOrder(SaveOrder saveOrder);

    Boolean updateOrder(UpdateOrder updateOrder);
}

下面是impl实现类

@Service
public class OrderServiceImpl implements OrderService {

    @Override
    public Boolean saveOrder(SaveOrder saveOrder) {
        System.out.println("save order, orderId : " + saveOrder.getId());
        return true;
    }

    @Override
    public Boolean updateOrder(UpdateOrder updateOrder) {
        System.out.println("update order, orderId : " + updateOrder.getOrderId());
        return true;
    }
}

上面的代码很简单,很方便的就能看懂, 下面我们就来编写自定义注解和AOP切面

自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RecordOperate {

	//desc 是用来放日志类型的描述
    String desc() default " ";
	//convert 用来放日志类型的转变类
    Class<? extends Convert> convert();

}

我们可以看到在自定义的注解类中有一个desc()属性, 它用于记录的字段信息;

还有一个convert()方法, 这个方法很关键 :

当我们每次调用 OrderService 的时候, 要根据不同的方法进行不同日志记录, 那么我们怎么知道, 我们调用的是 SaveOrder 还是 UpdateOrder , 所以不管是SaveOrder还是UpdateOrder, 我们都需要记录日志, 但是呢, 我们不可能对每个操作都单独的写一个切面记录日志, 所以呢需要写一个 convert() 对不同的方法属性转化成统一的log实体输出

下面编写转换器convert()

代码很简单, 泛型用PARAM表示, 返回结果是约定好的OperateLogDO

public interface Convert<PARAM> {
    OperateLogDO convert(PARAM param);
}

下面的代码是转换器的用法

SaveOrderConvert通过实现接口来重写convert()方法

public class SaveOrderConvert implements Convert<SaveOrder> {
    @Override
    public OperateLogDO convert(SaveOrder saveOrder) {
        OperateLogDO operateLogDO = new OperateLogDO();
        operateLogDO.setId(saveOrder.getId());
        return operateLogDO;
    }
}

UpdateOrderConvert通过实现接口来重写convert()方法

public class UpdateOrderConvert implements Convert<UpdateOrder> {
    @Override
    public OperateLogDO convert(UpdateOrder updateOrder) {
        OperateLogDO operateLogDO = new OperateLogDO();
        operateLogDO.setId(updateOrder.getOrderId());
        return operateLogDO;
    }
}

OperateLogDO实体

@Data
public class OperateLogDO {
    private Long id;
    public String desc;
    public String result;
}

AOP切面

@Component
@Aspect
public class OperateAspect {
    /**
     * 定义切入点
     * 横切逻辑
     * 织入
     */

    @Pointcut("@annotation(com.example.demo_aop.annotation.RecordOperate)")
    public void pointCut(){};
	
	//定义线程池的目的是记录日志不需要特别强的同步性, 所以创建线程异步记录
    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            1,1,1,TimeUnit.SECONDS, new LinkedBlockingQueue<>(100)
    );

    @Around("pointCut()")
    public Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result = proceedingJoinPoint.proceed();
        threadPoolExecutor.execute(() -> {
        //这段Java代码获取了一个切点方法的签名信息,
        //并且通过反射获取了该方法上的RecordOperate注解。
            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
            RecordOperate annotation = methodSignature.getMethod().getAnnotation(RecordOperate.class);

			//通过反射判断是哪个方法的转换器,并获取
            Class<? extends Convert> convert = annotation.convert();
            OperateLogDO operateLogDO = null;
            try {
            //创建Convert类的一个新实例
                Convert logConvert = convert.newInstance();
                operateLogDO = logConvert.convert(proceedingJoinPoint.getArgs()[0]);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

            operateLogDO.setDesc(annotation.desc());
            operateLogDO.setResult(result.toString());
            System.out.println("insert operateLog " + JSON.toJSONString(operateLogDO));
        });
        return result;
    }
}

这段切面代码也比较的简单, 但是呢需要具备一定的java基础功底

自定义注解应用

@Service
public class OrderServiceImpl implements OrderService {

    @RecordOperate(desc = "保存订单", convert = SaveOrderConvert.class)
    @Override
    public Boolean saveOrder(SaveOrder saveOrder) {
        System.out.println("save order, orderId : " + saveOrder.getId());
        return true;
    }

    @RecordOperate(desc = "更新订单", convert = UpdateOrderConvert.class)
    @Override
    public Boolean updateOrder(UpdateOrder updateOrder) {
        System.out.println("update order, orderId : " + updateOrder.getOrderId());
        return true;
    }
}

代码测试
@SpringBootApplication
public class AopApplication implements CommandLineRunner {
    public static void main(String[] args) {
        SpringApplication.run(AopApplication.class, args);
    }
   
    @Resource
    OrderService orderService;


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

        UpdateOrder updateOrder = new UpdateOrder();
        updateOrder.setOrderId(2L);
        orderService.updateOrder(updateOrder);
    }
}
(JVM running for 5.141)
save order, orderId : 1
update order, orderId : 2
insert operateLog {"desc":"保存订单","id":1,"result":"true"}
insert operateLog {"desc":"更新订单","id":2,"result":"true"}

大家也可以看到,这样做的好处是通过抽取公共组件,使用aop切面方式实现流水日志输出,并且代码无侵入,满足开闭原则!

开闭原则正确姿势, 使用AOP优雅的记录日志, 非常的哇塞

如果这篇【文章】有帮助到你💖,希望可以给我点个赞👍,创作不易,如果有对Java后端或者对spring, 分布式, 云原生感兴趣的朋友,请多多关注💖💖💖
👨‍🔧 个人主页 : 阿千弟
文章来源地址https://www.toymoban.com/news/detail-462670.html

到了这里,关于开闭原则正确姿势, 使用AOP优雅的记录日志, 非常的哇塞的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用Spring Boot AOP实现日志记录

    目录 介绍 1.1 什么是AOP 1.2 AOP体系与概念 AOP简单实现 2.1 新建一个SpringBoot项目,无需选择依赖 2.2 设置好本地Maven配置后,在pom.xml文件里添加添加maven依赖 2.3 创建一个业务类接口 2.4 在实体类实现接口业务  2.5 在单元测试运行结果 2.6 创建切面类 2.7 再次运行测试  总结 1.

    2024年02月14日
    浏览(54)
  • Java使用Aop实现用户操作日志记录(新手入门)

    导入打印日志,aop,hutool,的依赖,Hutool是一个Java工具包,里面封装了大量的常用工具类,到时候咱们就通过这个工具包中有一个工具类可以用来获取客户端IP地址。 自定义操作类型枚举类 因为基本是增删改查四个方法 自定义用来记录用户操作日志的注解 写一个方法加上我

    2024年02月06日
    浏览(46)
  • 【设计模式之美】SOLID 原则之二:开闭原则方法论、开闭原则如何取舍

    具体的说,添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。 举例说明: 现在,如果我们需要添加一个功能,当每秒钟接口超时请求个数,超过某个预先设置的最大阈值时,我们也要触发告警

    2024年01月25日
    浏览(49)
  • spring boot 使用AOP+自定义注解+反射实现操作日志记录修改前数据和修改后对比数据,并保存至日志表

    使用FieldMeta自定义注解,看个人业务自行觉得是否需要重新定义实体 实现类 :通过该实现类获取更新前后的数据。 该实现类的实现原理为:获取入参出入的id值,获取sqlSessionFactory,通过sqlSessionFactory获取selectByPrimaryKey()该方法,执行该方法可获取id对应数据更新操作前后的数

    2024年01月23日
    浏览(51)
  • 【设计模式】设计原则-开闭原则

    定义 作用 1、方便测试;测试时只需要对扩展的代码进行测试。 2、提高代码的可复用性;粒度越小,被复用的可能性就越大。 3、提高软件的稳定性和延续性,易于扩展和维护。 实现方式 通过“抽象约束、封装变化”来实现开闭原则。通过接口或者抽象类为软件实体定义一

    2024年02月15日
    浏览(36)
  • 设计模式-原则篇-01.开闭原则

    ​ 可以把设计模式理解为一套比较成熟并且成体系的建筑图纸,经过多次编码检验目前看来使用效果还不错的软件设计方案。适用的场景也比较广泛,在使用具体的设计模式之前先要学习软件设计的基础 “软件设计原则”,后面的23个设计模式都是遵从“软件设计原则演变而

    2024年02月09日
    浏览(42)
  • 软件设计模式原则(二)开闭原则

    继续讲解第二个重要的设计模式原则——开闭原则~ 一.定义         开闭原则,在面向对象编程领域中,规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为

    2024年02月06日
    浏览(48)
  • 面向对象设计的六大原则(SOLID原则)-——开闭原则

    开闭原则(Open-Closed Principle, OCP)是面向对象设计的五大SOLID原则之一。这个原则主张“软件实体(类、模块、函数等)应该对扩展开放,对修改关闭”。也就是说,软件的设计应该允许在不修改原有代码的情况下增加新的功能。这样的设计有助于降低代码的复杂性和维护成本

    2024年03月12日
    浏览(59)
  • C#设计模式之--六大原则 开闭原则

    设计模式六大原则是单一职责原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则、开闭原则。它们不是要我们刻板的遵守,而是根据实际需要灵活运用。只要对它们的遵守程度在一个合理的范围内,努为做到一个良好的设计。本文主要介绍一下.NET(C#) 开闭原则

    2024年02月10日
    浏览(46)
  • 设计模式——开闭原则

    开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的

    2024年02月11日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包