1 .Spring AOP 简介
1.1 AOP 概述
1.1.1 AOP 是什么?
AOP(Aspect Orient Programming) 是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程(OOP)的一种补充和完善.它以通过预编译方式和运行期动态代理方式,实现在不修改代码的情况下给程序动态统一添加额外功能的一种技术,如下图
实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面的运行期代理方式,理解**为一个动态过程,**可以在对象运行动态植入一些扩展功能或控制对象执行.
1.1.2 AOP 应用场景分析?
实际项目中通常会将系统分为两大部分,一部分是核心业务,一部分是非核心业务.在编程实现时我们首先要完成的是核心业务的实现,非核心业务一般是通过特定方式切入到系统中,这种特定方式一般就是借助AOP进行实现.
AOP就是要基于OCP(开闭原则),在不改变原有系统核心业务代码的基础上动态添加一些扩展功能并可以"控制"对象的执行.例如AOP应用于项目中的日志处理,事务处理,权限处理,缓存处理等.
1.1.3 AOP 应用原理
Spring AOP底层基于代理机制实现功能扩展:
- 假如目标对象(被代理对象)实现接口,则底层可以采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口).
- 假如目标对象(被代理对象)没有实现接口,则底层可以采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型).
Spring AOP 原理分析图:
说明:Spring boot2.x 中AOP现在默认使用CGLB代理,假如需要使用JDK动态代理可以在配置文件(application.properties)中进行如下配置:
Spring.aop.proxy-target-class=false
1.2 AOP 相关术语分析
公式: AOP = 切入点表达式+通知方法
- 切面(aspect) : 横切面对象, 一般为一个具体对象(可以借助@Aspect声明).
- 通知(Advice) : 在切面的某个特定连接点上执行的动作(扩展功能).
- 连接点(joinpoint) : 程序执行过程中某个特定的点,一般指被拦截到的方法.
- 切入点(pointcut): 对多个连接点(joinpoint)一种定义,一般可以理解为多个连接点的集合.
连接点与切入点定义如图:
1.2.1 切入点表达式
1.bean(bean的ID) 按照指定的bean名称拦截用户的请求,之后执行通知方法,只能匹配单个bean对象.
bean表达式一般应用于类级别,实现粗粒度的切入点定义.
- bean("userServiceImpl")指定一个userServiceImpl类中所有方法.
- bean("*ServiceImpl") 指定所有后缀为ServiceImpl的类中所有方法.
说明:bean表达式内部的对象是由spring容器管理的一个bean对象,表达式内容的名字应该是spring容器中某个bean的name(类名开头字母小写).
2.within(包名.类名) 可以按照类通配的方式去拦截用户的请求,控制粒度较粗.
within表达式应用于类级别,实现粗粒度的切入点表达式定义.
- within("com.aop.service.UserServiceImpl")指定当前包中这个类内部的所有方法.
- within("com.aop.service.*") 指定当前目录下的所有类的所有方法.
- within("com.aop.service..*") 指定当前目标已经子目录中类的所有方法.
within表达式应用场景分析:
1)对所有业务bean都要进行功能增强,但是bean名字又没有规则.
2)按业务模块(不同包下的业务)对bean对象进行业务功能增强.
3.execution (返回值类型 包名.类名.方法名(参数列表)) 方法参数级别,控制粒度较细.
- execution(void com.aop.service.UserServiceImpl.addUser()) 匹配addUser方法.
- execution(void com.aop.service.PersonServiceImpl.addUser(String)) 方法参数必须为String的addUser方法.
- execution (* com.aop.service..*.*(..)) 万能配置
4.@annotation (包名.注解名称) 按照注解的方式去拦截用户请求,应用于方法级别,实现细粒度的切入点表达式定义.
- @annotation(com.anno.RequiredLog) 匹配有此注解描述的方法.
- @annotation(com.anno.RequiredCache) 匹配有此注解描述的方法.
其中:RequiredLog为我们自己定义的注解,当我们使用@RequiredLog注解修饰业务层方法时,系统底层会在执行此方法时进行扩展操作.
1.2.2 通知方法
在基于Spring AOP编程的过程只中,基于AspectJ 框架标准,spring中定义了五种类型的通知(通知描述的是一种扩展业务)
1.前置通知(@Before): 主要在目标方法执行之前执行
2.后置通知(@After) : 在目标方法执行之后执行.
3. 异常通知(@AfterThrowing) : 在目标方法执行过程中出现异常执行,所以当做一些异常监控时可在此方法中进行代码实现.
4. 最终正常通知(@AfterReturning) : 在目标方法正常执行时执行
上述的通知方法,无法控制目标方法是否执行,所以一般"只做记录不做改变".
5.环绕通知(@Round) : 一般采用环绕通知实现对业务的控制.
1.2.3 通知执行顺序.
假如这些通知全部写到一个切面对象中,其执行顺序及过程:
1.3 切面优先级设置实现
切面的优先级需要借助@Order注解进行描述,数字越小优先级越高,默认优先级比较低.
- 定义日志切面并指定优先级。
@Order(1)
@Aspect
@Component
public class SysLogAspect {
…
}
- 定义缓存切面并指定优先级:
@Order(2)
@Aspect
@C