【Spring】AOP实例—日志模块的实现

2023-11-16

AOP实例—日志模块

AOP能够使系统服务(例如:日志模块、安全模块、事务管理)模块化,并以声明的方式将它们应用到它们需要影响的组件中去。使业务组件会具有更高的内聚性并且会更加关注自身的业务,完全不需要了解涉及系统服务所带来复杂性。


日志模块就属于一种系统服务,业务组件不需要自己编写日志逻辑,而是将日志这种渗透到整个系统的服务切面化,利用AOP编写好日志逻辑,并声明到需要记录日志的组件当中。本文以项目中比较常用的日志为例,讲解AOP及注解的部分功能 。

1.切面化日志模块

1.1导入jar包

使用AOP首先需要导入AspectJ的jar包aspectjweaver-1.9.5.jar

我用的是springboot,可以直接在pom中配置spring-boot-starter-aop,通过maven导入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

可以看到aspectjweaver-1.9.5.jar就在spring-boot-starter-aop下。

在这里插入图片描述

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法。Spring Aop只是借助了AspectJ的AOP语法,即注解(例如@Aspect,@Pointcut)。

1.2自定义日志注解
@Target(ElementType.METHOD)	//将注解作用于方法上。(因为要在执行方法时打印日志)
@Retention(RetentionPolicy.RUNTIME)	// jvm加载后注解仍然存在,用于反射的实现
public @interface AopLog {
    // 方法所属模块
    String module() default "";
    // 方法具体内容
    String operation() default "";
}

需要什么就加什么。对于自定义注解的创建、处理及调用请参考 自定义注解

日志实体类:

public class AopLog {
    // 所属模块
    private String module;
    // 具体操作
    private String operation;
    // 执行时间
    private String execTime;

    public String getModule() {
        return module;
    }

    public void setModule(String module) {
        this.module = module;
    }

    public String getOperation() {
        return operation;
    }

    public void setOperation(String operation) {
        this.operation = operation;
    }

    public String getExecTime() {
        return execTime;
    }

    public void setExecTime(String execTime) {
        this.execTime = execTime;
    }

    @Override
    public String toString() {
        return "AopLog{" +
                "module='" + module + '\'' +
                ", operation='" + operation + '\'' +
                ", execTime='" + execTime + '\'' +
                '}';
    }
}
1.3切面声明

AOP的概念可以参考spring官网

import org.aspectj.lang.annotation.Aspect;

@Component	// 开启组建扫描
@Aspect	// 声明为切面
public class LogAspect {
    
    //声明一个Pointcut切入点
    @Pointcut("@annotation(com.zmxqq.annotation.AopLog)")//(切入点指示器+声明需要执行的方法)
    public void pointcut() {}// 切入点签名(访问修饰符+void+方法名+任何参数)
    
    // 声明一个‘切入点pointcut()’的环绕通知
    @Around("pointcut()")
    public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
        Object result;

        // ProceedingJoinPoint:正在增强的连接点,也就是正在执行的方法
        // 获取目标对象的Class对象
        Class<?> targetClass = pjp.getTarget().getClass();
        // 通过pjp获取当前执行方法的标签(包括方法名称,参数等)
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        // 根据方法名及参数获取指定的方法。
        Method targetMethod = getDeclaredMethod(targetClass,signature.getName(),signature.getMethod().getParameterTypes());
        // 获取当前方法的注解的对象(这里的AopLog是自定义注解)
        AopLog aopLogAnnotation = targetMethod.getAnnotation(AopLog.class);

        // 记录方法执行的时间与执行结束的时间
        long starTime = System.currentTimeMillis();
            // 调用proceed(),开始执行方法
        result = pjp.proceed();
        long endTime = System.currentTimeMillis();

        // 获取当前方法注解的参数,并存入到AopLog注解实体类中
        String module = aopLogAnnotation.module();
        String operation = aopLogAnnotation.operation();
            // 注解与实体类有一个写上全路径名避免名字冲突
        com.zmxqq.model.AopLog aopLog = new com.zmxqq.model.AopLog();
        aopLog.setModule(module);
        aopLog.setOperation(operation);
        aopLog.setExecTime((double)(endTime-starTime) + "秒");

        // 打印日志
        System.out.println(aopLog.toString());

        return result;
    }

    // 根据方法名,参数类型获取本类方法,如果没有就去父类中查找
    protected Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                return getDeclaredMethod(superClass, name, parameterTypes);
            }
        }
        return null;
    }
}

在controller层的方法中加上@AopLog日志注解,然后调用此方法:

@GetMapping("/Test/AopTest")
@AopLog(module = "测试模块", operation = "打印日志")
public void AopTest() {
    System.out.println("日志注解测试")
}

详细解读下都需要哪些步骤:

  1. 首先利用最开始导入的AspectJ的jar包所提供的注解@Aspect声明一个切面类,@Component注解开启组件扫描,让spring可以扫描到这个切面。

  2. 切面类内部用@Pointcut声明一个切入点(当然@Pointcut也是AspectJ包中的),格式如代码中所示,其中切入点指示器@annotation将只匹配给定注解的连接点(连接点是指目标对象的方法执行,切入点是连接点的集合),上述代码中就将匹配限制在了使用了com.zmxqq.annotation路径下的AopLog注解的连接点,即所有使用@AopLog注解的方法。[注意:切入点签名返回值类型必须是void](其他切入点指示器规则可以查看官网或我的Spring官方文档笔记

  3. 利用切入点限制了具体的方法之后,就需要对这些方法执行某些操作,也就是AOP所说的通知。通知与切入点表达式相关联,并在切入点匹配的方法执行之前、之后或周围运行。我这里用了环绕通知,其中ProceedingJoinPoint继承自JoinPoint,表示正在增强的连接点,也就是正在执行的方法。再通过反射获取正在执行的这个方法的注解与注解的参数信息,保存到数据库或打印这些信息。我这里打印出来:

    在这里插入图片描述

  4. 调用pjp.proceed();将执行当前方法并返回一个Object对象,代码中在方法执行前后记录了时间用来计算执行时间。这部分可以自由发挥。

一个简单的基于AOP的日志模块完成,此外还有① 前置通知 @Before,② 后置通知 @AfterReturning,③ 抛出异常后通知 @AfterThrowing等通知注解,可以自行扩展。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Spring】AOP实例—日志模块的实现 的相关文章

随机推荐

  • Mybatis高性能批量插入方法

    当使用Mybatis大量插入时可以利用MySQL语句的特性使原来多次请求插入的语句变成一次请求 以此提高插入效率 一般的插入方式 1000条数据 一条一条的插入 Test public void testInsert SqlSession
  • 【rust/egui】(十)使用painter绘制一些图形—connections

    说在前面 rust新手 egui没啥找到啥教程 这里自己记录下学习过程 环境 windows11 22H2 rust版本 rustc 1 71 1 egui版本 0 22 0 eframe版本 0 22 0 上一篇 这里 绘制连接 在上一节
  • Invalid attempt to spread non-iterable instance

    这一类错误大概率是你的延展运算符附近除了错误 比如我的就是应为在一个对象前面用了延展运算符 我将数组的中括号写成了大括号 就报了这个错误
  • Mac 系统的 MySQL 如何修改密码(保姆级别教程)

    要修改 Mac 系统上的 MySQL 密码 可以按照以下步骤进行 打开终端 以管理员身份登录到 MySQL 服务器 sudo mysql u root p 输入管理员密码 切换到 MySQL 数据库 use mysql 查看当前用户列表 s
  • 记录的Android开发过程中遇到的问题。

    180508 更新 网上下载demo 本地studio版本和demo版本不一致处理方式 修改两处 1 项目的build gradle 里面classpath 2修改项目目录下 gradle gt wrapper gt gradle wrap
  • openpcdet验证已训练好权重文件

    python tools test py cfg file 配置文件路径 ckpt dir 权重文件所在文件夹路径 eval all
  • Java静态修饰符static

    1 Satic注意事项 1 Static修饰的方法可以被类调用或者直接使用 而未被static修饰的方法是实例方法 属于对象的 必须用对象调用 2 类在方法区 方法在栈内存 对象在堆内存 3 静态只能访问静态 不能访问实例 实例可以访问静态
  • windows7的5次shift实验

    原理 在win7的登录界面连续按5次shift键会弹出程序c windows system32 sethc exe 在开启win7时会出现正常登录和尝试修复 在尝试修复界面利用txt文本打开C盘 修改cmd exe为sethc exe 并将
  • FastDFS文件同步机制简介

    FastDFS文件同步机制简介 本篇文章转载于FastDFS作者 余庆 大佬的 FastDFS分享与交流 公众号 FastDFS 文件同步采用 binlog 异步复制方式 storage server 使用 binlog 文件记录文件上传
  • c语言警告文件末尾没有换行符,关于c ++:“文件末尾没有换行符”警告,即使在换行后也是如此...

    我最近一直在努力学习C 直到今天一直都很顺利 我正在尝试创建一个非常简单的应用程序 它基本上只是要求用户输入一个数字 然后显示该数字的阶乘 当我尝试在Cygwin中编译文件 g factorial cpp o fact 时 我收到以下警告
  • 微信小程序触底加载scroll-view

    微信小程序触底加载 scroll view 了解什么是触底加载 需求 有个固定高度的容器 实现容器里面的内容触底加载 1 内容盒子的高度 2 盒子里内容的总高度 3 滚动条的scrollTop 触底加载的原理就是 当里面的容器触底的时候进行
  • CPU核心数,线程数,时间片轮转机制解读

    CPU的核心数 CPU个数 是指物理上 即硬件上的核心数 核心数 是逻辑上的 简单理解为逻辑上模拟出的核心数 线程数 是同一时刻设备能并行执行的程序个数 线程数 cpu个数 核数 区分CPU线程数与JAVA多线程的概念 CPU线程数 在CP
  • 计算机的计算单位

    容量单位 在物理层面 高低电平记录信息 理论上只认识0 1两种状态 0 1能够表示的内容太少了 需要更大的容量表示方法 0 1称为bit 比特位 字节 1Byte 8bits 硬盘商一般使用10进位标记容量 500G一般格式化后只剩465G
  • Postgresql ODBC驱动,用sqlserver添加dblink跨库访问postgresql数据库

    在同样是SQLserver数据库跨库访问时 只需要以下方法 declare rowcount int set rowcount 0 set rowcount select COUNT from sys servers where name
  • APP自动化测试-7.移动端web app自动化测试

    APP自动化测试 7 移动端纯web应用测试 文章目录 APP自动化测试 7 移动端纯web应用测试 前言 一 移动端应用分类简述 二 web app基础配置 1 基础信息获取 2 驱动配置 3 启动 三 元素定位 总结 前言 移动端应用细
  • 在虚拟机上ifconfig结果中eth0没有IP地址(inet4)而是显示的是inet6

    问题描述 在虚拟机上执行ifconfig 结果中eth0没有显示IP地址inet4 而是显示inet6 出现原因 虚拟机上没有连接网络 解决方法 启动网卡 执行命令 ifup eth0 衍生问题 当执行上述命令时 却报如下错误 some o
  • 小程序工作学习:值的传递与操作以及target,options区别

    最近做小程序相关的东 学艺不精原理不了解 在前端界面的问题上遇到很多问题 好在在别人的帮助下以及在查资料补漏过程中还是有点收获的 问题 一 关于请求中如何能把这个页面的一下参数传递给下一页面 不能总是重复调用接口访问后台 这样的话影响加载页
  • python中jupyter notebook安装教程、常用插件和拓展配置及基本使用(全面)

    文章目录 1 jupyter安装 2 jupyter常用插件配置 2 1 Jupyter Notebook和conda的环境和包管理工具nb conda 2 2 Jupyter Notebook扩展jupyter contrib nbext
  • c#基础知识---集合之排序列表(SortedList)

    SortedList 类代表了一系列按照键来排序的键 值对 这些键值对可以通过键和索引来访问 排序列表是数组和哈希表的组合 它包含一个可使用键或索引访问各项的列表 如果您使用索引访问各项 则它是一个动态数组 ArrayList 如果您使用键
  • 【Spring】AOP实例—日志模块的实现

    AOP实例 日志模块 AOP能够使系统服务 例如 日志模块 安全模块 事务管理 模块化 并以声明的方式将它们应用到它们需要影响的组件中去 使业务组件会具有更高的内聚性并且会更加关注自身的业务 完全不需要了解涉及系统服务所带来复杂性 日志模块