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,一经查实,立即删除!

相关文章

D365 Project Operations

目录 D365包含哪些模块&#xff1f; D365 Project Operations是什么&#xff1f; D365 Project Operations优势 D365 Project Operations部署方案 总结 D365包含哪些模块&#xff1f; 微软的D365是一套功能强大的企业管理解决方案&#xff0c;其中包含了多种模块&#xff…

结账和反结账

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

深入React Hoooks:从基础到自定义 Hooks

使用 useContext useContext 是另一个常用的 Hook&#xff0c;它可让我们在函数组件中轻松访问 React 的 context。如果你的应用程序依赖于一些全局状态&#xff0c;或者你希望避免将 props 一层一层地传递到子组件&#xff0c;context 很有用。你可以在父组件设置一个值&…

C语言 链表经典OJ题

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

mysql5.7血泪史

因为工作需要&#xff0c;需要将mysql8.0.33版本切换为mysql5.7系列。 在这个过程当中&#xff0c;首先我将mysql8卸载干净。 mysql彻底卸载干净的5个步骤&#xff0c;超多图超详细保姆级教程最新教程新手小白轻松上手_卸载mysql-CSDN博客 然后发现mysql5.7在官网上下载很…

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…

CF Round 368 (Div. 2) C. Pythagorean Triples【数学 构造】

Codeforces Round 368 (Div. 2) C. Pythagorean Triples 题意&#xff1a; 给你一个整数n&#xff0c;让你构造另两个整数满足三角形的勾股定理。 思路&#xff1a; 首先&#xff0c;n < 2无解 由题意得&#xff1a;a^2 b^2 c^2&#xff1b;不妨设n a&#xff0c;则…

【AIGC半月报】AIGC大模型启元:2024.06(上)

AIGC大模型启元&#xff1a;2024.06&#xff08;上&#xff09; (1) ChatTTS&#xff08;语音合成项目&#xff09; (1) ChatTTS&#xff08;语音合成项目&#xff09; 2024.06.01 ChatTTS 文本转语音项目爆火出圈&#xff0c;引来大家极大的关注。短短三天时间&#xff0c;在…

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;更包括了质量管理体系的设计和优化、客户关系的管理、以及在整…

Flutter 中的 ToggleButtonsTheme 小部件:全面指南

Flutter 中的 ToggleButtonsTheme 小部件&#xff1a;全面指南 Flutter&#xff0c;作为由 Google 开发的跨平台 UI 框架&#xff0c;为开发者提供了丰富的组件来构建现代化的应用程序。ToggleButtons 是 Material Design 组件库中的一个组件&#xff0c;它允许用户从一组选项…

AI的绘画工具有哪些?

AI绘画工具利用人工智能技术帮助用户生成艺术作品或进行图像编辑&#xff0c;以下是一些AI绘画工具的列表&#xff1a; 1. 腾讯智影AI绘画&#xff1a;提供在线智能绘画工具&#xff0c;用户可以通过输入关键词和选择不同的效果预设来生成艺术作品。 2. Stylar&#xff1a;一…

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

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

遍历数组1

package demo; import java.util.ArrayList; public class Arrilist { public static void main(String[] args) { ArrayList<String>listnew ArrayList<>(); list.add("汤神"); list.add("yyx"); list.add("hong go…

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;还是…

开放式虚拟化格式1.0和2.0有什么区别

开放式虚拟化格式&#xff08;Open Virtualization Format&#xff0c;简称OVF&#xff09;是一种用于描述、打包、和分发虚拟机的标准格式。OVF 1.0和OVF 2.0是这个标准的两个不同版本。以下是它们之间的一些主要区别&#xff1a; 1. **扩展性**&#xff1a; - OVF 1.0主要…