AOP~面向切面编程介绍

news2025/1/14 0:54:53

AOP基础

概述

  • AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),面向特定方法的编程。

  • 动态代理是面向切面编程最主流的实现。

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

入门程序

  • 需求

    • 统计各个业务层方法的执行耗时

  • 步骤

    • 导入依赖

    • <!--        AOP依赖-->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>
    • 编写代码

    • package com.testpeople.aop;
      
      import lombok.extern.slf4j.Slf4j;
      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.annotation.Around;
      import org.aspectj.lang.annotation.Aspect;
      import org.springframework.stereotype.Component;
      
      @Component
      @Slf4j
      @Aspect //AOP类
      public class TimeAspect {
      
      
          @Around("execution(* com.testpeople.service.*.*(..))") //切入点表达式
          public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
              //1.记录开始时间
              long begin = System.currentTimeMillis();
              //2.调用原始方法运行
              Object result = joinPoint.proceed();
              //3.记录结束时间
              long end = System.currentTimeMillis();
      
              //日志
              log.info(joinPoint.getSignature()+"方法执行耗时:{}ms",end-begin);
      
              return result;
      
          }
      
      }
    • 效果

  


优势

  • 场景

    • 记录日志

    • 权限控制

    • 事务管理

  • 优势

    • 代码无侵入

    • 减少重复代码

    • 提高开发效率

    • 维护方便

核心概念

  • 连接点

    • JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)

  • 通知

    • Advice,指哪些重复的逻辑,也就是共性功能(体现为一个方法)

  • 切入点

    • PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用

  • 切面

    • Aspect,描述通知与切入点的对应关系(通知+切入点)

  • 目标对象

          Target,通知所应用的对象

图解

流程

  • 进行开发后,使用的是代理对象,而不是实际对象。

AOP进阶

通知类型

  1. @Around:环绕通知,此注解标注的通知在目标方法前,后都被执行。

  2. @Before: 前置通知,此注解标注的通知方法在目标方法前被执行。

  3. @After: 后置通知,此注解标注的通知方法在目标方法被执行,无论是否异常都会执行。

  4. @AfterReturning: 返回后通知,此注解标注的通知方法在,目标方法后被执行,有异常不执行。

  5. @AfterThrowing: 异常后通知,此注解标注的通知方法发生异常后执行。

测试

package com.testpeople.aop;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Aspect
public class MyAspect {

    @Before("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")
    public void before(){

        log.info("前置通知");

    }

    @Around("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        log.info("环绕通知"+"before");

        Object result = joinPoint.proceed();

        log.info("环绕通知"+"after");

        return result;

    }

    @After("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")
    public void after(){
        log.info("后置通知");

    }

    @AfterReturning("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")
    public void afterReturning(){
        log.info("后置返回通知");
    }

    @AfterThrowing("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")
    public  void afterThrowing(){

        log.info("后置异常通知");
    }



}

效果

注意

  • @Around:环绕通知需要自己调用 ProceedingJoinPoint.proceed();

  • @Around:环绕通知方法的返回值,必须指定为Object,来接受原始方法的返回值。

tips

  • 切入点表达式抽取

package com.testpeople.aop;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Aspect
public class MyAspect {

    @Pointcut("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")
    private void pt(){

    }
    //这个方法可以改修订范围,然后被别的包引用。


    @Before("pt()")
    public void before(){

        log.info("前置通知");

    }

    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        log.info("环绕通知"+"before");

        Object result = joinPoint.proceed();

        log.info("环绕通知"+"after");

        return result;

    }

    @After("pt()")
    public void after(){
        log.info("后置通知");

    }

    @AfterReturning("pt()")
    public void afterReturning(){
        log.info("后置返回通知");
    }

    @AfterThrowing("pt()")
    public  void afterThrowing(){

        log.info("后置异常通知");
    }



}

通知顺序

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

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

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

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

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

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

  • 概述

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

  • 作用

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

  • 常见形式

    • execution(...);根据方法的签名来匹配

  • 特殊符号

  • 如果匹配两个方法,可以使用“||”连接。

  • Tips

  • @annotation(...);根据注解匹配

    • 新建注解

    • package com.testpeople.aop;
      
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
      
      @Retention(RetentionPolicy.RUNTIME) //合适生效(运行是)
      @Target(ElementType.METHOD)//作用到哪里(方法)
      public @interface MyLog {
      
      
      }
    • 在需要添加方法上添加注解,更换切入点表达式。

    • @Around("@annotation(com.testpeople.aop.MyLog)")

连接点

  • 开发

  • @Around("@annotation(com.testpeople.aop.MyLog)")
    public Object testJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
    
        //1.获取 目标对象的类名
        String className = joinPoint.getTarget().getClass().getName();
        log.info(className);
        //2.获取 目标方法的方法名
        String methodName = joinPoint.getSignature().getName();
        log.info(methodName);
        //3.获取 目标方法运行时传入的参数
        Object[] methodArgs = joinPoint.getArgs();
        log.info(Arrays.toString(methodArgs));
        //4.放行 目标方法执行
        Object result = joinPoint.proceed();
    
        //5.获取 目标方法的返回值
        log.info(result.toString());
    
        return result;//此处可以改变函数的返回结果(添加其他方法)
    }

效果

AOP案例(操作日志记录功能)

需求

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

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

思路

步骤

  • 准备

    • 在案例中引入AOP的依赖

    • <!--        AOP依赖-->
      
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>
    • 设计好数据库表

    • # 操作日志记录表
      create table operate_log(
          id int unique primary key auto_increment comment 'ID',
          operate_user int unsigned comment '操作人ID',
          operate_time datetime comment '操作时间',
          class_name varchar(100) comment '操作类名',
          method_name varchar(100) comment '操作方法名',
          method_params varchar(1000) comment '操作方法参数',
          return_value varchar(2000) comment '返回值',
          cost_time bigint comment '耗时,单位:ms'
      ) comment '操作日志记录表';
    • 设计好实体类

    • package com.testpeople.pojo;
      
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.time.LocalDateTime;
      
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class OperateLog {
      
          private Integer id;//ID
      
          private Integer operateUser;//操作人ID
      
          private LocalDateTime operateTime;//操作时间
      
          private String className;//操作类名
      
          private String methodName;//方法名
      
          private String methodParams;//方法参数
      
          private String returnValue;//返回值
      
          private Long costTime;//耗时
      
      }
    • Mapper接口

    • package com.testpeople.mapper;
      
      import com.testpeople.pojo.OperateLog;
      import org.apache.ibatis.annotations.Delete;
      import org.apache.ibatis.annotations.Insert;
      import org.apache.ibatis.annotations.Mapper;
      
      @Mapper
      public interface OperateLogMapper {
      
          //插日志数据
      
          @Insert("insert into operate_log (operate_user,operate_time,class_name,method_name,method_params,return_value,cost_time) " +
                  "values(#{operateUser},#{operateTime},#{className},#{methodName},#{methodParams},#{returnValue},#{costTime})")
          void insert(OperateLog operateLog);
      
      }
    • 编码

      • 自定义注解

      • package com.testpeople.anno;
        
        import java.lang.annotation.ElementType;
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        import java.lang.annotation.Target;
        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.METHOD)
        public @interface Log {
        }
      • 定义切面类,完成记录操作日志的逻辑

      • package com.testpeople.aop;
        
        import com.alibaba.fastjson.JSONObject;
        import com.testpeople.mapper.OperateLogMapper;
        import com.testpeople.pojo.OperateLog;
        import com.testpeople.utils.JwtUtils;
        import lombok.extern.slf4j.Slf4j;
        import org.aspectj.lang.ProceedingJoinPoint;
        import org.aspectj.lang.annotation.Around;
        import org.aspectj.lang.annotation.Aspect;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Component;
        
        import javax.servlet.http.HttpServletRequest;
        import java.time.LocalDateTime;
        import java.util.Arrays;
        
        @Slf4j
        @Component
        @Aspect
        public class LogAspct {
        
            @Autowired
            private HttpServletRequest request;
        
            @Autowired
            private OperateLogMapper operateLogMapper;
        
            @Around("@annotation(com.testpeople.anno.Log)")
            public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
        
                //操作人ID~ 当前员工ID
                //获取请求头中的jwt令牌中的员工ID
                String jwt = request.getHeader("token");
                //解析
                Integer operateUser = (Integer) JwtUtils.parseJwt(jwt).get("id");
                //操作时间
                LocalDateTime operateTime = LocalDateTime.now();
                //类名
                String className = joinPoint.getTarget().getClass().getName();
                //方法名
                String methodName = joinPoint.getSignature().getName();
                //方法参数
                Object[] args = joinPoint.getArgs();
                String methodParams = Arrays.toString(args);
        
                //记录时间
                long begin = System.currentTimeMillis();
        
                //调用原始目标方法运行
                Object result = joinPoint.proceed();
                
                //记录结束时间
                
                long end = System.currentTimeMillis();
                
                //方法返回值
                String returnValue = JSONObject.toJSONString(result);
                //耗时
                long costTime = end - begin;
        
        
                //记录操作日志
        
                OperateLog operateLog = new OperateLog();
        
                operateLog.setOperateUser(operateUser);
                operateLog.setOperateTime(operateTime);
                operateLog.setClassName(className);
                operateLog.setMethodName(methodName);
                operateLog.setMethodParams(methodParams);
                operateLog.setReturnValue(returnValue);
                operateLog.setCostTime(costTime);
        
                operateLogMapper.insert(operateLog);
        
                log.info("AOP记录操作日志 {}",operateLog.toString()+"\n");
        
                return result;
        
            }
        
        
        }
      • 给有需求的类添加@Log注解

效果

注意

  • 获取当前用户

    • 从request中获取token 在token中提取当前用户id


以上是对SpringBoot框架中的AOP相关的介绍以及简单的使用,通过一个操作记录日志功能进行练习。多点关注、多点爱。(以上知识点笔记来自于小编学习黑马程序员的课程所记录)

项目地址

admin_web_project: 黑马程序员项目javaWebjavaWeb开发学习仓库,前后端分离项目前端Vue后端springboot数据库Mysql

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【基础篇】Docker 架构与组件 TWO

嗨&#xff0c;小伙伴们&#xff01;我是小竹笋&#xff0c;一名热爱创作的工程师。上一篇我们聊了聊 Docker 的历史与发展、与虚拟机的对比以及它在行业中的应用。今天&#xff0c;让我们更进一步&#xff0c;深入探讨 Docker 的架构与关键组件。 欢迎订阅公众号&#xff1a;…

Fantastic-admin:Vue 中后台管理系统

Fantastic-admin&#xff1a;Vue 中后台管理系统 在当今的前端开发世界里&#xff0c;fantastic-admin 作为一款功能强大的 Vue 中后台管理系统框架&#xff0c;简直是开发者的福音。本文将介绍 fantastic-admin 的基本信息、特点&#xff0c;以及如何快速上手和使用。 项目简介…

亚信安慧AntDB亮相PostgreSQL中国技术大会,获“数据库最佳应用奖”并分享数据库应用实践

7月12日&#xff0c;第13届PostgreSQL中国技术大会在杭州顺利举办&#xff0c;亚信安慧AntDB数据库荣获“数据库最佳应用奖”。大会上&#xff0c;亚信安慧AntDB数据库同事带来《基于AntDB的CRM系统全域数据库替换实践》和《亚信安慧AntDB数据库运维之路》两场精彩演讲&#xf…

AWS资源购买和使用:亚马逊云显示配额不足怎么解决

亚马逊云显示配额不足是许多AWS用户在使用云服务时可能遇到的常见问题。这种情况通常发生在用户试图启动新的EC2实例或扩展现有资源时&#xff0c;系统提示已达到特定资源类型的限制。本文中九河云将跟你们一起探讨这一问题的原因及其解决方案。 首先&#xff0c;了解AWS配额的…

最便宜的iPhone SE 4要来,配置爆炸但砍了灵魂

前几天乐视刚刚发布了「低配青春 AIR 版 iPhone SE 4」&#xff0c;电友们就坐不住了&#xff0c;纷纷问真 iPhone SE 4 究竟什么时候才能来。 阿红也去挖了一下情报&#xff0c;别说&#xff0c;还真在路上了。 众所周知&#xff0c;最近一代&#xff0c;也就是第三代 iPhone…

Liquibase 增加一列主键,自动生成 UUID:最佳实践与深度解析

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 自动生成 UUID UUID 是一种全局唯一的标识符&#xff0c;通常由 32 个十六进制数字组成&#xff0c;分为五组&#xff0c;形式如下&#xff1a; 123e4567-e89b-12d3-a456-426614174000在 Java 中&#xff0c;可以使…

从零开始写 Docker(十九)---增加 cgroup v2 支持

本文为从零开始写 Docker 系列第十九篇&#xff0c;添加对 cgroup v2 的支持。 完整代码见&#xff1a;https://github.com/lixd/mydocker 欢迎 Star 推荐阅读以下文章对 docker 基本实现有一个大致认识&#xff1a; 核心原理&#xff1a;深入理解 Docker 核心原理&#xff1a…

解锁未来安全:WT2003HP8芯片如何重塑智能电子锁的安全性与智能化体验

WT2003HP8混音芯片&#xff1a;智能电子锁的声音守护者 在当今这个追求智能化、便捷化的时代&#xff0c;智能电子锁作为智能家居的重要组成部分&#xff0c;正逐步改变着我们的生活方式。而WT2003HP8混音芯片&#xff0c;凭借其卓越的性能和广泛的应用潜力&#xff0c;在智能…

AFSim 仿真系统--任务处理器入门指南

任务处理器 任务处理器&#xff08;WSF_TASK_PROCESSOR&#xff09;是一个WSF处理器&#xff0c;它提供了检查轨迹管理器中的数据&#xff08;轨迹&#xff09;并对其采取行动的能力。这些行动包括&#xff1a; *向下属分配任务 *激活或停用传感器或干扰器 *开火武器 *操纵平台…

德国航空航天中心(DLR)利用元数据整理归纳Confluence

对于大型企业&#xff0c;在广阔的Confluence空间内找到信息&#xff0c;如同迷失在迷宫中。多年来&#xff0c;德国航空航天中心&#xff08;DLR&#xff09;依赖电子邮件和传统文件管理系统。在知识经理Andr Pliewischkies的领导下&#xff0c;公司引入了Confluence以减少信息…

Layui修改表格分页为英文

Layui修改表格分页为英文 1.前言2.Laypage属性 1.前言 主要记录初次使用Layui没有好好看官方文档踩坑&#xff0c;修改了源码才发现可以自定义 使用的Layui版本2.9.14 2.Laypage属性 Laypage属性中带的有自定义文本的属性 示例代码 table.render({.......page: {skipText: …

[米联客-安路飞龙DR1-FPSOC] SDK入门篇连载-09 PL AXI-GPIO实验

软件版本&#xff1a;Anlogic -TD5.9.1-DR1_ES1.1 操作系统&#xff1a;WIN10 64bit 硬件平台&#xff1a;适用安路(Anlogic)FPGA 实验平台&#xff1a;米联客-MLK-L1-CZ06-DR1M90G开发板 板卡获取平台&#xff1a;https://milianke.tmall.com/ 登录“米联客”FPGA社区 ht…

buu做题(9)

[MRCTF2020]PYWebsite 有个二维码 扫了一下啊二维码 function enc(code){hash hex_md5(code);return hash;}function validate(){var code document.getElementById("vcode").value;if (code ! ""){if(hex_md5(code) "0cd4da0223c0b280829dc3ea4…

【C语言报错已解决】“Undefined Reference”

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引言&#xff1a; 在开发过程中&#xff0c;我们经常会遇到各种编译错误或运行时错误。其中&#xff0c;“Undefined Referenc…

JavaFX布局-AnchorPane

JavaFX布局-AnchorPane 常用属性padding 实现方式Javafxml 将子节点锚定到容器的边界上&#xff0c;指定子节点相对于 AnchorPane 的四个边界&#xff08;上、下、左、右&#xff09;的距离适合宽高固定的一些表单如果允许最大化&#xff0c;拖动大小&#xff0c;需要自己计算子…

(面试必看!)一些和多线程相关的面试考点

文章导读 引言考点1. CAS 指令&#xff08;重点&#xff09;一、什么是CAS二、CAS 的优点三、CAS 的缺点四、ABA问题五、相关面试题 考点2. 信号量&#xff08;semaphore&#xff09;一、基本概念二、信号量的主要操作三、信号量的应用四、相关面试题 考点3、CountDownLatch 类…

万亿赛道 !二十届三中全会关于大规模设备更新措施深度解读 2024 !

第二十届三中全会审议通过《中共中央关于进一步全面深化改革、推进中国式现代化的决定》&#xff0c;涵盖300多项重要改革举措。总体而言&#xff0c;本次会议在财税体制改革、金融体制改革、统一大市场等方面做出了重点部署。此外&#xff0c;本次新增“高质量发展体制机制”作…

苹果手机丢了如何定位?3个技巧教你解决

在这个移动互联网高度发达的时代&#xff0c;苹果手机已成为我们日常生活中不可或缺的伴侣。然而&#xff0c;手机丢失的风险也随之增加。一旦手机不幸落入他人之手&#xff0c;不仅财产安全堪忧&#xff0c;个人隐私也可能面临泄露风险。那么&#xff0c;手机丢了如何定位呢&a…

【代码随想录训练营第42期 Day7打卡 LeetCode 454.四数相加II 383. 赎金信 15. 三数之和 18. 四数之和

目录 一、做题心得 二、题目及题解 454.四数相加II 题目链接 题解 383. 赎金信 题目链接 题解 15. 三数之和 题目链接 题解 18. 四数之和 题目链接 题解 三、小结 一、做题心得 今天是代码随想录训练营打卡的第七天&#xff0c;做的也是同昨天一样的哈希表部分…

电源防反接电路设计——NMOS管

电源电压接入正确时&#xff0c;由于MOS管中的寄生二极管的存在&#xff0c;从而使得MOS管的Vgs电压为输入电压减去寄生二极管压降电压0.7V&#xff0c;这个电压是大于MOS开关导通的阈值电压&#xff0c;从而使MOS管导通&#xff0c;导通后相当于寄生二极管被MOS管导通短路&…