03_03_初识SpringAOP和应用

一、SpringAOP的初识与原理

1、概述

  • AOP:面向切面编程
  • OOP:面向对象编程
  • 面相切面编程:是基于OOP基础之上的新编程思想,OOP面向的主要是对象是类,而AOP面向的主要对象是切面,它在处理日志、安全管理、事务管理、权限控制等方面具有非常重要的作用。是一种非侵入式(不改变原来的代码)的代码增强的开发工具,可以减
    少重复代码的编写,降低模块间的耦合度,并有利于提高程序的可拓展性可维护性
  • AOP是Spring中的核心点,虽然IOC容器没有依赖AOP,但是AOP提供了非常强大的功能,用来对IOC进行补充。
  • Spring AOP 是基于动态代理实现的,如果被代理的对象已经实现了某个接口,则可以使用JDK动态代理(核心是invocationHandler接口和Proxy类)的方式来创建代理对象;如果被代理对象没有实现某个接口,可以使用Cglib(核心是MethodInterceptor接口和Enhancer类),基于继承的方式,生成一个被代理对象的子类作为代理;
  • 简而言之,在程序运行期间,在不修改原有代码的情况下,增强跟主业务没有关系的公共功能代码到之前写好的方法中的指定位置, 这种编程的方式叫AOP;

2、代理模式

  • AOP的底层是通过代理模式来实现。
  • 代理模式是一种设计模式,其主要目的是为其他对象提供一种代理,以控制对该对象的访问。这意味着用户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
  • 代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。
  • 总的来说,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能。
  • 代理模型分为静态代理和动态代理(JDK动态代理CGLB动态代理)两种。

2.1 静态代理

  • 静态代理就是需要手动去为每一个被代理对象去创建一个代理类。
  • 例如,有一个游戏代练的示例:
    /*** 游戏接口(公共接口)*/
    public interface IGamePlayer {//登录游戏void start();//玩游戏void play();
    }
    
    /*** 菜鸟游戏玩家(目标对象-被代理对象)*/
    public class GamePlayer implements IGamePlayer {//玩家名称private String name;//构造赋值public GamePlayer(String name){this.name = name;}@Overridepublic void start() {System.out.println("正在登录游戏........");System.out.println(name + ":开始了游戏");}@Overridepublic void play() {System.out.println(name + "技术太low,游戏失败!");}
    }
    
        @Testpublic void testProxy(){GamePlayer player = new GamePlayer("菜鸟");player.start();player.play();}
    

菜鸟游戏把把输

  • 现在,因为菜鸟玩家的技术能力太低,玩游戏把把都输,他需要一个代练帮他玩游戏提升等级,我们可以帮他创建一个代理对象帮他代玩。
    /*** 代练游戏玩家(静态代理类)*/
    public class ProxyGamePlayer implements IGamePlayer{//代练private String proxyName;//菜鸟玩家private GamePlayer gamePlayer;public ProxyGamePlayer(String name){this.proxyName = name;this.gamePlayer = new GamePlayer(name);}@Overridepublic void start() {//代练以菜鸟的身份去玩游戏System.out.println("拿到"+proxyName+"的游戏账号密码");gamePlayer.start();}@Overridepublic void play() {//代练帮菜鸟玩游戏System.out.println("代练技术太强,赢得了游戏!");}
    }
    
     @Testpublic void testProxy(){IGamePlayer gamePlayer = new ProxyGamePlayer("菜鸟");gamePlayer.start();gamePlayer.play();}
    

代练赢

  • 静态代理的弊端
    • 需要为每一个被代理的类创建一个代理类,虽然这种方式可以实现,但是成本太高。

2.2 动态代理

2.2.1 JDK动态代理
  • 核心关键

    • 一个类:Proxy
      • 概述:Proxy是所有代理类的基类;
      • 作用:创建代理对象,newProxyInstance();
    • 一个接口:InvocationHandler
      • 概述:实现动态织入效果的关键接口;
      • 作用:通过反射机制,执行invoke()方法来实现动态织入的效果;
  • 还是以游戏代练为例,实现Jdk动态代理:

    • 创建一个为被代理对象(目标对象)创建代理类的工具类
    public class GamePlayerProxy {/*** 被代理对象(目标对象)*/private Object targetObj;/*** 有参构造,避免目标对象为null* @param targetObj*/public GamePlayerProxy(Object targetObj){this.targetObj = targetObj;}/*** 获取代理类对象*/public Object getProxyObject(){Object proxyObject = null;/**  ClassLoader loader 类加载器,通常指被代理类的接口的类加载器*  Class<?> [] interfaces  类型,通常指被代理类的接口的类型*  InvacationHandler handler 委托执行的代理类,具体功能增强的逻辑在这里实现*/ClassLoader loader = targetObj.getClass().getClassLoader();Class<?>[] interfaces = targetObj.getClass().getInterfaces();proxyObject = Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {//重写invoke()实现动态织入效果@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object invoke = "";if (method.getName().equals("play")){//逻辑替换System.out.println("代练技术太强,已成功帮菜鸟赢得了游戏!");}else {//逻辑增强//执行被代理的方法/** Object targetObj 被代理的对象* Object... args 被代理的方法的参数*/invoke = method.invoke(targetObj, args);}return invoke;}});//返回创建好的代理类对象return proxyObject;}
    
    • 使用代理类对象来代玩游戏:
      	@Testpublic void testJdkProxy(){//被代理(目标)对象IGamePlayer gamePlayer = new GamePlayer("菜鸟");//获取代理对象(代理对象不能转换为目标对象)GamePlayerProxy gamePlayerProxy = new GamePlayerProxy(gamePlayer);IGamePlayer player = (IGamePlayer) gamePlayerProxy.getProxyObject();//代理玩游戏player.start();player.play();}
    

    jdk动态代理实现

2.2.2 CGLB动态代理
  • 后期补充…

二、SpringAOP的应用

  • 通过上述的例子,已经实现了代打游戏的目的,但是这种动态代理的实现方式调用的是JDK的基本实现,如果需要被代理的目标对象没有实现任何接口,那么是无法为它创建代理对象的,这也是致命的缺陷。而在Spring中我们可以不编写上述如此复杂的代码,只需要利用AOP,就能够轻轻松松实现上述功能,当然,Spring AOP的底层实现也依赖的是动态代理。

1、AOP的核心概念及术语

概念图

  • 切面(Aspect): 指关注点模块化,这个关注点可能会横切多个对象。
  • 连接点(Join point): 在程序执行过程中某个特定的点。在Spring AOP中,一个连接点总
    是代表一个方法的执行。
  • 通知(Advice): 在切面的某个特定的连接点上执行的动作。
  • 切点(Pointcut): 匹配连接点的断言。
  • 引入(Introduction): 声明额外的方法或者某个类型的字段。
  • 目标对象(Target object): 被一个或者多个切面所通知的对象。
  • 织入(Weaving): 把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象的过程。这个过程可以在编译时、类加载时、运行时完成。

2、AOP的通知类型

  • 前置通知
    • 语法:@Before(value=“execution()”)
    • 执行时机:在切入点表达式中指定的方法执行之前执行(无论是否发生异常,都会执行);
  • 后置通知
    • 语法:@After(value=“execution()”)
    • 执行时机:在切入点表达式中指定的方法执行之后执行(无论是否发生异常,都会执行);
  • 返回通知
    • 语法:@AfterReturning(pointcut = “execution()”,returning = “result”)
    • 执行时机:在切入点表达式中指定的方法返回结果时执行(如果发生异常,则不执行);
  • 异常通知
    • 语法:@AfterThrowing(pointcut = “execution()”,throwing = “e”)
    • 执行时机:在切入点表达式中指定的方法发生异常时执行;
  • 环绕通知
    • 语法:@Around(value = “pointCut()”)
    • 作用:环绕通知可以对前置通知、后置通知、返回通知、异常通知进行整合使用;

3、AOP的应用场景

  • 日志管理
  • 权限认证
  • 安全检查
  • 事务控制

4、AOP的相关配置

4.1 添加pom依赖

 	<!--Aop的AspectJ框架的jar包 --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.28</version></dependency>

4.2 将目标类加入IOC容器

/**
* 计算器接口(公共接口)
*/
public interface CalculatorService {int add(int a,int b);int sub(int a,int b);int mul(int a,int b);int div(int a,int b);}
/**
*计算器实现类对象(目标对象)
*/
@Service
public class CalculatorServiceImpl implements CalculatorService {@Overridepublic int add(int a, int b) {System.out.println("正在执行加法......");return a+b;}@Overridepublic int sub(int a, int b) {System.out.println("正在执行减法......");return a-b;}@Overridepublic int mul(int a, int b) {System.out.println("正在执行乘法......");return a*b;}@Overridepublic int div(int a, int b) {System.out.println("正在执行除法......");return a/b;}
}

4.3 声明切面类并加入IOC容器

@Component   //标识该类是一个组件类(保证这个切面在IOC容器中)
@Aspect      //标识该类是一个切面
public class LogUtil {//可以采用引用切点的方式,让其他通知共同使用@Pointcut(value = "execution(* org.example.mvc.impl.*.*(..))")public void pointCut(){}//前置通知@Before(value = "execution(public int org.example.mvc..CalculatorServiceImpl.*(int, int))")public void beforeMethod(JoinPoint joinPoint){//获取方法名称String methodName = joinPoint.getSignature().getName();//获取参数Object[] args = joinPoint.getArgs();System.out.println("【前置通知】计算器  "+methodName+"  方法,执行参数为:"+ Arrays.toString(args));}//后置通知
//    @After("pointCut() && @annotation(logger)")@After(value = "pointCut()")public  void afterMethod(JoinPoint joinPoint){//获取方法名称String methodName = joinPoint.getSignature().getName();System.out.println("【后置通知】计算器  "+methodName+" 方法,执行完毕");}//后置返回通知@AfterReturning(pointcut = "pointCut()",returning = "result")//注意:returning属性中的返回结果名称要与入参中的参数名一致public void afterReturn(JoinPoint joinPoint,Object result){//获取方法名称String methodName = joinPoint.getSignature().getName();System.out.println("【后置返回通知】计算器 "+methodName+" 方法,执行结果为:"+ result);}//后置异常通知@AfterThrowing(pointcut = "pointCut()",throwing = "ex")//注意:throwing属性中的异常名称要与入参中的异常参数名一致public void afterThrowing(JoinPoint joinPoint,Exception ex){//获取方法名称String methodName = joinPoint.getSignature().getName();StringWriter stringWriter = new StringWriter();ex.printStackTrace(new PrintWriter(stringWriter,true));System.out.println("【后置异常通知】计算器 "+methodName+" 方法,执行时出现异常:" + stringWriter.getBuffer().toString());}//环绕通知@Around(value = "pointCut()")public Object around(ProceedingJoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();Object result = null;try {//前置通知System.out.println("【环绕-前置通知】计算器 "+methodName+" 方法,执行参数为:"+ Arrays.toString(args));//触发目标对象中的目标方法result = joinPoint.proceed();//返回通知System.out.println("【环绕-返回通知】计算器 "+methodName+" 方法,执行结果为:"+ result);} catch (Throwable throwable) {throwable.printStackTrace();//异常通知System.out.println("【环绕-异常通知】计算器 "+methodName+" 方法,执行时出现异常:" + throwable.getMessage());} finally {//后置通知System.out.println("【环绕-后置通知】计算器 "+methodName+" 方法,执行完毕");}return result;}}

切点表达式

  • 切点表达式语法:

    • execution(访问修饰符 方法返回值类型 包名.类名.方法名(参数...))
      • (1)访问修饰符:可写可不写;
      • (2)方法返回值类型:如果是JDK自带的类型,则直接可以用类型名,例如(Int)。如果不是,则需要写上自定义类型的全局限定名,如果返回值的类型不同,则可以用通用符 * 代表任何类型;
      • (3)包名:可以写具体的包名,也可以用通用符*占位代表任何包名,..代表子孙包。例如:cn.、cn.trs.、cn.trs.service…*
      • (4)类的全局限定名:可以写具体的类名,也可以使用通配符*代表任何类的名字;
      • (5)方法名:可以写具体的方法名,也可以使用通配符*代表任何方法的名字,也可以模糊匹配 *Add => userAdd()、roleAdd()
      • (6)参数:如果是JDK自带类型,可以不用写类型的全局限定名,否则,需要写上自定义类型的全局限定名。如果需要匹配任意参数可以写:..代替
  • 合并切点表达式:

    • 可以使用 &&|| !等符号进行合并操作,也可以通过名字来指向切点表达式。
     //&&:两个表达式同时
    execution( public int cn.trs.inter.MyCalculator.*(..)) && execution(* *.*(int,int) )
    //||:任意满足一个表达式即可
    execution( public int cn.trs.inter.MyCalculator.*(..)) && execution(* *.*(int,int) )
    //!:只要不是这个位置都可以进行切入
    //&&:两个表达式同时
    execution( public int cn.trs.inter.MyCalculator.*(..))
    

4.4 开启组件扫描和AOP的注解支持

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><!--开启组件扫描--><context:component-scan base-package="org.example"></context:component-scan><!--开启AspectJ的注解对AOP的支持--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
  • 在Spring容器中,如果有接口,那么会使用jdk自带的动态代理,如果没有接口,那么会使用cglib的动态代理。

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

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

相关文章

结账和反结账

结账与反结账功能在财务软件和会计系统中扮演着重要的角色&#xff0c;以下是关于这两个功能的详细解释&#xff1a; 一、结账功能 结账功能是计算和结转各个会计科目本期发生额和期末余额的过程&#xff0c;同时标志着一定时期内财务活动的结束和财务数据的固化。结账功能的…

C语言 链表经典OJ题

链表经典OJ题 移除链表元素链表的中间节点反转链表合并两个有序链表分割链表 移除链表元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head […

python小练习03

1.绘制奥运五环旗 #奥运五环的绘制 import turtle as t t.pensize(3) t.speed(0) def draw_circles():i0while i <4:args [[-60,0,"blue"],[0,0,"black"],[60,0,"red"],[-30,-30,"yellow"],[30,-30,"green"]]#定义一个…

lua vm 二: 查看字节码、看懂字节码

本文讲一讲如何查看 lua 的字节码&#xff08;bytecode&#xff09;&#xff0c;以及如何看懂字节码。 以下分析基于 lua-5.4.6&#xff0c;下载地址&#xff1a;https://lua.org/ftp/ 。 1. 查看字节码 1.1 方法一&#xff1a;使用 luac luac 是 lua 自带的编译程序&#x…

MySQL(三) - 基础操作

一、索引 由于我们在使用数据库的时候&#xff0c;大部分操作的都是查询操作&#xff0c;但是我们每一次进行查询都需要遍历一遍表中所有数据&#xff0c;这会花费O(n)的时间&#xff0c;因此数据引入了“索引” 也就是在底层使用了数据结构来进行优化查询的操作&#xff0c;但…

【TB作品】MSP430F149单片机,广告牌,滚动显示

LCD1602滚动显示切换播放暂停字符串 显示Public Places 显示No Smoking 播放 暂停 部分代码 char zifu1[] "Public Places "; char zifu2[] "Class Now "; char zifu3[] "No admittance "; char *zifu[] { zifu1, zifu2, zifu3 }…

优思学院|客户质量工程师CQE岗位的未来发展,你怎么看?

在现代工业的发展背景下&#xff0c;客户质量工程师&#xff08;CQE&#xff09;正逐渐成为企业质量管理体系中的关键角色。随着全球化和数字化的不断推进&#xff0c;CQE的职责不仅限于传统的质量控制&#xff0c;更包括了质量管理体系的设计和优化、客户关系的管理、以及在整…

【UML用户指南】-06-面向对象建模-关系(relationship)

目录 1、面向对象建模常见的关系 2、关系的组成元素 3、依赖关系 4、泛化关系 5、关联关系 关联的四种修饰 1.名称 2.角色 3.多重性 4.聚合 6、常用建模技术 6.1、对简单依赖建模 6.2、对单继承建模 6.3、对结构关系建模 1、面向对象建模常见的关系 依赖 &#x…

pyqt绘制各种直线

pyqt绘制各种直线 介绍效果代码 介绍 使用pyqt绘制各种直线 效果 代码 import sys from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtGui import QPainter, QPen, QPainterPath, QColor, QBrush from PyQt5.QtCore import Qt, QPoint, QLineF, QPointFclass…

JVM 指针压缩

运用java内存对齐填充&#xff0c;对java内存进行8字节划分&#xff0c;java对象指针映射到每个划分区域上&#xff0c;使得4个字节&#xff08;32位&#xff09;表示2^32个地址&#xff0c;从而使4个字节指针映射32G内存空间。 1.为什么进行指针压缩&#xff1a; jvm从32位变…

【全开源】Java代驾小程序APP代驾跑腿源码微信小程序代驾源码

&#x1f697;代驾小程序&#xff1a;便捷、安全的出行新选择 一、引言&#xff1a;出行新风尚 在如今繁忙的都市生活中&#xff0c;出行问题一直是人们关注的焦点。代驾小程序的出现&#xff0c;为我们提供了一种便捷、安全的出行新选择。无论是酒后不能驾车&#xff0c;还是…

牛客网刷题 | BC116 [NOIP2013]记数问题

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 试计算在区间1 到n…

LeetCode:环形链表II

文章收录于LeetCode专栏 LeetCode地址 环形链表II 题目 给定一个链表&#xff0c;返回链表开始入环的第一个节点。如果链表无环&#xff0c;则返回null。   为了表示给定链表中的环&#xff0c;我们使用整数pos来表示链表尾连接到链表中的位置&#xff08;索引从0开始&#…

安防监控视频平台LntonCVS视频监控汇聚平台遏制校园暴力保护校园学生安全应用方案

未成年人被誉为祖国的花朵&#xff0c;是我们国家的未来。然而&#xff0c;最近频繁曝出的未成年霸凌事件却引发了社会的广泛关注。这些事件手段残忍&#xff0c;事态恶劣&#xff0c;引发了全社会对如何保护未成年身心健康、规避霸凌事件发生的深刻思考。 为了更好地保障学生的…

使用软件分享--剪映(不需要会员版)剪映 Jianying_pro_3_2_0_8778_beta9_jianyingpro_beta(Windows)

专栏介绍&#xff1a;本专栏主要分享一些实用的软件&#xff08;Po Jie版&#xff09;&#xff1b; 声明1&#xff1a;软件不保证时效性&#xff1b;只能保证在写本文时&#xff0c;该软件是可用的&#xff1b;不保证后续时间该软件能一直正常运行&#xff1b;不保证没有bug&am…

深度学习-06-手动进行反向传播

深度学习-06-手动进行反向传播 本文是《深度学习入门2-自製框架》 的学习笔记&#xff0c;记录自己学习心得&#xff0c;以及对重点知识的理解。如果内容对你有帮助&#xff0c;请支持正版&#xff0c;去购买正版书籍&#xff0c;支持正版书籍不仅是尊重作者的辛勤劳动&#xf…

【网关】工业智能网关-02

一 公司简介 保定飞凌嵌入式技术有限公司始于2006年&#xff0c;是一家专注嵌入式核心控制系统研发、设计和生产的高新技术企业&#xff0c;是国内最早专业从事嵌入式技术的企业之一。 经过十几年的发展与积累&#xff0c;公司拥有业内一流的软硬件研发团队&#xff0c;在北京…

SuperMap GIS基础产品FAQ集锦(20240603)

一、SuperMap iDesktopX 问题1&#xff1a;请教一下&#xff0c;桌面把火星坐标系的数据投影转换为4326坐标系数据如何才能没有偏移呢&#xff1f; 11.1.1 【解决办法】可以使用iDesktopX提供的“电子地图坐标转换”插件实现对火星坐标系数据的纠偏。 问题2&#xff1a;请教…

麦肯锡:ChatGPT等生成式AI应用激增,大中华区增长最快

全球顶级咨询公司麦肯锡&#xff08;McKinsey & Company&#xff09;在官网发布了《he state of AI in early 2024:Gen AI adoption spikes and starts to generate value》&#xff0c;一份关于生成式AI应用的调查报告。 麦肯锡对多个国家/地区的1,363位管理者进行了调查…

前端表单校验完成之后,点击确认功能无反应FormInstance, FormRules

**产生原因&#xff1a;可能是在el-form 中添加的ref 前面加了“&#xff1a;”&#xff0c;也可能是ref中的值写错了** FormInstance, FormRules