【Spring】一文带你吃透AOP面向切面编程技术(下篇)

news2024/11/24 16:40:44

在这里插入图片描述

个人主页: 几分醉意的CSDN博客_传送门

上节我们介绍了什么是AOP、Aspectj框架的前置通知@Before传送门,这篇文章将继续详解Aspectj框架的其它注解。

文章目录

  • 💖Aspectj框架介绍
    • ✨JoinPoint通知方法的参数
    • ✨后置通知@AfterReturning
    • ✨环绕通知@Around
    • ✨异常通知@AfterTrowing
    • ✨@Pointcut定义和管理切入点注解
    • ✨总结
  • 💖投票传送门(欢迎伙伴们投票)

💖Aspectj框架介绍

AOP技术思想的实现:使用框架实现AOP。实现AOP的框架有很多。有名的两个

1. Spring:Spring框架实现AOP思想中的部分功能。Spring框架实现AOP的操作比较繁琐,笨重。
2. Aspectj:独立的框架,专门做AOp的,功能最强大的。属于Eclipse。

而我下面主要介绍的就是Aspectj框架来实现Aop,Aspectj框架可以使用注解和xml配置文件两种方式实现AOP。

✨JoinPoint通知方法的参数

切面类中的通知方法,可以有参数,但是必须是JoinPoint。
JoinPoint: 表示正在执行的业务方法。 相当于反射中 Method

使用要求:必须是参数列表的第一个
作用:获取方法执行时的信息,例如方法名称, 方法的参数集合

下面我们直接实战,注意下面用的是上一节的前置通知的业务接口和实现类。

切面类

@Aspect
public class MyAspect {
	@Before(value = "execution(* *..SomeServiceImpl.do*(..) )")
	    public void myBefore2(JoinPoint jp){
	        //获取方法的定义
	        System.out.println("前置通知中,获取目标方法的定义:"+ jp.getSignature());
	        System.out.println("前置通知中,获取方法的名称"+jp.getSignature().getName());
	        //获取方法执行时的参数
	        Object[] args = jp.getArgs(); //返回的是一个数组 里面存放的是所有参数
	        for (Object arg : args) {
	            System.out.println("前置通知,获取方法的参数是"+arg);
	        }
	
	        //切面的代码。
	        System.out.println("===前置通知,切面的功能,在目标方法之前先执行==:"+new Date());
	        System.out.println("");
	    }
	}

测试

@Test
    public void test(){
        //如果没有加入代理的处理:
        // 1)目标方法执行时,没有切面功能的。
        // 2) service对象没有被改变

        //加入代理的处理:
        // 1)目标方法执行时,有切面功能的。
        // 2) service对象是改变后的 代理对象 com.sun.proxy.$Proxy8
        String s = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(s);
        SomeService service = (SomeService)ctx.getBean("someService");
        service.doSome("ll" , 22);

    }
	//JoinPoint:哪个目标对象方法执行时,它就代表哪个方法
	//例如这里doSome执行时,它就代表doSome,然后可以获取这个方法的信息

执行结果:
在这里插入图片描述
拓展:
在这里插入图片描述

✨后置通知@AfterReturning


     * @AfterReturning:后置通知
     *    属性:value 切入点表达式
     *          returning 自定义的变量,表示目标方法的返回值的。
     *                    自定义变量名称必须和通知方法的形参名一样
     *    位置:在方法的上面
     *
     * 特点:
     *  1.在目标方法之后,执行的。
     *  2.能获取到目标方法的执行结果。
     *  3.不会影响目标方法的执行
     *
     * 方法的参数:
     *   Object res: 表示目标方法的返回值,使用res接收doOther的调用结果。
     *   Object res= doOther();
     *
     *  后置通知的执行顺序
     *  Object res = SomeServiceImpl.doOther(..);  先执行业务方法
     *  myAfterReturning(res); 在执行后置通知
     *
     *  思考:
     *    1 doOther方法返回是String ,Integer ,Long等基本类型,
     *      在后置通知中,修改返回值, 是不会影响目标方法的最后调用结果的。
     *    2 doOther返回的结果是对象类型,例如Student。
     *      在后置通知方法中,修改这个Student对象的属性值,会不会影响最后调用结果?
     *

下面通过举例的方式,带大家理解后置通知。

首先业务接口添加doOther方法,然后实现它的实现类

public interface SomeService {
    void doSome(String name,Integer age);
    String  doOther(String name,Integer age);
}
public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name, Integer age) {
        System.out.println("业务方法doSome(),创建商品订单");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("执行业务方法doOther,处理库存");
        return "abcd";
    }
}

创建切面类

@Aspect
public class MyAspect {
    //定义方法,表示切面的具体功能
    /*
       后置通知方法的定义
       1)方法是public
       2)方法是void
       3)方法名称自定义
       4)方法有参数,推荐使用Object类型
     */
    
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",
            returning = "res")
    public void myAfterReturning(JoinPoint jp , Object res){
        System.out.println("后置通知,在目标方法之后,执行的。能拿到执行结果:"+res);

        //修改目标方法的返回值
        if(res != null){
            res = "HELLO Aspectj";
        }
        System.out.println("后置通知,修改res后"+res);
    }
}

测试:

@Test
    public void test02(){
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ctx.getBean("someService");
        String ret = service.doOther("zhangsan", 20);

        System.out.println("test02中调用目标方法的结果:"+ret);
    }

执行结果:
在这里插入图片描述

✨环绕通知@Around

特点介绍:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

     * @Around:环绕通知
     *    属性:value 切入点表达式
     *    位置:在方法定义的上面
     *
     * 返回值:Object ,表示调用目标方法希望得到执行结果(不一定是目标方法自己的返回值)
     * 参数:  ProceedingJoinPoint, 相当于反射中 Method。
     *        作用:执行目标方法的,等于Method.invoke()
     *
     *        public interface ProceedingJoinPoint extends JoinPoint {}
     *
     * 特点:
     *  1.在目标方法的前和后都能增强功能
     *  2.控制目标方法是否执行
     *  3.修改目标方法的执行结果。
     *
@Aspect
public class MyAspect {
    //定义方法,表示切面的具体功能
    /*
       环绕置通知方法的定义
       1)方法是public
       2)方法是必须有返回值, 推荐使用Object类型
       3)方法名称自定义
       4)方法必须有ProceedingJoinPoint参数,
     */

    @Around("execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        Object methodReturn = null;
        System.out.println("执行了环绕通知,在目标方法之前,输出日志时间=="+ new Date());

        //执行目标方法  ProceedingJoinPoint,表示doFirst
        methodReturn = pjp.proceed();//method.invoke(),表示执行doFirst()方法本身

        if( methodReturn != null){
            methodReturn ="环绕通知中,修改目标方法原来的执行结果";
        }

        System.out.println("环绕通知,在目标方法之后,增加了事务提交功能");

        //return "HelloAround,不是目标方法的执行结果";
        //返回目标方法执行结果。没有修改的。
        return methodReturn;
    }


}

测试:

@Test
    public void test02(){
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ctx.getBean("someService");
        String ret = service.doFirst("zhangsan");
        System.out.println("ret调用目标方法的结果:"+ret);
    }

执行结果:
在这里插入图片描述

✨异常通知@AfterTrowing

业务接口和实现类:

public interface SomeService {
    void doSecond(String name);
}
@Service
public class SomeServiceImpl implements SomeService {
    
    @Override
    public void doSecond(String name) {
        System.out.println("执行业务方法doSecond,处理库存"+(10/0));
    }
}

切面类:

@Aspect
public class MyAspect {
    //定义方法,表示切面的具体功能
    /*
       异常通知方法的定义
       1)方法是public
       2)方法是没有返回值。是void
       3)方法名称自定义
       4)方法有参数是Exception
     */

    /**
     * @AfterThrowing:异常通知
     *     属性: value 切入点表达式
     *           throwing 自定义变量,表示目标方法抛出的异常。
     *                    变量名必须和通知方法的形参名一样
     *     位置:在方法的上面
     * 特点:
     *  1. 在目标方法抛出异常后执行的, 没有异常不执行
     *  2. 能获取到目标方法的异常信息。
     *  3. 不是异常处理程序。可以得到发生异常的通知, 可以发送邮件,短信通知开发人员。
     *      看做是目标方法的监控程序。
     *
     *  异常通知的执行
     *  try{
     *      SomeServiceImpl.doSecond(..)
     *  }catch(Exceptoin e){
     *      myAfterThrowing(e);
     *  }
     */
    @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing = "ex")
    public void myAfterThrowing(Exception ex){
        System.out.println("异常通知,在目标方法抛出异常时执行的,异常原因是:"+ex.getMessage());
        /*
           异常发生可以做:
           1.记录异常的时间,位置,等信息。
           2.发送邮件,短信,通知开发人员
         */
    }

}

测试:

@Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ctx.getBean("someService");
        service.doSecond("lisi");
    }

执行结果:在这里插入图片描述
✨ 最终通知@After

业务接口和实现类:

public interface SomeService {
    void doThird();
}
@Service
public class SomeServiceImpl implements SomeService {
    @Override
    public void doThird() {
        System.out.println("执行了业务方法doThird()");
    }
}

切面类:

@Aspect
public class MyAspect {
    //定义方法,表示切面的具体功能
    /*
       最终通知方法的定义
       1)方法是public
       2)方法是没有返回值。是void
       3)方法名称自定义
       4)方法没有参数
     */


    /**
     * @After:最终通知
     *    属性: value 切入点表达式
     *    位置: 在方法的上面
     * 特点:
     *  1. 在目标方法之后执行的。
     *  2. 总是会被执行。
     *  3. 可以用来做程序最后的收尾工作。例如清除临时数据,变量。 清理内存
     *
     *  最终通知
     *  try{
     *      SomeServiceImpl.doThird(..)
     *  }finally{
     *      myAfter()
     *  }
     */
    @After(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public void myAfter(){
        System.out.println("最终通知,总是会被执行的");
    }


}

测试:

    @Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ctx.getBean("someService");
        
        service.doThird();
    }

执行结果:
在这里插入图片描述

✨@Pointcut定义和管理切入点注解

@Aspect
public class MyAspect {

    @Before(value = "mypt()")
    public void myBefore(){
        System.out.println("前置通知,在目标方法之前先执行的");
    }

    @After(value = "mypt()")
    public void myAfter(){
        System.out.println("最终通知,总是会被执行的");
    }

    /**
     * @Pointcut: 定义和管理切入点,不是通知注解。
     *     属性: value 切入点表达式
     *     位置: 在一个自定义方法的上面, 这个方法看做是切入点表达式的别名。
     *           其他的通知注解中,可以使用方法名称,就表示使用这个切入点表达式了
     */
    @Pointcut("execution(* *..SomeServiceImpl.doThird(..))")
    private void mypt(){
        //无需代码
    }
}

✨总结

AOP是一种动态的技术思想,目的是实现业务功能和非业务功能的解耦合。业务功能是独立的模块,其他功能也是独立的模块。例如事务功能,日志等等。让这些事务,日志功能是可以被复用的。
当目标方法需要一些功能时,可以在不修改,不能修改源代码的情况下,使用aop技术在程序执行期间,生成代理对象,通过代理执行业务方法,同时增加功能。

💖投票传送门(欢迎伙伴们投票)

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

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

相关文章

mysql数据库完全备份和增量备份与恢复

mysql数据备份: 数据备份方式 物理备份: 冷备:.冷备份指在数据库关闭后,进行备份,适用于所有模式的数据库热备:一般用于保证服务正常不间断运行,用两台机器作为服务机器,一台用于实际数据库操作应用,另外…

基于stm32mp157的嵌入式linux+qt项目实战物联网毕业设计选题之智慧医疗项目

stm32mp157开发板FS-MP1A是华清远见自主研发的一款高品质、高性价比的Linux单片机二合一的嵌入式教学级开发板。开发板搭载ST的STM32MP157高性能微处理器,集成2个Cortex-A7核和1个Cortex-M4 核,A7核上可以跑Linux操作系统,M4核上可以跑FreeRT…

吃瓜教程 | Datawhale 打卡(Task 01)

第1章 绪论 引言 机器学习致力于研究如何通过计算的手段,利用经验来改善系统自身的性能。 “经验”通常以“数据”的形式存在。 机器学习研究的主要内容: 在计算机上从数据中产生“模型”(model)的算法,即“学习算法…

软件开发风险 需要规避的4个重点

1、前期减少投资 开发软件,最好减少前期的投入。软件开发成本高低是取决于所需开发的功能,需求越多,需要实现的功能越多,开发成本就越高。在不确定开发软件能够带来预期价值的情况下,建议先开发核心功能,辅…

分享166个HTML医疗保健模板,总有一款适合您

分享166个HTML医疗保健模板,总有一款适合您 166个HTML医疗保健模板下载链接:https://pan.baidu.com/s/1tBFEInec5Jnw_ShQd21MJg?pwdakif 提取码:akif Python采集代码下载链接:采集代码.zip - 蓝奏云 import os import shuti…

【残差稠密网络:医学图像:超分】

Residual dense network for medical magnetic resonance images super-resolution (医学磁共振图像超分辨率的残差稠密网络) 高分辨率磁共振成像(MRI)有助于专家定位病灶和诊断疾病,但高分辨率MRI难以获得。此外&am…

PyQt5数据库开发1 4.2 配置SQL Server 2008 数据源(ODBC编程)

文章目录 配置SQL Server 2008 数据源(ODBC编程) 1. 了解要配置的数据源服务器名称,以及数据库和对应表 2. 打开控制面板,点击管理工具 3. 双击数据源 4. 选择“用户DSN”选项卡,点击“添加” 5. 选择SQL Serv…

晚上下班之后可以做什么副业,业余时间需要利用起来

对大多数普通人来说,他们晚上有很多空闲时间,但他们总是在手机上玩游戏,刷视频,白白度过一夜。事实上,近年来,很多朋友都想利用晚上的时间做一些副业,因为目前的工资已经不能满足自己的需求&…

第05章_MySQL排序与分页

第05章_排序与分页 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 1. 排序数据 如果没有使用排序操作,默认情况下查询返回的数据时按照添加数据的顺序显示的 SELECT employee_id, la…

yocto创建自己的machine

前面讲了如何离线构建yocto工程,这节讲如何创建自己的machine,在初始化yocto启动bitbake需要输入如下命令 DISTROfsl-imx-fb MACHINEimx6ull14x14evk source imx-setup-release.sh -b build其中DISTRO用来指定发行版本 MACHINE用来指定硬件平台 build为构…

HTML复习1

VSCode 工具生成骨架标签新增代码 < !DOCTYPE html>表示的什么意思&#xff1f; 这句代码的意思是&#xff1a;当前页面采取的是HTML5版本来显示网页. 注意&#xff1a; < !DOCTYPE> 声明位于文档中的最前面的位置&#xff0c;处于 < html> 标签之前。< …

SAP S/4HANA Cloud 2302 财务模块亮点

&#xff08;亮点1:&#xff09;含项目制造的按订单设计 (ETO) 使用 SAP S/4HANA Cloud 2302&#xff0c;新的范围项目 6GD首先发布在德国和美国的国家版本下&#xff0c;提供项目制造的按订单设计 (ETO)的功能。 价值体现 借助 ETO 解决方案&#xff0c;您可以&#xff1a;…

【模型部署】TensorRT的安装与使用

文章目录1.TensorRT的安装1.1 cuda/cudnn以及虚拟环境的创建1.2 根据cuda版本安装相对应版本的tensorRT2. TensorRT的使用2.1 直接构建2.2 使用 Python API 构建2.3 使用 C API 构建2.3.1 属性配置2.3.2 验证2.4 IR 转换模型2.4.1 使用 Python API 转换2.4.2 使用 C API 转换2.…

Moonbeam生态说|走近生态项目SubWallet

「Moonbeam生态说」是Moonbeam中文爱好者社区联合Moonbeam中文高级大使组织的社区AMA活动。该活动为已部署Moonriver或Moonbeam的项目方提供了在主流Moonbeam非官方中文社区内介绍自己的项目信息&#xff0c;包括&#xff1a;项目介绍、团队介绍、技术优势等&#xff0c;帮助社…

【Unity VR开发】结合VRTK4.0:创建抽屉

语录&#xff1a; 为有牺牲多壮志&#xff0c;敢叫日月换新天。 前言&#xff1a; 前面我们知道了门的基本实现原理是通过角度驱动器实现的&#xff0c;那么今天我们来实现一下抽屉的实现原理&#xff1a;线性驱动器。 正文&#xff1a; 步骤一&#xff1a; 首先我们需要在新…

零基础学Java要具备哪些前提条件?

很多零基础的学员对于学Java比较迷茫&#xff0c;想通过学Java掌握一技之长&#xff0c;却不知道入门需要具备哪些条件?不知道怎么去学习?下面详细来和大家聊聊&#xff1a;首先&#xff0c;要对Java语言感兴趣&#xff0c;兴趣是最好的老师&#xff0c;只有拥有兴趣才能在学…

浅析云边端协同架构的应用意义与EasyCVR视频融合能力升级

随着5G时代的到来&#xff0c;万物互联产生了海量数据&#xff0c;据IDC预测&#xff0c;到2025年全球设备连接总数将达到1000亿&#xff0c;集中式处理模型下核心网络无法承载如此大的数据量传输&#xff0c;数据也无法在云中心存储计算&#xff0c;因此基于云边端的架构模式成…

ATR指标在外汇交易中的另类运用方法

当涉及到外汇交易时&#xff0c;有许多不同的指标可以使用。然而&#xff0c;ATR指标可能是一个被低估的工具&#xff0c;可以帮助您发现有利可图的交易机会。本文将介绍ATR指标是什么&#xff0c;如何使用它来识别价格波动和制定交易策略&#xff0c;以及如何在外汇市场中另辟…

DRF之实战总结

前言 DRF概念&#xff1a;Django REST framework框架是一个用于构建Web API的强大而又灵活的工具. 通常简称为DRF框架 或 REST framework框架 特点&#xff1a; 提供了定义序列化器serializer的方法,可以快速根据Django ORM或者其他库自动序列化/反序列化;提供了丰富的类视图…

解决rimraf使用时提示unexpected token “.”

解决rimraf使用时提示unexpected token “.” 前言 最近运行一个Cordova项目时&#xff0c;npm install后打包&#xff0c;命令栏提示了下面这个问题&#xff1a; 很奇怪啊&#xff0c;就我这里有问题&#xff0c;别人之前都没事&#xff0c;很头疼。 问题原因 经过一番摸索…