AOP与SpringBoot使用AOP实例

news2024/11/23 0:43:41

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);
    }

测试

 

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

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

相关文章

浅谈thrift协议+举例通用mockserver如何实现

目录 简单来说&#xff1a; 举个例子&#xff1a; 简单来说&#xff1a; 1&#xff09;是一个跨平台跨语言的通信协议&#xff0c;定义和创建跨语言服务&#xff0c;是一个高性能、轻量级RPC框架。 2&#xff09;开发者无需关注不同语言/相同语言服务间如何通信&#xff0c;…

基于html+css的图片展示15

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

什么是Python?

目录 Python简介 Python发展史 Python优点与缺点 Python现如今的应用领域 总结 Python简介 Python是一种高级、解释型、面向对象的动态编程语言&#xff0c;由Guido van Rossum在1989年创建&#xff0c;首次发布于1991年。 Python设计的哲学是优雅、清晰、简单&#xff0c…

Windows逆向安全(一)之基础知识(八)

if else嵌套 这次来研究if else嵌套在汇编中的表现形式&#xff0c;本次以获取三个数中最大的数这个函数为例子&#xff0c;分析if else的汇编形式 求三个数中的最大值 首先贴上代码&#xff1a; #include "stdafx.h"int result0; int getMax(int i,int j,int k)…

机器视觉工程师必须知道机器视觉精度要思考哪些

​在和客户交流项目技术要求,这个项目,我要求的精度是0.01mm? 第一个问题:什么是精度? 精度要求0.01mm: 1.视觉重复性极差?静态?动态? 2.视觉与第三方相关性差异?极差?相关性系数? 3.整体系统误差?机械重复性误差? 4.产品尺寸公差? 第二个问题:精度与公差…

完美解决丨TypeError: fun() takes 2 positional arguments but 3 were given

python def fun(a, b): return a b c fun(1, 2, 3) Traceback (most recent call last): File "test.py", line 5, in <module c fun(1, 2, 3) TypeError: fun() takes 2 positional arguments but 3 were given 上面的代码中&#xff0c; fun 函数定义了两…

最通俗的语言解释01背包问题(力扣416题javascript版本)

【声明】以下内容参考了代码随想录&#xff0c;不用作商业用途~ 先来看一个场景&#xff1a;有N件物品&#xff0c;背包最大的重量为W&#xff0c;第i件物品的重量为weight[i]&#xff0c;得到的价值为value[i]&#xff0c;每件物品只用一次&#xff08;即不能重复放进背包&…

【计算方法】正交区域查询---KD-Tree概念

一、说明 kd 树是一种二叉树数据结构&#xff0c;可以用来进行高效的 kNN 计算。kd 树算法偏于复杂&#xff0c;本篇将先介绍以二叉树的形式来记录和索引空间的思路&#xff0c;以便读者更轻松地理解 kd 树。 二、正交区域查找 2.1 定义 对于k维空间的张量数据表格&#xff0…

Unity RenderStreaming 云渲染3.1.0-exp.6 食用手册

Unity云渲染 &#x1f957;资源&#x1f364;兼容性&#x1f367;手机端连接&#x1f969;安装方法&#x1f35b;IP端口设置&#x1f371;官方案例尝鲜&#x1f332;导入案例&#x1f332;添加场景&#x1f332;启动WebApp&#x1f332;打开Menu场景&#x1f332;连接参数设置&…

300左右蓝牙耳机推荐哪个好?300元左右最好的蓝牙耳机

蓝牙耳机如今在我们的生活中太普遍了&#xff0c;记得疫情刚开始天天要戴口罩&#xff0c;口罩的绳子和耳机线相缠十分的不方便&#xff0c;所以更多的人选择蓝牙耳机&#xff0c;下面整理了几款300元左右的蓝牙耳机品牌。 一、南卡小音舱Lite2蓝牙耳机 售价&#xff08;&…

4-数据结构

数据结构&#xff08;data structure&#xff09; 1. 简介 数据结构是在计算机中组织与存储数据的方式 如果想要表示“一排数字”&#xff0c;自然想到使用「数组」数据结构 数组的存储方式可以表示数字的相邻关系、顺序关系&#xff0c;但至于其中存储的是整数int&#xff0c…

【数据结构:线性表】顺序表

⚡线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直…

网络请求实战-RESTFUL约定和Postman工具

RESTFUL协议 表现层状态转化&#xff08;Representational state transfer&#xff09; 资源、表示和转换 资源&#xff08;Resource&#xff09; 服务端的一个资源 拥有URL 表示&#xff08;Representation&#xff09; 服务端的资源在客户端的表示 客户端拥有操作服务…

three.js之scene

THREE.Scene对象有时被称为场景图&#xff0c;可以用来保存所有图形场景的必要信息。在Three.js中&#xff0c;这意味着THREE.Scene保存所有对象、光源和渲染所需的其他对象。 本节主要是构建一个基本场景&#xff0c;然后可以通过gui添加&#xff0c;删除场景里的对象等。 效果…

lua变量、数据类型、if判断条件和数据结构table以及【lua 函数】

一、lua变量【 全局变量和局部变量和表中的域】 Lua 变量有三种类型&#xff1a;全局变量和局部变量和表中的域。 ▪ 全局变量&#xff1a;默认情况下&#xff0c;Lua中所有的变量都是全局变量。 ▪ 局部变量&#xff1a;使用local 显式声明在函数内的变量&#xff0c;以及函数…

Golang每日一练(leetDay0040)

目录 118. 杨辉三角 Pascals Triangle &#x1f31f; 119. 杨辉三角 II Pascals Triangle II &#x1f31f; 120. 三角形最小路径和 Triangle &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/…

港联证券策略:A股市场短期预计将保持区间震荡

港联证券以为&#xff0c;跟着4月30日的接近&#xff0c;一切板块的年报以及一季度报将进入密布发表期。从历史数据来看&#xff0c;4月中旬今后指数震动调整的概率较大&#xff0c;在操作上需注意操控全体仓位。从板块方面看&#xff0c;近期为数字经济和中特估双主线驱动的行…

Adobe lr中文版下载官方版 专业摄影师的必备软件 附各个平台

说到修图软件&#xff0c;大家第一时间一定都能联想到Adobe旗下的Photoshop&#xff08;Adobe Photoshop CC&#xff09;和Lightroom&#xff08;Adobe Photoshop Lightroom CC&#xff09;&#xff0c;其中PhotoShop很多设计师都找到了对应的手机版&#xff0c;但有些用户由于…

MacOS 上安装并配置 OpenJDK 图文详细版

1、概述 1.1、什么是 OpenJDK &#xff1f; OpenJDK 是 Java 的一个开源实现&#xff0c;由 Sun Microsystems 于2006年开始发布&#xff0c;是 Java SE 平台&#xff08;Java Standard Edition&#xff09;的参考实现之一。OpenJDK 是由 GPL v2 授权下的自由软件&#xff0c…

Msray-Plus采集工具帮您快速获取数据,让您的市场营销更加精细

随着互联网的不断发展&#xff0c;数据已经成为企业竞争的重要资产之一。市场营销人员需要通过数据来了解客户需求、市场趋势和竞争对手情况&#xff0c;从而制定更加精细的市场营销策略。然而&#xff0c;采集数据并不是一件容易的事情&#xff0c;需要耗费大量的时间和精力。…