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.rhkb.cn/news/99184.html

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

相关文章

HTTPS

HTTPS是什么 HTTPS 属于应用层协议&#xff0c;其原理是通过SSL/TLS协议在HTTP和TCP之间插入一层安全机制。通过SSL/TLS握手过程&#xff0c;客户端和服务器协商出一个对称密钥&#xff0c;用于后续的数据加密和解密&#xff0c;从而保证数据的机密性和完整性。 为什么会需要…

如何使用ChatGPT创建个性化的健身锻炼计划

ChatGPT广泛应用于各个行业&#xff0c;健身也不例外。 ChatGPT 在健身领域的一个常用案例是创建个性化的锻炼计划。 在要求 ChatGPT 创建锻炼计划时&#xff0c;简单地输入自己的目标和当前的健身水平是一个很好的开始。完成此操作后&#xff0c;你还可以使用其他提示和措施来…

利用HTTP代理实现请求路由

嘿&#xff0c;大家好&#xff01;作为一名专业的爬虫程序员&#xff0c;我知道构建一个高效的分布式爬虫系统是一个相当复杂的任务。在这个过程中&#xff0c;实现请求的路由是非常关键的。今天&#xff0c;我将和大家分享一些关于如何利用HTTP代理实现请求路由的实用技巧&…

Const的区别 学习笔记

C程序设计之const八大用法_哔哩哔哩_bilibili const优先修饰const左边&#xff0c;若左边没有东西&#xff0c;则修饰右边的东西 前两个a,b不是指针由于修饰的是int&#xff0c;是类型&#xff0c;所以a和b的值不能改变 c的类型的指针&#xff0c;const修饰的是int&#xff0…

【Apollo】阿波罗自动驾驶技术:引领汽车行业革新

前言 Apollo (阿波罗)是一个开放的、完整的、安全的平台&#xff0c;将帮助汽车行业及自动驾驶领域的合作伙伴结合车辆和硬件系统&#xff0c;快速搭建一套属于自己的自动驾驶系统。 开放能力、共享资源、加速创新、持续共赢是 Apollo 开放平台的口号。百度把自己所拥有的强大、…

怎样通过本地电脑搭建SFTP服务器,并实现公网访问?

本地电脑搭建SFTP服务器&#xff0c;并实现公网访问 文章目录 本地电脑搭建SFTP服务器&#xff0c;并实现公网访问1. 搭建SFTP服务器1.1 下载 freesshd 服务器软件1.3 启动SFTP服务1.4 添加用户1.5 保存所有配置 2. 安装SFTP客户端FileZilla测试2.1 配置一个本地SFTP站点2.2 内…

AutoSAR配置与实践(基础篇)2.5 RTE对数据一致性的管理

传送门 点击返回 ->AUTOSAR配置与实践总目录 AutoSAR配置与实践&#xff08;基础篇&#xff09;2.5 RTE对数据一致性的管理 一、 数据一致性问题引入二、 数据一致性的管理2.1 RTE管理 (SWC间)2.2 中断保护 (SWC内)2.3 变量保护IRVS (SWC内)2.4 Task分配2.5 任务抢占控制 一…

HTTP 握手过程

HTTP 握手过程 TCP 建立连接 3 次握手 客户端请求连接服务器服务器响应成功客户端回应服务器准备开始连接 TCP 结束连接 4 次挥手 客户端向服务器发送&#xff0c;断开请求服务器向客户端发送&#xff0c;还有数据没有传输完毕&#xff0c;请稍等服务器向客户端发送&#x…

Air780EG —— 合宙4G定位解决方案

定位模式&#xff1a; 外部单片机控制模式(常见于AT固件客户)&#xff1a; 开机 -> 搜星 -> 定位成功 -> 上报 -> 关机 780E自行控制模式(常见于二次开发客户&#xff0c;AT用户也可以使用): 开机 -> 搜星 -> 定位成功 -> 模块休眠&#xff0c;关闭GP…

Brain:背内侧前额叶/背侧前扣带皮层(dmPFC/dACC)的相关争议

摘要 背内侧前额叶皮层/背侧前扣带皮层(dmPFC/dACC)是一个功能存在诸多理论和争议的脑区。甚至其精确的解剖边界也饱受争议。在过去的几十年里&#xff0c;dmPFC/dACC与15种以上的认知过程相关联&#xff0c;这些过程有时看起来完全无关(例如&#xff0c;身体感知、认知冲突)。…

志凌海纳 SmartX 携手灵雀云推出全栈云原生联合解决方案

近日&#xff0c;北京志凌海纳科技有限公司&#xff08;以下简称“SmartX”&#xff09;与北京凌云雀科技有限公司&#xff08;以下简称“灵雀云”&#xff09;联合推出全栈云原生联合解决方案&#xff0c;为客户提供从基础设施到容器云平台的一站式服务&#xff0c;加速客户云…

RK3399平台开发系列讲解(内核调试篇)Valgrind使用案例

🚀返回专栏总目录 文章目录 一、使用未初始化的内存案例二、内存泄露三、在内存被释放后进行读/写案例四、从已分配内存块的尾部进行读/写案例五、两次释放内存案例沉淀、分享、成长,让自己和他人都能有所收获!😄 📢Valgrind 是一个开源的内存调试和性能分析工具,用于…

电脑找不到MSVCR120.dll怎么办?MSVCR120.dll是什么?

在我们的日常生活和工作中&#xff0c;电脑故障是难以避免的问题。而MSVCR120.dll文件是Windows系统中的一个重要组件&#xff0c;如果出现损坏或丢失&#xff0c;可能会导致程序无法正常运行&#xff0c;这个问题可能是由于系统文件损坏、病毒感染等原因导致的。因此&#xff…

高忆管理:市盈率一般多少合理?

市盈率&#xff08;PE Ratio&#xff09;是衡量一只股票估值水平的重要目标&#xff0c;其计算公式为股票当前市价除以每股收益。一般来说&#xff0c;市盈率较低的股票被认为是具有出资价值的好股票&#xff0c;而市盈率较高的股票则或许被认为是过度投机或者受商场热潮影响的…

leetcode:字符串相乘(两种方法)

题目&#xff1a; 给定两个以字符串形式表示的非负整数 num1 和 num2&#xff0c;返回 num1 和 num2 的乘积&#xff0c;它们的乘积也表示为字符串形式。 注意&#xff1a;不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 "2", nu…

linux部署kafka3.5.1(单机)

一、下载jdk17 kafka3.x版本需要jdk11以上版本才能更好的兼容&#xff0c;jdk11、jdk17都是LTS长期维护版本&#xff0c;而且jdk17支持springboot3.x,所以我选择了openjdk17。 下载地址: Archived OpenJDK GA Releaseshttps://jdk.java.net/archive/ 二、上传jdk安装包解压 …

docker tomcat时间少8小时问题解决

docker容器与系统时间一致并且正确&#xff0c;但是java程序在运行中通过log日志发现发了8小时 解决方法 修改docker容器中tomcat/bin/catalina.sh文件&#xff0c;添加一下内容 JAVA_OPTS"$JAVA_OPTS -Dfile.encodingUTF8 -Duser.timezoneGMT08" 附 操作命令 一…

SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第一天)Mybatis的学习

SSM框架的学习与应用(Spring Spring MVC MyBatis)-Java EE企业级应用开发学习记录&#xff08;第一天&#xff09;Mybatis的学习 一、当前的主流框架介绍(这就是后期我会发出来的框架学习) Spring框架 ​ Spring是一个开源框架&#xff0c;是为了解决企业应用程序开发复杂…

VMware虚拟安装Ubuntu,然后切换Ubuntu内核版本

无论你选择哪种方法&#xff0c;一旦进入 GRUB 引导菜单&#xff0c;你应该能够选择需要的内核版本并启动系统。 打开终端&#xff1a;你可以通过按下 Ctrl Alt T 快捷键来打开终端。 使用 sudo&#xff1a;切换内核需要管理员权限&#xff0c;因此你需要使用 sudo 命令。首…

【Spring Boot】构建RESTful服务 — 实战:实现Web API版本控制

实战&#xff1a;实现Web API版本控制 前面介绍了Spring Boot如何构建RESTful风格的Web应用接口以及使用Swagger生成API的接口文档。如果业务需求变更&#xff0c;Web API功能发生变化时应该如何处理呢&#xff1f;可以通过Web API的版本控制来处理。 1.为什么进行版本控制 …