AOP与SpringBoot使用AOP实例

AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实就是面向特定方法编程。

动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。

使用场景:

        记录操作日志、权限控制、事务管理......

优势:

        代码无入侵、减少重复代码、提高开发效率、维护简单

 AOP的核心概念

JoinPoint(连接点):被拦截的点。Spring中指可以被动态代理拦截目标类的方法。

PointCat(切入点):指要对哪些JointPoint进行拦截,既被拦截的连接点。
Advice(通知):拦截JoinPoint后要做的事。既对切入点增强的内容。
Target(目标): 指代理的目标对象。
Weavìng(植入):指把增强代码应用到目标上,生成代理对象的过程。
Proxy (代理):生成的代理对象
Aspect(切面):切入点和通知的结合。

 AOP 的通知类型

  • @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行
  • @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
  • @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
  • @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行

@Around环绕通知需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行 @Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值

 AOP的通知顺序

当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行。

1、不同切面类中,默认按照切面类的类名字母排序:

        目标方法前的通知方法:字母排名靠前的先执行

        目标方法后的通知方法:字母排名靠前的后执行

2、用 @Order(数字) 加在切面类上来控制顺序

        目标方法前的通知方法:数字小的先执行

        目标方法后的通知方法:数字小的后执行

 切入点表达式

切入点表达式:描述切入点方法的一种表达式

作用:主要用来决定项目中的哪些方法需要加入通知

常见形式:

        1、execution(……):根据方法的签名来匹配

        2、@annotation(注解名) :根据注解匹配

 切入点表达式-execultion

execution 主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,

语法为:execution(访问修饰符?  返回值  包名.类名.?方法名(方法参数) throws 异常?)

其中带 ? 的表示可以省略的部分

访问修饰符:可省略(比如: public、protected)

包名.类名: 可省略

throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

 根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式。

 书写建议:

         所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是 find 开头,更新类方法都是 update开头。

        描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性。

        在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用 ..,使用 * 匹配单个包。       

  切入点表达式-@annotation


 AOP案例

将增、删、改 相关接口的操作日志记录到数据库表中。

日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时参数、返回值、方法执行时长

需要对所有业务类中的增、删、改 方法添加统一功能,使用 AOP 技术最为方便

切入点表达式:利用exclution或@annotation都可,这里我用@annotation

通知类型:用@Around环绕通知就可

数据库设计

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_log
-- ----------------------------
DROP TABLE IF EXISTS `sys_log`;
CREATE TABLE `sys_log`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `create_user` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `create_time` datetime NOT NULL,
  `class_name` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `method_name` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `method_params` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `return_value` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `cost_time` mediumtext CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '日志信息类' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OperatorLog {
    private Integer id; //ID
    private String createUser; //操作人ID
    private LocalDateTime createTime; //操作时间
    private String className; //操作类名
    private String methodName; //操作方法名
    private String methodParams; //操作方法参数
    private String returnValue; //操作方法返回值
    private Long costTime; //操作耗时

    public OperatorLog(String createUser, LocalDateTime createTime, String className, String methodName, String methodParams, String returnValue, Long costTime) {
        this.createUser = createUser;
        this.createTime = createTime;
        this.className = className;
        this.methodName = methodName;
        this.methodParams = methodParams;
        this.returnValue = returnValue;
        this.costTime = costTime;
    }
}

Mapper

@Mapper
public interface LogMapper {

    @Insert("insert into sys_log (create_user,create_time,class_name,method_name,method_params,return_value,cost_time)" +
            "values (#{createUser},#{createTime},#{className},#{methodName},#{methodParams},#{returnValue},#{costTime});")
    void addLog(OperatorLog log);
}

自定义注解

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

导入AOP的依赖

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

自定义AOP

@Slf4j
@Configuration
@Aspect
public class CustomizationAop {
    @Autowired
    HttpServletRequest request;

    @Autowired
    EmpMapper empMapper;

    @Autowired
    LogMapper logMapper;

    @Around("@annotation(com.huang.anno.OperatorLog)")
    public Object around(ProceedingJoinPoint p) throws Throwable{
        //获取token
        String token = request.getHeader("token");
        Claims claims = JwtUtils.parseJwt(token);
        //获取操作人名字
        Integer id = (Integer) claims.get("id");
        String operatorName = empMapper.getOneById(id).getName();
        //获取现在时间
        LocalDateTime now = LocalDateTime.now();
        //获取类名
        String className = p.getTarget().getClass().getName();
        //获取方法名
        String methodName = p.getSignature().getName();
        //获取参数名
        Object[] args = p.getArgs();
        String params = Arrays.toString(args);
        //方法执行前时间
        long begin = System.currentTimeMillis();
        //方法执行
        Object result = p.proceed();
        //方法执行后时间
        long end = System.currentTimeMillis();
        //获取执行时间
        long costTime = end - begin;
        //方法返回值
        String returnValue = JSONObject.toJSONString(result);
        if (returnValue.length()>255) {
            returnValue = returnValue.substring(0, 255);
        }

        OperatorLog operatorLog = new OperatorLog(operatorName, now, className, methodName, params, returnValue, costTime);
        logMapper.addLog(operatorLog);

        log.info("日志记录:{}",operatorLog);
        return result;
    }
}

实现

    @GetMapping
    @OperatorLog
    public Result page(@RequestParam(required = false) String name,
                       @RequestParam(required = false) Short gender,
                       @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
                       @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end,
                       @RequestParam(defaultValue = "1") Integer page,
                       @RequestParam(defaultValue = "10") Integer pageSize){
        PageBean pageBean = empService.page(name,gender,begin,end,page,pageSize);
        return Result.success(pageBean);
    }

测试

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/12309.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows使用Dockers+battery historian踩坑记

1、首先&#xff0c;需要翻墙。 2、然后安装Dockers&#xff0c;网上好多博客说安装Docker Toolbox&#xff0c;我亲测无效&#xff0c;卸载后安装Docker for Windows&#xff0c;安装完成后打开&#xff0c;会提示&#xff1a; Hardware assisted virtualization and data e…

Mybatis03学习笔记

目录 使用注解开发 设置事务自动提交 mybatis运行原理 注解CRUD lombok使用&#xff08;偷懒神器&#xff0c;大神都不建议使用&#xff09; 复杂查询环境&#xff08;多对一&#xff09; 复杂查询环境&#xff08;一对多&#xff09; 动态sql环境搭建 动态sql常用标签…

大数据实战 --- 淘宝用户行为

目录 开发环境 数据描述 功能需求 数据准备 数据清洗 用户行为分析 找出有价值的用户 开发环境 HadoopHiveSparkHBase 启动Hadoop&#xff1a;start-all.sh 启动zookeeper&#xff1a;zkServer.sh start 启动Hive&#xff1a; nohup hiveserver2 1>/dev/null 2>…

生成树端口选举

所有交换机运行RSTP,SW1优先级4096,SW2优先级4096,SW3优先级8192,SW1的G0/0/1、G0/0/2接口通过手动模式加入Eth-Trunk 1,SW1的G0/0/3、G0/0/4接口通过手动模式加入Eth-Trunk 2,SW2的G0/0/1、G0/0/2接口通过手动模式加入Eth-Trunk 1,SW3的G0/0/1、G0/0/2接口通过手动模式…

【Python】Python读写.xlsx文件(基本操作、空值补全等)

【Python】Python读写.xlsx文件&#xff08;Pandas&#xff09; 文章目录 【Python】Python读写.xlsx文件&#xff08;Pandas&#xff09;1. 介绍2. Pandas读写xlsx文件2.1 基本操作2.1.1 实现任务2.1.2 代码2.1.3 结果 2.2 进阶操作2.2.1 写操作2.2.2 查看数据表的基本信息2.2…

电脑有自带的录屏功能吗?电脑录屏如何录人脸

案例&#xff1a;所有电脑都有自带的录屏功能吗&#xff1f; “在网上了解到电脑有录屏功能&#xff0c;但是我在我的电脑上又找不到。想问问小伙伴们是所有的电脑都有自带的录屏功能吗&#xff1f;怎样才能找到电脑自带的录屏功能&#xff1f;” 在日常使用电脑时&#xff0…

Python 无监督学习实用指南:1~5

原文&#xff1a;Hands-on unsupervised learning with Python 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形象&#xff0c;只关…

封装通用el-form表单(2种方式)

1、序言 项目地址&#xff1a;git clone form-demo: 封装通用el-form 一个后台管理系统最常见的是表单&#xff0c;表单最常见的是输入框、下拉选择、日期选择、单选、复选框等等&#xff0c; 系统添加若干模块&#xff0c;就复制粘贴若干个el-form、el-form-item&#xff0c;有…

重学Java设计模式-行为型模式-责任链模式

重学Java设计模式-行为型模式-责任链模式 内容摘自&#xff1a;https://bugstack.cn/md/develop/design-pattern/2020-06-18-重学 Java 设计模式《实战责任链模式》.html#重学-java-设计模式-实战责任链模式「模拟618电商大促期间-项目上线流程多级负责人审批场景」 责任链模…

Shell 脚本编程

1. shell 概述 &#x1f95e; shell 是一个命令行解释器&#xff0c;它能接受应用程序、用户 的命令&#xff0c;然后调用操作系统内核。 ⭐ 还是一门 功能强大的编程语言&#xff0c;易编写、易调试、灵活性强。 2. shell入门 &#xff08;1&#xff09;脚本格式 &#x1f…

js中 = 等号赋值的问题,Js中对象的引用问题,深浅拷贝

js "" 赋值符号 在js中 “”对于基本数据类型是赋值符号&#xff0c;比较&#xff08; 或 &#xff09;的时候是值&#xff1b;对于引用数据类型-对象来说 是地址引用&#xff0c;比较的时候是比较的地址。 基本数据类型和引用数据类型的比较 let a 3; let b a;…

离散数学_九章:关系(1)

关系 9.1关系及其性质 1、二元关系 2、集合A上的关系 3、n元素集合 有多少个关系&#xff1f; 4、关系的性质 1. 自反 2. 对称 3. 反对称 4. 传递 5、关系的组合 关系的合成 关系的幂 9.1关系及其性质 1、二元关系 设A和B是集合&#xff0c;一个从 A 到 B 的二元关…

stm32当中GPIO输出知识点汇总(GPIO的八种模式及其原理)

一、GPIO工作模式. 1. 四种输入模式 GPIO_Mode_IN_FLOATING 浮空输入模式 GPIO_Mode_IPU 上拉输入模式 GPIO_Mode_IPD 下拉输入模式 GPIO_Mode_AIN 模拟输入模式 2. 四种输出模式 GPIO_Mode_Out_OD 开漏输出模式 GPIO_Mode_Out_PP 推挽输出模式 GPIO_Mod…

CentOS7-部署Tomcat并运行Jpress

1. 简述静态网页和动态网页的区别。 2. 简述 Webl.0 和 Web2.0 的区别。 3. 安装tomcat8&#xff0c;配置服务启动脚本&#xff0c;部署jpress应用。1、简述静态网页和动态网页的区别 静态网页&#xff1a; 请求响应信息&#xff0c;发给客户端进行处理&#xff0c;由浏览器进…

目标检测基础之IOU计算

目标检测基础之IOU计算 概念理解——什么是IOUdemo后记 概念理解——什么是IOU IOU 交并比&#xff08;Intersection over Union&#xff09;&#xff0c;从字面上很容易理解&#xff1a;计算交集在并集的比重。从网上截张图看看 I O U A ∩ B A ∪ B IOU \frac{A \cap B}…

基于BenchmarkSQL的Oracle数据库tpcc性能测试

基于BenchmarkSQL的Oracle数据库tpcc性能测试 安装BenchmarkSQL及其依赖安装软件依赖编译BenchmarkSQL BenchmarkSQL props文件配置数据库用户配置BenchmarkSQL压测装载测试数据TPC-C压测&#xff08;固定事务数量&#xff09;TPC-C压测&#xff08;固定时长&#xff09;生成测…

[ 云原生 | Docker ] 构建高可用性的 SQL Server:Docker 容器下的主从同步实现指南

文章目录 一、前言二、SQL Server 主从同步的原理介绍三、具体的搭建过程3.1 准备工作3.1.1 卸载旧版本&#xff08;如果有&#xff0c;可选&#xff0c;非必须&#xff09;3.1.2 安装 Docker3.1.3 验证本地 Docker 是否安装成功 3.2 创建 Docker 网络3.3 创建主从节点的 SQL S…

[Linux系统]系统安全及应用一

系统安全及应用 一、账号安全基本措施1.1系统账号清理1.1.1将非登录用户的shell设为/sbin/nologin1.1.2锁定长期不使用的账号1.1.3删除无用的账号1.1.4锁定账号文件文件chattr1.1.5查看文件校验和md5sum 1.2密码安全控制1.2.1设置密码有效期 1.3历史命令限制1.3.1 减少记录命令…

C语言笔记 | 一元三次方程

文章目录 0x00 前言 0x01 问题分析 0x02 代码设计 0x03 完整代码 0x04 运行效果 0x05 参考文献 0x06 总结 0x00 前言 在 1545 年&#xff0c;意大利学者卡丹所写的《关于代数的大法》中&#xff0c;提出了一元三次方程的求根公式。人们将其称为卡丹公式。对于标准型的一…

港科夜闻|国务院港澳办主任夏宝龙在香港科大考察期间,表示对学校开展创科工作的鼓励及希望...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、国务院港澳办主任夏宝龙在香港科大考察期间&#xff0c;表示对学校开展创科工作的鼓励及希望。考察期间&#xff0c;夏宝龙主任参观了香港科大的空气动力学和声学实验中心&#xff0c;以及香港科大先进显示与光电子技术国…