当前位置: 首页 > backend >正文

Spring系列四:AOP切面编程 第二部分

Spring AOP

  • 💗Spring AOP
  • 🍝基本介绍
  • 🍝AOP编程 快速入门
  • 🍝注意事项和细节
  • 🍝课后作业
  • 🐋AOP-切入表达式
    • 💧通配符
    • 💧注意事项和细节

💗Spring AOP

🍝基本介绍

●什么是AOP
AOP的全称(aspect oriented programming), 面向切面编程.

在这里插入图片描述
●AOP实现方式
1.基于动态代理的方式 [内置aop实现]
2.使用框架aspectj来实现

🍝AOP编程 快速入门

●说明
1.需要引入核心的aspect包
2.在切面类中声明通知方法
1)前置通知: @Before
2)返回通知: @AfterReturning
3)异常通知: @AfterThrowing
4)后置通知: @After 最终通知
5)环绕通知: @Around

●需求说明
我们使用aop编程的方式, 来实现手写的动态代理案例效果, 以上一个案例为例进行讲解.

1.导入AOP编程需要的包
在这里插入图片描述

2.新建com.zzw.spring.aop.aspectj.SmartAnimalAble

public interface SmartAnimalAble {//求和float getSum(float i, float j);//求差float getSub(float i, float j);
}

3.新建com.zzw.spring.aop.aspectj.SmartDog

易错点: 不要引入别的包下的SmartAnimalAble, 要引入同包下的SmartAnimalAble

@Component //使用@Component 当spring容器启动时, 将 SmartDog 注入到容器
public class SmartDog implements SmartAnimalAble {@Overridepublic float getSum(float i, float j) {float result = i + j;System.out.println("方法内部打印result = " + result);return result;}@Overridepublic float getSub(float i, float j) {float result = i - j;System.out.println("方法内部打印result = " + result);return result;}
}

4.新建com.zzw.spring.aop.aspectj.SmartAnimalAspect

切面类, 类似于我们前面自己写的MyProxyProvider, 但是功能强大很多

@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定)]
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//希望将showBeginLog方法切入到SmartDog-getSum前执行-前置通知/*** 解读* 1. @Before 表示是前置通知, 即在我们的目标对象执行方法前执行* 2. value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))"* 指定切入到哪个类的哪个方法 形式是: 访问修饰符 返回类型 全类名.方法名(形参列表)* 3. showBeginLog方法可以理解成就是一个切入方法, 这个方法名是可以由程序员指定的 比如:showBeginLog* 4. JoinPoint joinPoint 在底层执行时, 由AspectJ切面编程框架, 会给该切入方法传入 joinPoint对象* , 通过该方法, 程序员可以获取到 相关信息* @param joinPoint*/@Before(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")public void showBeginLog(JoinPoint joinPoint) {//通过连接点对象joinPoint, 可以获取方法签名Signature signature = joinPoint.getSignature();System.out.println("切面类showBeginLog()-方法执行前-日志-方法名-" + signature.getName() + "-参数 "+ Arrays.asList(joinPoint.getArgs()));}//返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方@AfterReturning(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")public void showSuccessEndLog(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();System.out.println("切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName());}//异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}@AfterThrowing(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")public void showExceptionLog(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();System.out.println("切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName());}//最终通知: 即把showFinallyEndLog方法切入到目标方法执行后, 不管是否发生异常都要执行, finally{}@After(value = "execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))")public void showFinallyEndLog(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();System.out.println("切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());}
}

5.新建src/beans08.xml

具体来说,当你通过 getBean() 方法获取 SmartDog 类时,返回的对象实际上是 Spring 自动生成的代理对象,而不是 SmartDog 类的原始对象。这个代理对象会在目标方法执行前后,以及发生异常时,执行切面类中定义的各种通知方法,从而实现了 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"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"><context:component-scan base-package="com.zzw.spring.aop.aspectj"/><!--开启基于注解的AOP功能--><aop:aspectj-autoproxy/>
</beans>

6.测试com.zzw.spring.aop.aspectj.AopAspectjTest

public class AopAspectjTest {@Testpublic void smartDogTestByProxy() {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans08.xml");//这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象SmartAnimalAble smartAnimalAble =ioc.getBean(SmartAnimalAble.class);smartAnimalAble.getSum(1, 2);//System.out.println("smartAnimalAble运行类型是 " + smartAnimalAble.getClass());//class com.sun.proxy.$Proxy16}
}

🍝注意事项和细节

1.关于切面类方法命名可以自己规范一下, 比如showBeginLog(), showSuccessEndLog(), showExceptionLog(), showFinallyEndLog()

2.切入表达式的更多配置, 比如使用模糊配置
@Before(value=“execution(* com.zzw.aop.proxy.SmartDog.*(…))”)

第一个\*表示: 任意修饰符和返回类型
第二个\*表示: 任意方法名
..表示: 任意形参列表


3.表示所有访问权限, 所有包下的所有类的所有方法, 都会被执行该前置通知方法
@Before(value=“execution(* *.*(…))”)

4.当spring容器开启了 基于注解的AOP功能 <aop:aspectj-autoproxy/>, 我们获取注入的对象, 需要以接口的类型来获取, 因为你注入的对象.getClass() 已经是代理类型了

5.当spring容器开启了 基于注解的AOP功能 <aop:aspectj-autoproxy/>, 我们获取注入的对象, 也可以通过id来获取, 但是也要转成接口类型.

//这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象
SmartAnimalAble smartAnimalAble = ioc.getBean(SmartAnimalAble.class);//SmartAnimalAble smartAnimalAble = (SmartAnimalAble) ioc.getBean("smartDog");

在这里插入图片描述

🍝课后作业

●作业要求
1.有接口 UsbInterface (方法 work)
2.实现子类 PhoneCamera 实现 UsbInterface
3.请在SmartAnimalAspect 切面类, 写一个方法(可输出日志信息作为前置通知, 在PhoneCamera对象执行work方法前调用
4.其它如返回通知, 异常通知, 后置通知, 也可以加入.


●代码实现
1.新建com.zzw.spring.aop.homework.UsbInterface接口

public interface UsbInterface {void work(String name);
}

2.新建com.zzw.spring.aop.homework.Phone

@Component //将Phone当作一个组件注入到容器中
public class Phone implements UsbInterface {@Overridepublic void work(String name) {System.out.println(name + " 手机正在工作中....");}
}

3.新建com.zzw.spring.aop.homework.Camera

@Component //将Camera对象注入到Spring容器
public class Camera implements UsbInterface {@Overridepublic void work(String name) {System.out.println(name + " 相机正在工作中....");}
}

4.切面类com.zzw.spring.aop.homework.Phone.SmartAnimalAspect

@Aspect
@Component
public class SmartAnimalAspect {//希望将showBeginLog切入到Phone/Camera-work() 前执行//前置通知//切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效//比如下面我们是对UsbInterface切入, 那么对实现类 Phone/Camera 都生效//@Before(value = "execution(public void *.work(String))")@Before(value = "execution(public void com.zzw.spring.aop.homework.UsbInterface.work(String))")public void showBeginLog(JoinPoint joinPoint) {//通过连接点对象, 可以获取方法签名Signature signature = joinPoint.getSignature();System.out.println("切面类showBeginLog()-方法执行前-日志-方法名-" + signature.getName() + "-参数"+ Arrays.asList(joinPoint.getArgs()));}//返回通知@AfterReturning(value = "execution(public void UsbInterface.work(String))")//@AfterReturning(value = "execution(public void com.zzw.spring.aop.homework.*.work(String))")public void showSuccessEndLog(JoinPoint joinPoint) {//通过连接点对象, 可以获取方法名Signature signature = joinPoint.getSignature();System.out.println("切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName());}//异常通知//@AfterThrowing(value = "execution(public void *.work(String))")@AfterThrowing(value = "execution(public void UsbInterface.work(String))")public void showExceptionLog(JoinPoint joinPoint) {//通过连接点对象, 可以获取方法名Signature signature = joinPoint.getSignature();System.out.println("切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName());}//后置通知//@After(value = "execution(public void *.work(String))")@After(value = "execution(public void UsbInterface.work(String))")public void showFinallyEndLog(JoinPoint joinPoint) {//通过连接点对象, 可以获取方法名Signature signature = joinPoint.getSignature();System.out.println("切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());}
}

5.src/beans09.xml

<?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="com.zzw.spring.aop.homework"/><!--开启基于注解的AOP功能--><aop:aspectj-autoproxy/>
</beans>

6.测试com.zzw.spring.aop.homework.AspAspectjTest

public class AspAspectjTest {public static void main(String[] args) {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans09.xml");UsbInterface phone = (UsbInterface) ioc.getBean("phone");phone.work("华为");System.out.println("===================================");UsbInterface camera = (UsbInterface) ioc.getBean("camera");camera.work("索尼");//System.out.println("phone的运行类型是" + phone.getClass());}
}

🐋AOP-切入表达式

💧通配符

切入点表达式

1.作用
通过表达式的方式定位一个或多个具体的连接点

2.语法细节
    ①切入点表达式的语法格式
    execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名] (参数列表))
    ②举例说明

表达式execution(* com.zzw.spring.aop.aspectj.SmartDog.*(..))
含义如果SmartAnimalAble是接口, 则表示 接口/类 中声明的所有方法.
第一个 * 代表任意修饰符及任意返回值
第二个 * 表示任意方法
.. 匹配任意数量, 任意类型的参数 (规定.的数量是2个)
若目标类/接口与该切面类在同一个包中可以省略包名
表达式execution(public * SmartDog.*(..))
含义SmartDog 接口/类 中的所有公有方法
表达式execution(public double SmartDog.*(..))
含义SmartDog 接口/类 中返回double类型数值的方法
表达式execution(public double SmartDog.*(double, ..))
含义第一个参数为double类型的方法.
..匹配任意数量, 任意类型的参数
表达式execution(public double SmartDog.*(double, double))
含义参数类型为double, double类型的方法

:匹配 com.zzw.bean 包及其子包下的所有类。

表达式execution(public double com.zzw.bean..set*( ))
含义匹配 com.zzw.bean 包及其子包下的所有类;set*:匹配所有以 set 开头的方法名

    ③在AspectJ中, 切入点表达式可以通过&&, ||, ! 等操作符结合起来

表达式execution(* *.add(int, ..)) || execution(* *.sub(int, ..))
含义任意类中第一个参数为int类型的add方法或sub方法

💧注意事项和细节

1.切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效
2.切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效
3.切入表达式也可以对没有实现接口的类, 进行切入

●代码实现
1.新建com.zzw.spring.aop.aspectj.Car

@Component //把Car视为一个组件[对象], 注入到Spring容器
public class Car {public void run() {System.out.println("小汽车 run...");}
}

2.com.zzw.spring.aop.aspectj.SmartAnimalAspect
指向类的方法

//切面类
@Aspect //表示是一个切面类
@Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//给Car配置一个前置通知@Before(value = "execution(public void Car.run())")public void ok1(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();System.out.println("切面类的ok1()-执行的目标方法-" + signature.getName());}
}

3.测试com.zzw.spring.aop.homework.AspAspectjTest

public class AopAspectjTest {@Testpublic void test() {ApplicationContext ioc =new ClassPathXmlApplicationContext("beans08.xml");Car car = ioc.getBean(Car.class);//说明: car对象仍然是代理对象System.out.println("car的运行类型=" + car.getClass());//car的运行类型=class com.zzw.spring.aop.aspectj.Car$$EnhancerBySpringCGLIB$$5e9a8b7acar.run();}
}

4.补充: 动态代理jdk的Proxy和Spring的CGlib

具体来说,Spring AOP 使用 JDK 动态代理和 CGLIB 两种方式来生成代理对象,其中选择使用哪种方式取决于被代理的类是否实现了接口。

如果被代理的类实现了接口,Spring AOP 会使用 JDK 动态代理。JDK 动态代理要求被代理的类必须实现至少一个接口,代理对象会实现这个接口并在运行时生成代理实例。

如果被代理的类没有实现接口,Spring AOP 会使用 CGLIB 来生成代理对象。CGLIB 可以代理没有实现接口的类,它通过继承被代理类来生成代理对象。

http://www.xdnf.cn/news/2813.html

相关文章:

  • EasyGBS国标GB28181设备管理软件打造园区安防高效解决方案
  • 【C++】类和对象(4)
  • 开源CMS系统的SEO优化功能主要依赖哪些插件?
  • java 和 C#操作数据库对比
  • Web技术与Apache网站部署
  • 知识付费平台:野兔YeTu
  • 静态库与动态库简介
  • CAD2008无法完成激活注册问题
  • LINE FRIENDS 正式与 Walrus 合作,全新 AI 驱动的游戏即将上线
  • maven私服配置
  • 如何创建并使用极狐GitLab 受保护分支?
  • 明远智睿SSD2351开发板:开启工业控制新征程
  • Linux[开发工具]
  • 短视频矩阵系统贴牌批量剪辑功能开发,支持OEM
  • 马井堂-大语言模型对教学的应用分析
  • C++面试常青客:LRUCache最近最少使用算法
  • 【超详细讲解】什么是序列化和反序列化?
  • Elastic Platform 8.18 和 9.0:ES|QL Lookup Joins 功能现已推出,Lucene 10!
  • STM32 USB配置详解
  • 使用多线程快速向Excel中快速插入一万条数据案例
  • UDP协议详解+代码演示
  • 品融天猫代运营服务内容详解:专业化体系驱动品牌增长
  • Spring Boot 3与JDK 8环境下的单元测试实践指南
  • QT中的多线程
  • 驱动开发硬核特训 │ Day 23(下篇): i.MX8MP LCDIFv3 驱动中的 Regulator 系统全解
  • KML文件转shp并保留关键字段
  • 擦除整片flash后,程序下载到单片机,单片机不运行
  • Android Kotlin ViewModel 错误处理:最佳 Toast 提示方案详解
  • java 使用 POI 为 word 文档自动生成书签
  • 热扩散测试要求不起火、不爆炸,电动汽车电池新国标GB38031-2025将于2026年7月实施