Spring系列篇--关于AOP【面向切面】的详解

目录

一.AOP是什么

二.案例演示 

1.前置通知1.1 先准备接口

1.2然后再准备好实现类

1.3对我们的目标对象进行JavaBean配置 

1.4 编写前置系统日志通知

1.5配置系统通知XML中的JavaBean

1.6 配置代理XML中的JavaBean

1.7 测试代码开始测试

注意这里有一个报错问题!!!

2. 后置通知2.1 先准备好后置通知的系统日志

2.2 配置后置系统通知的XML的JavaBean

 2.3 测试结果

3.环绕通知

3.2 环绕通知的系统日志

3.3 配置环绕通知的XML的JavaBean与前置通知和后置通知一致

3.4 测试结果

4.异常通知4.1 异常通知的系统日志和其他系统日志不同的是,方法名为固定的afterThrowing,不能修改

5.过滤通知5.1 直接在XML中配置JavaBean

四.总结aop是面向切面编程,普通程序由上而下正常执行,aop的程序执行是先执行到目标对象的目标方法中,如果连接点上由前置通知,则先执行前置通知再执行目标方法,最后如果目标方法有后置通知则最后执行后置通知代码,不管是前置通知,后置通知,环绕通知,异常通知,过滤通知,代码都是非业务核心代码,如日志、事务的管理(开启、提交、回滚)         


一.AOP是什么

简介:
面向切面编程(Aspect-Oriented Programming)是一种编程范式,它的主要目的是通过预编译和运行期动态代理实现程序功能的横切(cross-cutting)特性,如日志记录、性能统计、事务监控等。它可以帮助开发者将这些原本分散在各个方法或类中的业务逻辑抽象出来,提高代码复用性,降低耦合度
AOP(Aspect-Oriented Programming)是Spring框架的一个重要特性,它通过将横切关注点(cross-cutting concerns)从核心业务逻辑中分离出来,以模块化的方式在整个应用程序中重复使用。以下是关于AOP的简介及其特点:

AOP是一种编程范式,它通过将横切关注点切割出来,将其模块化,并将其应用于多个类和模块,以提高代码的重用性和可维护性。
横切关注点是指与核心业务逻辑无关但存在于多个类或模块中的非功能性需求,例如日志记录、性能监控、事务管理等。
特点:

模块化:AOP允许将横切关注点从核心业务逻辑中提取出来,形成独立的切面(Aspect),使得关注点的逻辑可以独立于各个模块。
解耦:AOP通过解耦横切关注点与核心业务逻辑,使得它们可以独立演化和变化,提高了模块之间的松耦合程度。
重用性:AOP允许将切面应用于多个类和模块,从而实现了关注点的重用,避免了代码的重复编写。
可维护性:将横切关注点抽象为切面后,使得代码结构更清晰,易于理解和维护。
动态性:AOP可以在运行时动态地将切面应用到目标对象上,而不需要修改目标对象的源代码,增强了系统的灵活性和可扩展性。
多样性:Spring框架支持不同类型的切面编程,包括基于代理的AOP和基于字节码增强的AOP。这样可以选择最适合应用程序需求的AOP实现方式。
在Spring框架中,AOP的实现采用了代理模式和动态代理技术。Spring提供了多种AOP的实现方式,包括基于XML配置的AOP、基于注解的AOP和基于纯Java配置的AOP(JavaConfig)等,开发者可以根据具体需求选择适合的方式来配置和使用AOP。
 

面向切面:
1.专业术语
①目标对象:
专业解释:被通知(被代理)的对象

通俗理解:在书店中,商品就是目标。每个商品都有自己的属性(比如价格、名称、库存等)和行为(比如计算促销价格、更新库存等)。收银员通过扫描商品的条形码来与商品进行交互,调用商品的方法来获取商品信息以及执行一些操作。商品本身即代表了目标

②连接点:
专业解释:程序执行过程中明确的点,如方法的调用,或者异常的抛出

通俗理解:在书店中,我们可以将顾客结账的行为看作一个连接点

③通知:
专业解释:在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)

通俗理解:

前置通知(Before Advice):在切入点前执行的代码,在读者购买图书之前,我们可以记录读者购买的图书信息
后置通知(After Advice):在切入点后执行的代码,在读者购买图书之后,我们可以更新图书库存
环绕通知(Around Advice):在切入点前后都执行的代码,我们可以对读者进行额外的安全检查和记录日志
异常通知(After-Throwing Advice):异常通知是在切入点发生异常时执行的额外功能代码。假设当顾客购买商品的数量大于库存数量时,就会发生异常。我们希望在顾客购买商品时检查库存,并在发生异常时执行异常通知,向顾客显示错误信息并处理异常情况
过滤通知(After-Returning Advice):过滤通知是在切入点成功执行后执行的额外功能代码。假设我们有一个特殊会员组,他们在购买商品时可以获得额外的积分。我们可以使用过滤通知来筛选出这些特殊会员,并在成功购买后给他们添加积分
④代理:
专业解释:将通知应用到目标对象后创建的对象(代理=目标+通知)

通俗理解:在书店中,收银员是一个代理角色。他们既代表顾客与商品交互,又代表书店执行一些额外的任务。当顾客带着商品到收银台时,收银员会扫描每个商品的条形码,获取商品信息并计算总价。这里,收银员即充当了顾客与商品之间的代理角色,也充当了超市执行计算总价等额外任务的代理角色

⑤切入点:
专业解释:

多个连接点的集合,定义了通知应该应用到那些连接点 (也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)

通俗理解:在书店场景中,我们可能希望在计算折扣方法之前或之后记录日志和进行库存管理。这些切入点决定了我们在代码中操作的位置

⑥适配器:
专业解释:适配器是一个中间组件,用于将面向切面编程框架与原始的业务逻辑代码连接起来(适配器=通知(Advice)+切入点(Pointcut))

通俗理解:在书店场景中,适配器可以将代理对象与书店的购买图书业务逻辑连接起来,使得代理对象能够在购买图书的过程中添加额外的功能

2.代码演示
        在上面场景模拟的代码中,我们能够发现记录日志的代码基本相同,那么有没有可能将这部分的代码抽取出来进行封装,统一进行维护呢?同时也可以将日志代码和业务代码完全分离,解耦合

  那么我们便可以将业务方法中的非业务核心代码(日志记录)抽离出来形成一个横切面,并且将这个横切面封装成一个对象,将所有的记录日志的代码写到这个对象中,以实现与业务代码的分离,这便是面向切面编程的思想

2.1将记录日志的代码进行封装
 

三.案例演示 


1.前置通知
1.1 先准备接口

package com.lya.aop.biz;public interface IBookBiz {// 购书public boolean buy(String userName, String bookName, Double price);// 发表书评public void comment(String userName, String comments);
}

1.2然后再准备好实现类

package com.lya.aop.biz.impl;import com.YU.aop.biz.IBookBiz;
import com.YU.aop.exception.PriceException;public class BookBizImpl implements IBookBiz {public BookBizImpl() {super();}public boolean buy(String userName, String bookName, Double price) {// 通过控制台的输出方式模拟购书if (null == price || price <= 0) {throw new PriceException("book price exception");}System.out.println(userName + " buy " + bookName + ", spend " + price);return true;}public void comment(String userName, String comments) {// 通过控制台的输出方式模拟发表书评System.out.println(userName + " say:" + comments);}}

1.3对我们的目标对象进行JavaBean配置 

<!--目标对象-->
    <bean class="com.lya.aop.biz.impl.BookBizImpl" id="bookBiz"></bean>

1.4 编写前置系统日志通知

package com.lya.aop.advice;import java.lang.reflect.Method;
import java.util.Arrays;import org.springframework.aop.MethodBeforeAdvice;/*** 买书、评论前加系统日志* @author YU**/
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
//        在这里,可以获取到目标类的全路径及方法及方法参数,然后就可以将他们写到日志表里去String target = arg2.getClass().getName();String methodName = arg0.getName();String args = Arrays.toString(arg1);System.out.println("【前置通知:系统日志】:"+target+"."+methodName+"("+args+")被调用了");}}

1.5配置系统通知XML中的JavaBean

<!--通知-->
    <bean class="com.lya.aop.advice.MyMethodBeforeAdvice" id="myMethodBeforeAdvice"></bean>

1.6 配置代理XML中的JavaBean

<!-- 代理--><bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy"><!-- 配置目标对象 --><property name="target" ref="bookBiz"></property><!-- 配置代理接口,目标对象的接口 --><property name="proxyInterfaces"><value>com.YU.aop.biz.IBookBiz</value></property><property name="interceptorNames"><list><value>myMethodBeforeAdvice</value></list></property></bean>

1.7 测试代码开始测试

package com.lya.util;import com.lya.biz.IBookBiz;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author 程序猿-小李哥* @site www.xiaolige.com* @company 猪八戒有限集团* @create 2023-08-17-15:34*/
public class Demo {public static void main(String[] args) {
//        今天所学:
//        1.AOP的介绍:专心做事//       2专业术语
//        1.连接点
//        2.通知:前,后,环绕
//        3.目标
//        4.代理
//        代理=目标+通知//        3配置xml初始化Spring容器IOCClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("spring.xml");//        演示一:目标对象
//        BookBizImpl bookBiz = context.getBean("bookTarget",BookBizImpl.class);
//        bookBiz.buy("晓东","欠你一夜",2000d);
//        bookBiz.comment("晓东","不看亏了,看了真爽啊!");//        演示二:前置通知
//        错误:类型强转,Object proxy1 = context.getBean("proxy");System.out.println(proxy1.getClass()+"代理的类型");
//      com.sun.proxy.$Proxy5代理的类型//      这里proxy==new bookbizimpl
//        BookBizImpl proxy = context.getBean("proxy",BookBizImpl.class);
//        proxy.buy("晓东","欠你一夜",2000d);
//        proxy.comment("晓东","不看亏了,看了真爽啊!");//        使用接口接收代理对象!!!因为代理对象实现了接口在xml中IBookBiz proxy = context.getBean("proxy",IBookBiz.class);proxy.buy("晓东","欠你一夜",2000d);proxy.comment("晓东","不看亏了,看了真爽啊!");}
}

注意这里有一个报错问题!!!

因为proxy代理已经实现了接口可以看作为一个实现类

 使用接口接收代理对象!!!因为代理对象实现了接口在xml中

测试结果:

 由测试结果可得知,不仅获取到了我们的参数,同时根据方法获取到了我们的系统日志,也就是前置通知

2. 后置通知
2.1 先准备好后置通知的系统日志

package com.zking.aop.advice;import java.lang.reflect.Method;
import java.util.Arrays;import org.springframework.aop.AfterReturningAdvice;/*** 买书返利* @author Administrator**/
public class MyAfterReturningAdvice implements AfterReturningAdvice {@Overridepublic void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {String target = arg3.getClass().getName();String methodName = arg1.getName();String args = Arrays.toString(arg2);System.out.println("【后置通知:买书返利】:"+target+"."+methodName+"("+args+")被调用了,"+"该方法被调用后的返回值为:"+arg0);}}

2.2 配置后置系统通知的XML的JavaBean

<!--后置通知-->
    <bean class="com.YU.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean>
并在前面已经配置好的代理接口中添加一个value值

 2.3 测试结果

 由测试结果我们可以得知,后置通知永远都在方法执行后才会显示通知,与前置通知不同的是每次前面的方法调用后都会返回一个参数

3.环绕通知


3.1 环绕通知就是前置通知和后置通知的结合,在实际应用开发中,我们一般不会单独编写前置通知和后置通知,单独使用前置通知或者后置通知时,我们会使用环绕通知,将里面前置(后置)通知的功能注释,以达到单独使用的目的

3.2 环绕通知的系统日志

package com.lya.advice;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;import java.util.Arrays;/*** @author 程序猿-小李哥* @site www.xiaolige.com* @company 猪八戒有限集团* @create 2023-08-17-18:45** 环绕通知*/
public class AroundAdvice  implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {//获取目标对象的执行方法String methodName=invocation.getMethod().getName();//获取目标对象执行方法的参数Object[] params=invocation.getArguments();//获取目标对象Object target = invocation.getThis();System.out.println("[环绕通知] "+target.getClass().getName()+"."+methodName+","+ "执行的参数:"+ Arrays.toString(params));Object returnValue = invocation.proceed(); //放行操作System.out.println("[环绕通知] 返回参数等于:"+returnValue);return returnValue;}
}

3.3 配置环绕通知的XML的JavaBean与前置通知和后置通知一致

3.4 测试结果

由测试结果得知,环绕通知就是前置通知和后置通知的结合,优点就是不需要再多次去进行配置及编码,所以就像我们前面所说在实际开发应用中我们一般都会选择使用环绕通知

4.异常通知
4.1 异常通知的系统日志和其他系统日志不同的是,方法名为固定的afterThrowing,不能修改

package com.lya.advice;/*** @author 程序猿-小李哥* @site www.xiaolige.com* @company 猪八戒有限集团* @create 2023-08-17-18:56*/import org.springframework.aop.ThrowsAdvice;/*** 异常通知*/
public class ExceptionAdvice implements ThrowsAdvice {public void afterThrowing(PriceException e) {System.out.println("[异常通知] 价格异常,撤销订单!");}
}

价格异常

package com.lya.advice;/*** @author 程序猿-小李哥* @site www.xiaolige.com* @company 猪八戒有限集团* @create 2023-08-17-19:04** 价格异常通知*/
public class PriceException  extends RuntimeException {public PriceException() {super();}public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);}public PriceException(String message, Throwable cause) {super(message, cause);}public PriceException(String message) {super(message);}public PriceException(Throwable cause) {super(cause);}
}

4.2 在我们正常程序出问题没有去配置异常通知时会出现报错,并且不会执行后面的后置通知,如以下情况

4.3 异常处理配置和前面的配置相同

4.4 当我们配置好异常通知模块时,程序出现异常时会上报日志进行提示

5.过滤通知
5.1 直接在XML中配置JavaBean

<!--过滤通知-->
    <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="regexpMethodPointcutAdvisor">
        <property name="advice" ref="myAfterReturningAdvice"></property>
        <property name="pattern" value=".*buy"></property>
    </bean>


 

将图中指出部分替换成过滤通知

 测试结果:

对比框中内容,在调用过buy方法后进行过滤,第二次调用时不再buy方法而是comment方法 

四.总结
aop是面向切面编程,普通程序由上而下正常执行,aop的程序执行是先执行到目标对象的目标方法中,如果连接点上由前置通知,则先执行前置通知再执行目标方法,最后如果目标方法有后置通知则最后执行后置通知代码,不管是前置通知,后置通知,环绕通知,异常通知,过滤通知,代码都是非业务核心代码,如日志、事务的管理(开启、提交、回滚)         
 

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

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

相关文章

JVM虚拟机:初始化的介绍

本文重点 我们前面学习了三个步骤: 装载 连接 初始化 初始化 初始化的时候,会为静态成员变量赋值初始值,它有两种方式: ①声明类变量是指定初始值 ②使用静态代码块为类变量指定初始值 例子 最后输出的结果为3,它的过程是这样的: main方法中输出T.count,由于count是…

自签证书让Chrome信任的方式

自签证书让Chrome信任的方式(域名情况) 网站是搭建在linux上的&#xff0c;内容大概是一个code-server;我要在windows的chrome中访问&#xff0c;在Linux机器上自签了一个证书&#xff0c;准备让windows中的chrome信任。linux装好openssl。首先买好域名&#xff0c;配置好解析…

tkinter+爬虫+pygame实现音乐播放器

文章目录 前文安装模块示意图爬虫完整代码pygametkinter完整代码结尾前文 本文将涉及爬虫(数据的获取),pygame(音乐播放器),tkinter(界面显示),将他们汇聚到一起制造一个音乐播放器,欢迎大家的订阅。 安装模块 pip install requests,parsel,lxpy,pygame 示意图

Flask下载文件报错304 NOT MODIFIED

文章目录 问题描述解决方案参考文献 问题描述 前端 Vue 下下来的文件无法正常打开&#xff0c;大小比正常的略大一点&#xff0c;通过 Postman 直接调用是正常的 解决方案 由前端解决 如果响应大小比文件略大一点&#xff0c;从 responses 中取出关键数据再组成文件如果响应…

open cv学习 (二)色彩空间和通道

色彩空间和通道 demo1 import cv2hsv_image cv2.imread("./img.png")cv2.imshow("img", hsv_image) hsv_image cv2.cvtColor(hsv_image, cv2.COLOR_BGR2HSV) h, s, v cv2.split(hsv_image) cv2.imshow("B", h) cv2.imshow("G", s…

文本图片怎么转Excel?分享一些好用的方法

在处理数据时&#xff0c;Excel 是一个非常强大的工具&#xff0c;但有时候需要将文本和图片转换为 Excel 格式&#xff0c;这可能会让人感到困惑。在本文中&#xff0c;我们将介绍一些好用的方法&#xff0c;以便您能够轻松地将文本和图片转换成 Excel 格式。 将文本图片为Exc…

部署piwigo网页 通过cpolar分享本地电脑上的图片

通过cpolar分享本地电脑上有趣的照片&#xff1a;发布piwigo网页 文章目录 通过cpolar分享本地电脑上有趣的照片&#xff1a;发布piwigo网页前言1. 设定一条内网穿透数据隧道2. 与piwigo网站绑定3. 在创建隧道界面填写关键信息4. 隧道创建完成 总结 前言 首先在本地电脑上部署…

K8S核心组件etcd详解(上)

1 介绍 https://etcd.io/docs/v3.5/ etcd是一个高可用的分布式键值存储系统&#xff0c;是CoreOS&#xff08;现在隶属于Red Hat&#xff09;公司开发的一个开源项目。它提供了一个简单的接口来存储和检索键值对数据&#xff0c;并使用Raft协议实现了分布式一致性。etcd广泛应用…

关于计数以及Index返回订单号升级版002(控制字符长度,控制年月标记,拾取未使用编号)--使用两个表来满足操作

1实现步骤以及说明 1.根据参数获取当前setNoIndex表里现在的No的index值&#xff0c;如果包含关键字当前对应数据&#xff0c;则现在SetIndexNoLeft 表中找到有无未使用并未占用的那条数据&#xff08;被占用的数据IsTaken1&#xff0c;生成后使用当前时间与updated时间进行比…

Django图书商城系统实战开发-实现订单管理

Django图书商城系统实战开发-实现订单管理 简介 在本教程中&#xff0c;我们将继续基于Django框架开发图书商城系统&#xff0c;这次的重点是实现订单管理功能。订单管理是一个电子商务系统中非常重要的部分&#xff0c;它涉及到用户下单、支付、发货以及订单状态的管理等方面…

【hive】简单介绍hive的几种join

文章目录 前言1. Common Join2. Map Join介绍&#xff1a;使用方法&#xff1a;限制&#xff1a; 3. Bucket Map Join介绍&#xff1a;好处&#xff1a;使用条件&#xff1a;使用方法&#xff1a; 4. Sort Merge Bucket Map Join介绍&#xff1a;如何使用&#xff1a; 5. Skew …

如何在控制台查看excel内容

背景 最近发现打开电脑的excel很慢&#xff0c;而且使用到的场景很少&#xff0c;也因为mac自带了预览的功能。但是shigen就是闲不住&#xff0c;想自己搞一个excel预览软件&#xff0c;于是在一番技术选型之后&#xff0c;我决定使用python在控制台显示excel的内容。 具体的需…

Redis与MySQL的比较:什么情况下使用Redis更合适?什么情况下使用MySQL更合适?

Redis和MySQL是两种不同类型的数据库&#xff0c;各有自己的特点和适用场景。下面是Redis和MySQL的比较以及它们适合使用的情况&#xff1a; Redis适合的场景&#xff1a; 高性能读写&#xff1a;Redis是基于内存的快速Key-Value存储&#xff0c;读写性能非常高。它适用于需要…

NodeJs导出PDF

&#xff08;优于别人&#xff0c;并不高贵&#xff0c;真正的高贵应该是优于过去的自己。——海明威&#xff09; 场景 根据订单参数生成账单PDF 结果 示例代码 /* eslint-disable no-unused-vars */ /* eslint-disable no-undef */ /* eslint-disable complexity */ const…

【jquery实现动态给表格添加删除行,合并指定单元格】

jquery实现动态给表格添加删除行&#xff0c;合并指定单元格 前端技术 jspjquery 动态添加行 //新增行 $("#addRowBtn").click(function(){var rowEl$("<tr><td><input typecheckbox classcheckItem/></td><td><input nam…

NPOI 读取和写入Excel

在C#中使用NPOI库读取和写入Excel文件&#xff0c;你需要先下载并安装NPOI库。你可以在NuGet管理器中搜索NPOI并进行安装。 以下是一个使用NPOI库进行Excel文件读取和写入的示例&#xff1a; 读取Excel文件&#xff1a; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel…

【仿写tomcat】二、扫描java文件,获取带有@WebServlet注解的类

tomcat仿写 项目结构扫描文件servlet注解map容器servlet工具类启动类调用 项目结构 扫描文件之前当然要确定一下项目结构了&#xff0c;我这里的方案是tomcat和项目同级 项目的话就仿照我们平时使用的结构就好了&#xff0c;我们规定所有的静态资源文件都在webApp目录下存放…

【数据库】P1 数据库基本常识

数据库基本常识 数据库 ≠ 数据库管理系统表&#xff08;Table&#xff09;SQL是什么 数据库 ≠ 数据库管理系统 数据库是保存有组织的数据的容器&#xff0c;数据库称为 DB&#xff08;DataBase&#xff09;&#xff1b;数据库管理系统是创建和操纵数据库的软件&#xff0c;数…

充气泵方案设计——便携无线充气泵方案

充气泵方案的进化史是人类历史上电子技术发展史中的一环。电子技术&#xff0c;特别是微电子技术是 20 世纪发展最为迅速、影响最为广泛的技术成就。电子技术的核心是电子器件,电子器件的进步和换代&#xff0c;引起了电子电路极大的变化&#xff0c;出现了很多新的电路和应用。…