Spring AOP 面向切面编程(下)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Spring AOP 面向切面编程(下)

三. Advice 通知演示


刚刚已经演示过了 @Before 前置通知, 就不在赘述了


1. @After 注解


利用 @After 后置通知实现一个统一的日志处理功能


  • 建立切面
@Aspect
    @Component
    public class LogAOP {
    }


  • 创建切点
@Aspect
    @Component
    public class LogAOP {
        // 创建切点
        @Pointcut("execution(* com.example.demo.controller.LogController.* (..))")
        public void doPointcut() {
            // 切点只是为了配置规则, 并非具体实现. 因此为空方法
        }
    }


  • 创建后置通知
@Aspect
    @Component
    public class LogAOP {
        // 创建切点
        @Pointcut("execution(* com.example.demo.controller.LogController.* (..))")
        public void doPointcut() {
            // 切点只是为了配置规则, 并非具体实现. 因此为空方法
        }
        // 创建后置通知
        @After("doPointcut()")
        public void doAfter() {
            System.out.println("记录日志结束 ");
        }
    }


  • 建立连接点
@RestController
    public class LogController {
        // 创建日志对象
        public static final Logger log = LoggerFactory.getLogger(LogController.class);
        @RequestMapping("/user/log")
        public String longLog() {
            System.out.println("执行登陆功能");
            log.info("记录操作日志 : 用户成功登陆");
            return "Spring AOP";
        }
    }


访问路由方法模拟执行用户登陆


b0857873b6dab954b773820da7a25914.png389ed4705751bfaa79042b4358bedd1f.png


可以看到, 当执行登陆后, 记录下操作日志, 此时该连接点方法执行结束, 执行后置通知 " 日志记录结束 "


2. @AfterReturning 注解


还是刚刚的登陆操作, 当执行的是 @AfterReturning 返回通知时, 预期在登陆操作结束后执行


  • 建立切面
@Aspect 
    @Component 
    public class UserAOP {
    }


  • 创建切点
@Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
    public void pointcut() {
        // 切点只是为了配置规则, 并非具体实现. 因此为空方法
    }


  • 建立返回通知
@AfterReturning("doPointcut()")
    public void doAfterReturning() {
        System.out.println("执行返回通知 ");
    }


  • 创建连接点
@RequestMapping("user/login")
    public String login() {
        System.out.println("执行登陆操作 ");
        return "Spring AOP";
    }


执行登陆方法

7ac69f590bc5cc68e3be13988c78b467.png10b42605ef841e74b41e73bfe4a7a6be.png


3. @AfterThrowing 注解


异常通知, 当执行匹配的连接点的方法遇到异常结束后返回通知

还是刚刚的切面里, 同样的切点, 建立异常通知, 执行登陆方法

@AfterThrowing("doPointcut()")
    public void doAfterThrowing() {
        System.out.println("执行异常通知 ");
    }


可以看到抛异常了, 并且控制台也在异常之后打印了异常通知

6295a2391d1fbbb43debbafeea999c8d.png82e888be436a20b98b8b1d5666dacb08.png


4. @Around 注解


环绕通知, 被通知的方法本身被通知包裹着, 也就是执行环绕通知在被通知的方法执行之前会发一次通知, 在被执行方法执行结束后也会执行一次通知.


还是在刚刚的切面和切点, 建立环绕通知

// 添加事件本身
// ProceedingJoinPoint 表示正在执行的方法或表达式的连接点
// 配置环绕通知
@Around("doPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("开始执行环绕通知" );
    long startTime = System.currentTimeMillis();
    // 因为是将方法包围起来执行, 因此只能在方法里调用本身
    Object object = joinPoint.proceed(); // 调用连接点的方法
    System.out.println("结束执行环绕通知" );
    long endTime = System.currentTimeMillis();
    System.out.println("时间差为 : " + (endTime - startTime));
    return object; // 计算时间差后并获取秒数毫秒级
}


可以看到, 环绕通知里面有 joinPoint, 前面说到它是连接点的意思. 这里不难明白, 切面需要通过连接点来确定何时调用环绕通知和如何调用它们. 因此这里传入的必须是连接点. 而前面的前置、后置通知等切面只需要在正确匹配的连接点之前或者之后通知就可以了.


建立连接点

@RequestMapping("/user/count")
    public String fun1() {
        System.out.println("执行了 count 方法");
        int count = 0;
        for(int i = 0; i < 1000000000; i++) {
            count++;
        }
        return "统计方法";
    }


通过环绕通知, 我们就可以统计某个方法的具体执行时间了, 从而作为该方法是否需要优化的重要依据之一.

28859cd6906c5bc41639cc76fadf582b.png


0692762a9820fdae3dd6c0f0f4d1c2a6.png


5. 环绕和前置后置通知同时执行


你可能发现了, 环绕通知其实就是一个前置通知搭配一个后置通知. 那么, 既然前置后置都有了, 为什么还需要环绕通知呢 ? 他们放在一起执行会报错呢 ? 还是有什么联系呢 ?

public class UserAOP {
    @Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
    public void doPointcut() {
        // 切点只是为了配置规则, 并非具体实现. 因此为空方法
    }
    // 配置前置通知
    @Before("pointcut()") // 里面填写针对那个切点的通知, 可以有多个切点
    public void doBefore() {
        System.out.println("执行 before 前置通知 : 登陆检验" );
    }
    @After("doPointcut()")
    public void doAfter() {
        System.out.println("执行 After 后置通知 : 登陆检验结束");
    }
    // 添加事件本身
    // ProceedingJoinPoint 表示正在执行的方法或表达式的连接点
    // 配置环绕通知
    @Around("doPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("开始执行环绕通知" );
        long startTime = System.currentTimeMillis();
        // 因为是将方法包围起来执行, 因此只能在方法里调用本身
        Object object = joinPoint.proceed(); // 调用连接点的方法
        System.out.println("结束执行环绕通知" );
        long endTime = System.currentTimeMillis();
        System.out.println("时间差为 : " + (endTime - startTime));
        return object; // 计算时间差后并获取秒数毫秒级
    }
}


a69e85a83a38c186c01a7dfb0a394450.png


bcf434ed5fc5148dfa4cb5320c345b9b.png

可以看到, 前置和后置通知总是在之间环绕通知的. 这是为什么 ?


由于环绕通知需要在目标方法执行之前和之后分别执行, 以便正确的控制目标方法的执行


具体来说, 如果放在前置和后置执行之间, 那么当目标方法执行时, 环绕通知也会被调用. 通俗一点理解就是执行方法时的前置通知时间是极短的, 如果在这执行前置通知还需要执行环绕通知有可能会导致重复执行, 从而导致代码重复和性能损失.


6. 总结


Spring AOP 除了上面的这几样简单的统一功能处理外, 还有很多功能可以实现, 只要它符合统一集中处理的思想, 也就是符合 AOP 的思想. 就可以用 Spring AOP 来实现.


四. Spring AOP 原理分析



Spring AOP 倒地是怎么执行的呢 ? 它为什么就知道那些连接点是我们匹配的, 那些是我们不需要的呢 ?


Spring AOP 是构建在动态代理基础上的. 因此 Spring 对 AOP 的支持局限于方法级别的拦截.


我们上面所写的 Spring AOP 它都是原生的. 而在 JDK 中早已替我们封装了


  1. JDK 动态代理


通过反射的机制, 在运行的时候生成一个代理对象, 并将所有的方法调用转发给代理对象. 代理对象实现了 InvocationHandler 接口,并重写了 invoke() 方法, 该方法会在代理对象被调用的时候执行.


  1. CGLIB 动态代理


和 JDK 动态代理类似, 也是通过代理对象来实现方法调用的转发. 但 CGLIB 动态代理比 JDK 动态代理更加的灵活, 因为它可以处理继承关系中私有的方法中的私有方法.


无论是那种方式, 在 Spring AOP 都可以使用动态代理的方式来实现切面逻辑



相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
21天前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
28天前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
74 1
什么是AOP面向切面编程?怎么简单理解?
|
2月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
41 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
27天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
30 1
|
1月前
|
XML Java 开发者
论面向方面的编程技术及其应用(AOP)
【11月更文挑战第2天】随着软件系统的规模和复杂度不断增加,传统的面向过程编程和面向对象编程(OOP)在应对横切关注点(如日志记录、事务管理、安全性检查等)时显得力不从心。面向方面的编程(Aspect-Oriented Programming,简称AOP)作为一种新的编程范式,通过将横切关注点与业务逻辑分离,提高了代码的可维护性、可重用性和可读性。本文首先概述了AOP的基本概念和技术原理,然后结合一个实际项目,详细阐述了在项目实践中使用AOP技术开发的具体步骤,最后分析了使用AOP的原因、开发过程中存在的问题及所使用的技术带来的实际应用效果。
59 5
|
23天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
31 0
|
2月前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
88 2
|
2月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
137 9
|
2月前
|
Java 数据库连接 Spring
【2021Spring编程实战笔记】Spring开发分享~(下)
【2021Spring编程实战笔记】Spring开发分享~(下)
30 1
|
2月前
|
Java 容器
AOP面向切面编程
AOP面向切面编程
43 0