一、AOP含义
- AOP:通过预编译方式和动态代理,可实现在不修改源代码的情况下,给程序动态同意添加的一种技术
1、模式演变
每个类每个函数都会有日志记录,产生大量重复代码;——后来形成工具类方式,每个类对象调用工具日志类,
OOP:面向对象编程的方式
- OOP的缺点
1、形成依赖关系
2、每个类都需要调用,如Log方法
3、如果发生修改,每个地方都需要修改
2、AOP的应用场景
【切面类一般不是核心业务逻辑】
- 日志记录
- 权限验证:操作权限,登录,角色等
- 事务处理
- 效率检查:哪个方法执行效率最低(大并发情况下)
- 异常处理:对异常统一处理,或者监控实时处理
- 缓存处理:RedisCache
- 数据持久化:调用方法,先存储一份
- 内容分发:调用方法请求的时候,记录调用逻辑、方法、内容、次数等
3、AOP的主要概念
- aspect:切面,切面由切点和通知组成,即包括横切逻辑的定义也包括链接点的定义
- pointcut:切点,每个类拥有多个连接点,可以理解为连接点的集合
- Joinpoint:连接点,程序执行的某个特定位置,如某个方法调用前后等(每个函数调用写日志的时候,调用方法就是连接点,多个方法的写日志,集合成为切点)
- weaving:织入,将增强添加到目标类的具体连接点过程(过程描述)
- advice:通知(增强),是织入到目标类的具体连接点的过程
- target:目标对象,通知织入的目标类
- aop Proxy:代理对象,即增强后产生的对象
4、advice的类型
- before advice,前置通知,目标方法执行之前执行。无论是否遇到异常都会执行
- After Returning Advice:后置通知,在方法返回后切入,抛出异常则不会切入
- After Throwing Advice:异常通知,在方法抛出异常切入
- After Finally Advice:最终通知,在目标方法后执行,抛出异常则不会执行
- Around Advice:环绕通知,可以控制目标方法的执行(调用
ProceedingJoinPoint.proceed()
),可以在目标方法执行全过程中执行
5、@Aspect
- 定义一个切面类Aspect
@Component
@Aspect
public class ParamLogAspect {}
- 定义一个切点Pointcut
/**
* 切点名称:controller
* 执行环境:执行过程中即:execution(表示 目标地址下所有类的所有方法,不限定参数)
*/
@Pointcut("execution(public * com..*.controller..*.*(..))")
public void controller() {
}
- 定义Advice通知
注解@Before、@After、@AfterReturning、@AfterThrowing、@Around
@Around("@within(org.springframework.web.bind.annotation.RestController) && controller() || @within(org.springframework.stereotype.Controller) && controller()")
public Object observer(ProceedingJoinPoint pjp) throws Throwable {}
ProceedingJoinPoint 只能用在Around中,其他只能用JoinPoint
- ProceedingJoinPoint 继承了JoinPoint,在JoinPoint的基础上暴露出
proceed()
方法; proceed
是AOP代理链执行的方法
import org.aspectj.lang.reflect.SourceLocation;
public interface JoinPoint {
String toString(); //连接点所在位置的相关信息
String toShortString(); //连接点所在位置的简短相关信息
String toLongString(); //连接点所在位置的全部相关信息
Object getThis(); //返回AOP代理对象,也就是com.sun.proxy.$Proxy18
Object getTarget(); //返回目标对象,一般我们都需要它或者(也就是定义方法的接口或类,为什么会是接口呢?
//这主要是在目标对象本身是动态代理的情况下,例如Mapper。所以返回的是定义方法的对象如
//aoptest.daoimpl.GoodDaoImpl或com.b.base.BaseMapper<T, E, PK>)
Object[] getArgs(); //返回被通知方法参数列表
Signature getSignature(); //返回当前连接点签名。其getName()方法返回方法的FQN,如void aoptest.dao.GoodDao.delete()
//或com.b.base.BaseMapper.insert(T)(需要注意的是,很多时候我们定义了子类继承父类的时候,
//我们希望拿到基于子类的FQN,无法直接拿到,要依赖于
//AopUtils.getTargetClass(point.getTarget())获取原始代理对象,下面会详细讲解)
SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置
String getKind(); //连接点类型
StaticPart getStaticPart(); //返回连接点静态部分
}
public interface ProceedingJoinPoint extends JoinPoint {
public Object proceed() throws Throwable;
public Object proceed(Object[] args) throws Throwable;
}