1.AOP

是面向切面编程就是面向特定方法编程

AOP的优势主要体现在以下四个方面:

减少重复代码:不需要在业务方法中定义大量的重复性的代码,只需要将重复性的代码抽取到AOP程序中即可。

代码无侵入:在基于AOP实现这些业务功能时,对原有的业务代码是没有任何侵入的,不需要修改任何的业务代码。

提高开发效率

维护方便

2.AOP基础

入门案例:

需求:统计部门管理各个业务层方法执行耗时

原始方式代码示例:

需要执行业务层的每一个方法,获取方法运行的开始时间; 然后运行原始的方法逻辑; 最后在每一个方法运行结束时,获取方法运行结束时间,计算执行耗时。

SpringAOP实现:

导入AOP依赖:

编写AOP程序:针对于特定方法根据业务需要进行编程

重新启动SpringBoot服务,打开浏览器访问部门管理的功能进行测试

AOP常见的应用场景:

记录系统的操作日志;权限控制;事务管理

2.AOP核心概念

连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)

通知:Advice,指那些重复的逻辑,也就是共性功能(最终体现为一个方法)

切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用

切面:Aspect,描述通知与切入点的对应关系(通知+切入点)

目标对象:Target,通知所应用的对象

所定义的通知是如何与目标对象结合在一起:

底层是基于动态代理技术来实现的,在程序运行的时候,会自动的基于动态代理技术为目标对象生成一个对应的代理对象。在代理对象当中就会对目标对象当中的原始方法进行功能的增强。

动态代理:

3.AOP进阶

通知类型:

根据通知方法执行时机的不同,将通知类型分为以下常见的五类:

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

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

@After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行@AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行@AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行

通过代码加深理解:

无报错情况下,@AfterThrowing标识的通知方法不会执行

报错情况下,@AfterReturning标识的通知方法不会执行,@AfterThrowing标识的通知方法执行了

@Around环绕通知中原始方法调用时有异常,通知中的环绕后的代码逻辑也不会在执行了 

解决切入点表达式重复的问题:抽取,用注解@PointCut

@PointCut:将公共的切入点表达式抽取出来,需要用到时引用该切入点表达式即可

注:当切入点方法使用private修饰时,仅能在当前切面类中引用该表达式;public:在其他外部的切面类中也可以引用该表达式

通知顺序:

定义两种类型的通知进行测试,一种是前置通知@Before,一种后置通知@After

定义多个切面类:

启动程序

执行顺序:在不同切面类中,默认按照切面类的类名字母排序

目标方法前的通知方法:字母排名靠前的先执行;目标方法后的通知方法:字母排名靠前的后执行

想控制执行顺序方式:修改切面类的类名(繁琐,不推荐);使用Spring提供的@Order注解

4.切入点表达式

作用:主要用来决定项目中的哪些方法需要加入通知

常见形式:

execution(……):根据方法的签名来匹配

@annotation(……):根据注解匹配

execution表达式:

主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配

语法:

execution(访问修饰符?返回值包名.类名.?方法名(方法参数)throws异常?)

其中带?的表示可以省略的部分:

访问修饰符:可省略(比如:public、protected)

包名.类名:可省略

throws异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

可以使用通配符描述切入点:

.*:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分

..:多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数

切入点表达式的语法规则:

@annotation表达式:

用于匹配标识有特定注解的方法。

实现步骤:

编写自定义注解

在业务类要做为连接点的方法上添加自定义注解

切面类

execution切入点表达式与@annotation切入点表达式的应用场景 :

如果execution切入点表达式方便描述指定的方法,就使用execution表达式

否则,就使用@annotation切入点表达式

5.AOP案例

需求:将案例(Tlias智能学习辅助系统)中增、删、改相关接口的操作日志记录到数据库表中

操作日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时参数、返回值、方法执行时长

步骤:

准备工作:(可以私信找我要)

    引入AOP的起步依赖

    导入资料中准备好的数据库表结构,并引入对应的实体类

编码实现:

    自定义注解@LogOperation

    定义切面类,完成记录操作日志的逻辑

在需要记录的日志的Controller层的方法上,加上注解

重启服务

6.连接点

对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型

对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型

7.获取当前登录员工

员工登录成功后,哪里存储的有当前登录员工的信息:给客户端浏览器下发的jwt令牌中

如何从JWT令牌中获取当前登录用户的信息:获取请求头中传递的jwt令牌,并解析

TokenFilter 中已经解析了令牌的信息,如何传递给AOP程序、Controller、Service:ThreadLocal

ThreadLocal:

并不是一个Thread,而是Thread的局部变量

ThreadLocal为每个线程提供一份单独的存储空间,具有线程隔离的效果,不同的线程之间不会相互干扰

ThreadLocal常用方法:

public void set(T value)设置当前线程的线程局部变量的值

public T get()返回当前线程所对应的线程局部变量的值

public void remove()移除当前线程的线程局部变量

记录当前登录员工步骤:

定义ThreadLocal操作的工具类,用于操作当前登录员工ID

TokenFilter中,解析完当前登录员工ID,将其存入ThreadLocal(用完之后需将其删除)

在AOP程序中,从ThreadLocal中获取当前登录员工的ID

hreadLocal的应用场景:

在同一个线程/同一个请求中,进行数据共享

更多推荐