Spring AOP

目录

一、什么是AOP

二、AOP例子

1、添加配置

[2、编写AOP程序 计算Controller中每个方法的运行时间](#2、编写AOP程序 计算Controller中每个方法的运行时间)

三、AOP详解

1、@Aspect注解

2、切点表达式

(1)第一种方式--方法签名匹配

(2)第二种方式--自定义注解

[3、连接点---Join Point](#3、连接点---Join Point)

4、通知---Advice

5、通知类型

(1)@Around

(2)@Before

(3)@After

(4)@AfterReturning

(5)@AfterThrowing

6、@PointCut

7、@Order

8、切面---Aspect

[四、Spring AOP原理](#四、Spring AOP原理)

1、代理模式的概念

2、代理模式的主要角色

3、静态代理

4、动态代理

(1)JDK实现动态代理

(2)CGLIB动态代理

[5、Spring AOP动态代理实现方式](#5、Spring AOP动态代理实现方式)

一、什么是AOP

AOP是一种思想,是某一类事物的统一处理。它的实现方式有很多,比如:Spring AOP,AspectJ,CGLIB等。之前的拦截器验证是否登陆、统一功能进行结果统一、统一异常处理等都是AOP的实现。但这些不是AOP的全部,这些是针对url进行拦截的。AOP的维度更加细致,也可以针对包、类、方法名、参数名等进行拦截处理。

二、AOP例子

上一篇博客实现了图书管理系统,现在希望能对部分运行时间长的功能进行时间优化,所以我们需找到哪一部分运行时间最长。针对所有方法,方法运行前计算当前时间,方法运行完成后计算当前时间,两时间之差就是运行时间。

针对以上需求实现,我们可以使用Spring AOP实现

1、添加配置
2、编写AOP程序 计算Controller中每个方法的运行时间

三、AOP详解

1、@Aspect注解

虽然注解是@Aspect,但是实现AOP思想是通过Spring AOP,该注解是为了标识这是一个切面类。

2、切点表达式

切点表达式来描述切点,常见两种方式:

(1)第一种方式--方法签名匹配

eg:execution(* com.example.book.Controller.*.*(..))

execution()是最常见的切点表达式,用来匹配方法,语法为:

java 复制代码
execution(<访问修饰符> <返回类型> <包名.类名.方法(方法参数)> <异常>)

其中:访问修饰符和异常可以省略。

①*使用说明

*可以匹配任意字符,只匹配一个元素(返回类型,包,类名,方法名,方法参数)

返回类型使用*:返回任意类型;

包名使用*:表示任意包;

类名使用*:表示任意类;

方法名使用*:表示任意方法;

参数使用*:表示一个任意类型的参数;

②..使用说明

..可以匹配多个连续的任意字符,可以匹配任意层次的包,或任意类型,任意个数的参数。

包名使用.. :表示此包以及此包下的所有子包;

参数使用.. :表示方法的参数可以是任意多个,任意类型的。

eg:

表示匹配访问修饰符为public、返回类型为String、包为com.example.demo.controller、类为TestController、方法名为t1、无参数的方法。

(2)第二种方式--自定义注解

上述方法签名匹配不能细致的描述不能类的不同方法,只能统一的描述某个包下的某个类下的某个方法,此时可以使用自定义注解。

①先定义一个注解

@Target标识了自定义注解所修饰的对象范围,即该注解可以用在什么地方。

ElementType.TYPE:用在类、接口或enum;

ElementType.METHOD:用在方法;

ElementType.PARAMETER:用在参数;

ElementType.TYPE_USE:用在任何地方;

@Retention指注解被保留的时间长短,表示注解的生命周期

RetentionPolicy.SOURCE:表示注解仅存在于源码中,编译成字节码后会被丢弃,在运行时无法获取到该注解的信息;

RetentionPolicy.CLASS:表示注解存在于源代码和字节码中,只能通过反射获取到该注解的信息,实际运行时无法获取到;

RetentionPolicy.RUNTIME:表示注解存在于源代码、字节码和运行时。

②利用该注解描述切点

@annotation注解+自定义注解的路径

③在连接点的方法上添加自定义注解

访问该方法,运行结果:

3、连接点---Join Point

指满足切点表达式下的方法都为连接点,也就是可以被AOP控制的方法,通过连接点名称.proceed(),可以调用指定路径下的方法。

4、通知---Advice

具体要做的工作,把重复的工作抽取出来单独定义,这部分代码就是通知。例如:以上代码中,计算每个方法消耗的时间,对应代码就是通知。

5、通知类型

通知类型指通知的执行时机,有以下几种:

(1)@Around

@Around:环绕通知,此注解标注的通知方法在目标方法前后都会执行。

(2)@Before

@Before:前置通知,此注解标注的通知方法在目标方法前会被执行。

(3)@After

@After:后置通知,此注解标注的通知方法在目标方法后会被执行。

(4)@AfterReturning

@AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行。

目标方法没有异常:

目标方法有异常:

(5)@AfterThrowing

@AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行。

目标方法没有异常:

目标方法有异常:

6、@PointCut

在以上不同的通知类型中,我们发现一个问题,针对切点表达式:execution(* com.example.b-ook.TestController.*.*(..))我们一直在重复写,可以通过其他方式简化吗???

@PointCut注解可以把公共的切点表达式提取出来,需要使用时引入即可。

上述代码就可以修改为:

注:若其他切面类需要使用该公共切点时,需要将公共切点的修饰限定符改为public,且该类在引用公共切点时,需要:全限定类名.方法名();

全限定类名:该类路径+该类类名。

7、@Order

当一个项目中存在多个切面类时,此时AOP的执行顺序是怎么样的呢???

运行结果:

结论:和类名名称有关,针对Before的通知类型,类名的字母顺序靠前的先执行。

可以通过@Order指定执行顺序,方式:@Order(数字)

运行结果:

8、切面---Aspect

切面=切点+通知

通过切面,我们可以知道哪些方法在执行哪些操作。

四、Spring AOP原理

Spring AOP是基于动态代理实现的AOP。

1、代理模式的概念

有些目标方法在进行调用时,不是直接对目标方法进行调用,而是通过代理类(为其他类提供一种代理以控制对目标方法的访问)间接调用,代理对象可以在客户端和目标对象之间起到中介的作用。

2、代理模式的主要角色

eg:中介推销房东的房子:Subject是房东租房这件事,也是中介需要做的事;RealSubject是房东;Proxy是中介。

3、静态代理

在程序运行前,代理类的.class文件就已经存在。例如:在出租房子前,中介就已经做好出租房子的相关工作,就等租户来租房子了。

Subject:

RealSubject:

Proxy:

调用代理类的rentHouse方法:

运行结果:

上述代理实现方式就是静态代理,对目标对象的每个方法的增强都是手动完成的。此时若增加一个卖房子的功能,则HouseSubject、RealHouseSubject、ProxyHouseSubject都需要修改。

4、动态代理

相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标对象单独创建一个代理对象,程序运行时,由JVM实现,根据需要动态生成。

实现动态代理的两种方式:JDK和CGLIB

(1)JDK实现动态代理

①实现步骤

定义一个接口及其实现类(静态代理中的HouseSubject和RealHouseSubject);

自定义InvocationHandler,并重写invoke方法,在invoke方法中调用目标方法并自定义一些处理逻辑;

通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法创建代理对象。

②代码实现

运行结果:

注:JDK动态代理有一个致命的问题是其只能代理实现了接口的类。

(2)CGLIB动态代理

可以使用CGLIB动态代理非接口的类。

①实现步骤

定义一个被代理类;

自定义MethodInterceptor,并重写intercept方法,该方法用于增强目标方法,和JDK动态代理中的invoke方法类似;

通过Enhancer类的create()创建代理类。

②代码实现

先引入依赖:

运行结果:

5、Spring AOP动态代理实现方式

动态代理有两种实现方式:JDK和CGLIB,Spring AOP是使用哪种方式实现的???

在考虑目标对象所在类是否实现接口的前提下,还需考虑一些配置。

SpringBoot 2.X开始,默认使用CGLIB代理,可以通过配置项spring.qop.proxy-target-class=false来进行修改,对于实现接口的目标类,默认使用JDK代理。

相关推荐
骑鱼过海的猫123几秒前
【java】java通过s3访问ceph报错
java·ceph·iphone
杨充7 分钟前
13.观察者模式设计思想
java·redis·观察者模式
Lizhihao_9 分钟前
JAVA-队列
java·开发语言
喵叔哟18 分钟前
重构代码之移动字段
java·数据库·重构
喵叔哟18 分钟前
重构代码之取消临时字段
java·前端·重构
fa_lsyk21 分钟前
maven环境搭建
java·maven
Daniel 大东40 分钟前
idea 解决缓存损坏问题
java·缓存·intellij-idea
wind瑞1 小时前
IntelliJ IDEA插件开发-代码补全插件入门开发
java·ide·intellij-idea
HappyAcmen1 小时前
IDEA部署AI代写插件
java·人工智能·intellij-idea
马剑威(威哥爱编程)1 小时前
读写锁分离设计模式详解
java·设计模式·java-ee