Spring学习记录之面向切面编程

AOP(面向切面编程)是一种编程思想,其作用在于在不改变其原始设计的基础上进行功能增强。这也是Spring的开发理念:无侵入式编程。其实,这是一种代理思想,事实上,SpringAOP是动态代理的一种形式。
在进行AOP的学习前,我们先来了解一下代理:代理的作用是增强程序的功能,其遵循的也是无侵入式编程的思想,即我们的原始程序(委托方)只负责我们自己的功能即可,将一些额外的功能交给代理程序(代理方)去完成。
代理可分为两种,一种是静态代理,另一种是动态代理,其中静态代理可采用继承与组合方法来实现。

1.静态代理

1.1通过继承实现静态代理

通过继承被代理对象,重写被代理方法,可以对其进行代理。
优点:被代理类无需实现接口
缺点:只能代理这个类,要想代理其他类,要想代理其他类需要写新的代理方法。

public class Tank{public void move() {System.out.println("Tank moving cla....");}public static void main(String[] args) {new ProxyTank().move();}
}
class ProxyTank extends Tank{@Overridepublic void move() {System.out.println("方法执行前...");super.move();System.out.println("方法执行后...");}
}

1.2使用组合方法实现静态代理

定义一个 Movable 接口被代理类需要和代理类都需要实现该接口。(接口在这里的目的就是起一个规范作用保证被代理类和代理类都实现了move()方法)。代理类需要将该接口作为属性,实例化时需要传入该接口的对象,这样该代理类就可以实现代理所有实现Movable的类了。
优点:可以代理所有实现接口的类。
缺点:被代理的类必须实现接口。

public class Tank implements Movable{@Overridepublic void move() {System.out.println("Tank moving cla....");}public static void main(String[] args) {Tank tank = new Tank();new LogProxy(tank).move();}
}
class LogProxy implements Movable{private Movable movable;public LogProxy(Movable movable) {this.movable = movable;}@Overridepublic void move() {System.out.println("方法执行前....");movable.move();System.out.println("方法执行后....");}
}
interface Movable {void move();
}

动态代理之SpringAOP

我们首先来了解一下关于SpringAOP中的一些基本概念:

在这里插入图片描述

我们通过一个例子来了解上面的基础概念:

在这里插入图片描述

AOP流程

在这里插入图片描述

上面的流程事实上是为了告诉我们一件事,如果切入点与通知匹配成功,那么此时我们使用的原始对象就不是原本的Bean了,而是通过代理创建的代理对象,如果没有成功,那么依旧使用原始对象,这样也就不会发生报错、异常等问题了。

在这里插入图片描述

切入点表达式

关于切入点表达式 的书写,可以采用通配符的形式,快速描述。

在这里插入图片描述

在这里插入图片描述

AOP通知类型

AOP通知类型可分为五种,分别是前置通知(@Before)、后置通知(@After)、环绕通知(@Around)、返回后通知(执行成功通知,@AfterReturning)以及抛出异常后通知(
@AfterThrowing)。其中环绕通知是最常用的,也是最重要的。

在这里插入图片描述

对于ProceedingJoinPoint这个形参,如果不使用这个形参调用pjp.proceed()方法,那么则会跳过原始方法的执行,即隔离原始方法,我们可以通过这个特性进行权限控制,比如我们在执行某个操作时进行身份校验,如果身份符合,那么我们就执行原始方法,否则就不执行。

小案例

那么,我们来实现一个小案例,计算出一个方法的万次执行时间:
首先,我们需要在Spring容器中扫描AOP包,并告诉Spring容器要以注解形式使用AOP。

package configs;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan({"dao","service","aop"})
@PropertySource({"classpath:db.properties"})
@Import({JdbcConfig.class, MybatisConfig.class})
@EnableAspectJAutoProxy//告诉Spring我是以注解形式开发的AOP,去启动Aspect
public class SpringConfig {
}

随后我们定义通知,我们选择环绕通知来实现,其步骤为,首先需要定义一个切入点,这个切入点的表达式为@Pointcut("execution(* service.*.*(..))")//1.定义切入点,代表我们对service层内容都进行切入。
随后我们需要绑定关系,即定义切面:使用注解形式@Around("pt()")
需要注意的是,在使用环绕通知时我们为了对原始操作进行调用,因此需要传入一个ProceedingJoinPoint对象,其调用proceed方法代表执行原始操作。
并且我们需要将该通知转换为Bean,因为Spring是管理Bean的。即在开头加上@Component注解
同时还要告诉Spring在读到我后将我作为AOP处理。
最后完整代码如下:

package aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component//3要让Spring可控制,将其变成Bean
@Aspect//读到我后把我当AOP处理
public class MyAdvice {@Pointcut("execution(* service.*.*(..))")//1.定义切入点private void pt(){}//2.绑定关系即切面
//    @Around("pt()")
//    public void around(ProceedingJoinPoint pjd) throws Throwable {
//        System.out.print("before..\n");
//        //表示对原始操作的调用,需要传入一个ProceedingJoinPoint对象,其调用proceed方法代表执行原始操作
//        pjd.proceed();
//        System.out.print("\nafter..\n");
//    }@Around("pt()")public Object around(ProceedingJoinPoint pjd) throws Throwable {long start=System.currentTimeMillis();//表示对原始操作的调用,需要传入一个ProceedingJoinPoint对象,其调用proceed方法代表执行原始操作for(int i=0;i<10000;i++){Object rs=pjd.proceed();}long end=System.currentTimeMillis();System.out.print("执行万次时间为: "+(end-start)+"ms");return 100;}
}

此外,需要注意的是,上面并没有给出具体是哪个方法的执行效率,这不方便我们进行有针对性的优化,我们可以通过Signature signature=pjd.getSignature()来获取对象信息,完整代码如下:

@Around("pt()")public Object around(ProceedingJoinPoint pjd) throws Throwable {Signature signature=pjd.getSignature();long start=System.currentTimeMillis();//表示对原始操作的调用,需要传入一个ProceedingJoinPoint对象,其调用proceed方法代表执行原始操作for(int i=0;i<10000;i++){Object rs=pjd.proceed();}long end=System.currentTimeMillis();System.out.print(signature.getDeclaringTypeName()+"的"+signature.getName()+"方法执行万次时间为: "+(end-start)+"ms");return rs;}

此外,我们还需要注意的是,通知方法是需要有返回值的,即原始对象的返回值我们需要返回一下(此时的原始对象已经是通过代理生成的),同时我们可以在该模块对返回结果进行封装修改,比如查询结果加上分页功能。

在这里插入图片描述

AOP获取数据

在某些情况,我们需要根据不同的原始对象做出不同的处理,此时我们就需要通过获取原始对象中的数据来做出区分,从而做出对应的处理,在这里,数据可分为参数、返回值以及异常
那么该如何获取呢?很简单,我们只需要两个对象即可:JoinPointProceedingJoinPoint,其中后者是前者的子类,我们多用后者获取环绕通知的数据,而前者获取其他通知的数据。

那么,这个数据获取了有什么用呢?事实上,我们可以对获取的参数进行处理,比如我们所需要的数据编码格式为utf8类型,这里我们就可以对参数转换一下。

@Around("pt()")public Object around(ProceedingJoinPoint pjd) throws Throwable {Object[] args=pjd.getArgs();//表示对原始操作的调用,需要传入一个ProceedingJoinPoint对象,其调用proceed方法代表执行原始操作for(int i=0;i<10000;i++){Object rs=pjd.proceed(args);}return rs;}

同样的,我们也可以使用JoinPoint来获取参数。在环绕通知中,使用ProceedingJoinPoint可以获取参数以及结果,那么对于其他通知类型呢?我们可以在参数列表中给定一个Object对象来接收,同时还要在@AfterReturning的注解中指定接收值是rs这个对象,即:(value = “pt()”,returning = “rs”)

@AfterReturning(value = "pt()",returning = "rs")//这里指定的rs一定要与下面形参中的rs保持一致。public Object aftermethod(Object rs){System.out.print(rs+"\n");return rs;}

此外,需要注意的是,如果形参中有JoinPoint对象,那么该对象一定要在Object这个接收对象之前,即

@AfterReturning(value = "pt()",returning = "rs")public Object aftermethod(JoinPoint jp,Object rs){System.out.print(rs+"\n");return rs;}

那么对于异常该如何接收呢,很简单,和上面的写法类似:

@AfterReturning(value = "pt()",returning = "t")public Object afterthrow(Throwable t){System.out.print(t+"\n");return t;}

总结一下,AOP是一种编程范式,称为面向切面编程。可以用于功能增强。通常我们都对接口进行描述,如果是对实现类则会导致耦合度太高。

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

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

相关文章

一站式App流量统计,Xinstall助您洞悉用户行为

在如今的移动互联网时代&#xff0c;App的推广和运营对于开发者来说至关重要。然而&#xff0c;想要精准掌握App的流量情况&#xff0c;却并不是一件容易的事情。这时&#xff0c;一款强大的App流量统计工具就显得尤为重要。而Xinstall&#xff0c;正是这样一款能够帮助开发者轻…

优惠:阿里云4核16G服务器优惠价格26.52元1个月、149.00元半年

阿里云4核16G服务器优惠价格26.52元1个月、79.56元3个月、149.00元半年&#xff0c;配置为阿里云服务器ECS经济型e实例ecs.e-c1m4.xlarge&#xff0c;4核16G、按固定带宽 10Mbs、100GB ESSD Entry系统盘&#xff0c;活动链接 aliyunfuwuqi.com/go/aliyun 活动链接打开如下图&a…

项目实战-开发工具入门/基本框架搭建/项目初始化/引入组件库

上周更新完了之前vue3的shopping项目&#xff0c;接下来&#xff0c;将会开启一个新的项目&#xff0c;效果是类似于移动端的一个伙伴匹配项目&#xff0c;今天这篇文章从需求分析到架构设计再到项目初始化&#xff0c;基本框架搭建几个部分来为大家详细介绍。 从这个项目开始…

流程引擎表单引擎有哪几个方面的优势?

想要在信息科技迅猛发展的时代中&#xff0c;拥有强劲的市场竞争力&#xff0c;随时捕捉市场发展动态&#xff0c;获得长久发展&#xff0c;就需要正确掌握优质的发展技术平台&#xff0c;为企业可持续发展注入新鲜动力。低代码技术平台、流程引擎表单引擎的兴盛发展是推动各中…

UI功能6大流程、接口测试8大流程这些你真的全会了吗?

在讲接口流程测试之前&#xff0c;首先需要给大家申明下&#xff1a;接口测试对于测试人员而言&#xff0c;非常非常重要&#xff0c;懂功能测试接口测试&#xff0c;就能在企业中拿到一份非常不错的薪资。 这么重要的接口测试&#xff0c;一般也是面试笔试必问。为方便大家更…

一些 AI 工具

AI 搜索&#xff1a;Phind&#xff0c;perplexity AI聊天大模型&#xff1a;chatgpt&#xff0c; kimi&#xff08;国内可用&#xff0c;支持上传文件&#xff09; AI 机器人&#xff1a;https://www.coze.com/ AI工具集&#xff1b;https://ai-bot.cn/#term-2 agent GPT&a…

大数据--hdfs--java编程

环境&#xff1a; virtualbox ubantu1604 Linux idea社区版2023 jdk1.8 hadoop相关依赖 使用java操作 1. 判断/user/stu/input/test.txt文件是否存在&#xff0c;存在则读出文件内容&#xff0c;打印在控制台上。反之&#xff0c;输出“文件不存在”。 package abc;impo…

二进制文件和为文本文件

二进制文件和为文本文件 根据数据的组织形式&#xff0c;数据文件被称为文本文件或者二进制文件。 数据在内存中以⼆进制的形式存储&#xff0c;如果不加转换的输出到外存的文件中&#xff0c;就是二进制文件。 如果要求在外存上以ASCII码的形式存储&#xff0c;则需要在存储前…

零成本使用Grass赚钱,简化教程,一学就会

文章目录 第一步&#xff1a;注册账户第二步&#xff1a;安装Grass Chrome插件1、离线安装&#xff08;推荐&#xff09;2、在线安装&#xff08;有外力的话推荐&#xff09; 第三步&#xff1a;登录Grass插件第四步&#xff1a;Grass开始运行结语 第一步&#xff1a;注册账户 …

计算机二级大题

题目来源&#xff1a;计算机二级Python半个月抱佛脚大法&#xff08;内呈上真题版&#xff09; - 知乎 1.大题1 注意csv文件读取的处理 ls[] for line in f: ls.append(line.strip(\n).split(,)) 2. 大题2 第一问&#xff1a; #计算有效票张数 fopen("vote.txt",…

数据挖掘与机器学习 1. 绪论

于高山之巅&#xff0c;方见大河奔涌&#xff1b;于群峰之上&#xff0c;便觉长风浩荡 —— 24.3.22 一、数据挖掘和机器学习的定义 1.数据挖掘的狭义定义 背景&#xff1a;大数据时代——知识贫乏 数据挖掘的狭义定义&#xff1a; 数据挖掘就是从大量的、不完全的、有噪声的、…

【群晖】Docker Compose部署 Emby Server

【群晖】Docker Compose部署 Emby Server 本来群晖上面的 Emby 是用套件安装的&#xff0c;但是不巧的是前两天脑袋一抽装了两个插件&#xff0c;导致 Emby Server被当肉鸡了&#xff0c;还找不到脚本代码在哪儿&#xff0c;一天时间上传了3T的流量。无奈之下&#xff0c;只能尝…

【python绘图colorbar对齐】

[Toc]# 1、问题描述 python在绘图过程中&#xff0c;可能会出现colorbar高度与主图不匹配情况&#xff0c;需要进行调整&#xff0c;使得与主图高度对齐&#xff0c;使图像更美观。示例&#xff1a;colorbar位置高于主图 2、解决方法 通过调整shrink参数匹配对齐,pad调整x轴…

《妈妈是什么》笔记(二) 让孩子自己做选择

经典摘录 孩子也会需要独立的空间做事情&#xff0c;求独立、求空间、求私隐 对于不管因为什么&#xff0c;别人在受到肯定和赞赏的时候&#xff0c;会对我们自己的心理带来因“比较”而产生的不适感甚至嫉妒感&#xff0c;进而在行为上影响了我们自己的节奏&#xff0c;产生一…

ThingsBoard初始化数据库Postgres+Cassandra

本章将介绍ThingsBoard初始化数据PostgresCassandra&#xff0c;两种数据库结合使用&#xff0c;以及源码的编译安装。本机环境&#xff1a;Centos7、Docker、Postgres、Cassandra 环境安装 开发环境要求&#xff1a; docker &#xff1b;Docker&#xff1b;Postgres:Cassandr…

Django日志(一)

一、概念与配置 1.1、概述 日志是程序员经常在代码中使用快速和方便的调试工具。它在调试方面比print更加的优雅和灵活 而且日志记录对于调试很有用,可以提供更多,更好的结构化,有关应用程序的状态和运行状况的信息 Django框架的日志通过python内置的logging模块实现的,可…

基于Gabor滤波器的指纹图像识别,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

2024年【山东省安全员C证】考试试卷及山东省安全员C证复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 山东省安全员C证考试试卷是安全生产模拟考试一点通生成的&#xff0c;山东省安全员C证证模拟考试题库是根据山东省安全员C证最新版教材汇编出山东省安全员C证仿真模拟考试。2024年【山东省安全员C证】考试试卷及山东省…

【Linux】信号的处理{信号处理的时机/了解寄存器/内核态与用户态/信号操作函数}

文章目录 0.对于信号捕捉的理解1.信号处理的时机1.1 何时处理信号&#xff1f;1.2 内核态和用户态1.3 内核态和用户态的切换 2.了解寄存器3.信号捕捉的原理4.信号操作函数4.1sighandler_t signal(int signum, sighandler_t handler);4.2int sigaction(int signum, const struct…

express+mysql+vue,从零搭建一个商城管理系统15--快递查询(对接快递100)

提示&#xff1a;学习express&#xff0c;搭建管理系统 文章目录 前言一、安装md5&#xff0c;axios二、新建config/logistics.js三、修改routes/order.js四、查询物流信息五、试错与误区总结 前言 需求&#xff1a;主要学习express&#xff0c;所以先写service部分 快递100API…