maven依赖
<!--引入SpringBoot的Web模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
AOP的基本概念:
什么是AOP:
- AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。
- 在不改变原有的逻辑的基础上,增加一些额外的功能。代理也是这个功能,读写分离也能用aop来做。
AOP的相关概念:
- 横切关注点:对哪些方法进行拦截,拦截后如何处理,这些关注点成为横切关注点
- Aspect(切面):通常是一个类,里面定义切入点和通知
- JoinPoint(连接点):程序执行过程中明确的点,一般是方法的调用。被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中拦截到的点只是方法,实际上还是可以拦截到字段或者构造器的。
- Advice(通知):AOP在特定切入点上执行的增强处理,有五个通知类型:前置、后置、最终、异常和环绕
- PointCut(切入点):就是需要被增强的地方
- weave(织入):将切面应用到目标对象并导致代理对象创建的过程
AOP使用场景:
- Authentication 权限
- Caching 缓存
- Context passing 内容传递
- Error handling 错误处理
- Lazy loading 懒加载
- Debugging 调试
- logging, tracing, profiling and monitoring 记录跟踪 优化 校准
- Performance optimization 性能优化
- Persistence 持久化
- Resource pooling 资源池
- Synchronization 同步
- Transactions 事务
ProceedingJoinPoint和JoinPoint
- ProceedingJoinPoint的 proceed方法的作用:执行目标方法,或者说是去调用了目标方法,得到目标方法的返回值。
- 环绕通知 ProceedingJoinPoint 执行proceed方法的作用是让目标方法执行,这也是环绕通知和前置、后置通知方法的一个最大区别。
- Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。
- ProceedingJoinpoint和JoinPoint的区别
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;
}
//提供访问连接点的静态部分,如被通知方法签名、连接点类型等:
public interface StaticPart {
Signature getSignature(); //返回当前连接点签名
String getKind(); //连接点类型
int getId(); //唯一标识
String toString(); //连接点所在位置的相关信息
String toShortString(); //连接点所在位置的简短相关信息
String toLongString(); //连接点所在位置的全部相关信息
}
- ProceedingJoinpoint继承JoinPoint,在JoinPoint的基础上暴露出proceed方法。
五种通知方式示例
@Aspect
@Component
public class SysLogAspect {
private static Logger logger = LoggerFactory.getLogger(SysLogAspect.class);
@Before("@annotation(sysLog)")
public void before(JoinPoint joinPoint,SysLog sysLog){
System.out.println("前置通知...");
}
@After("@annotation(sysLog)")
public void after(JoinPoint joinPoint,SysLog sysLog){
System.out.println("after...");
}
@AfterThrowing(value = "@annotation(sysLog)",throwing = "ex")
public void afterThrow(JoinPoint joinPoint,Exception ex,SysLog sysLog){
System.out.println("afterThrowing");
System.out.println("AOP:afterThrow"+ex);
}
//获取目标方法的返回值 result
@AfterReturning(value = "@annotation(sysLog)",returning ="result")
public void afterReturning(JoinPoint joinPoint,Object result,SysLog sysLog){
System.out.println("AOP afterRuturning:"+result);
}
@Around("@annotation(sysLog)")
public Object around(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable {
System.out.println("around before...");
Object result = joinPoint.proceed();
System.out.println("around return:"+result);
System.out.println("around after...");
return result;
}
}
@SysLog("保存配置")
@GetMapping("/test")
public String save(String username){
System.out.println("执行目标方法...");
return username;
}
没有异常的结果:
出现异常的结果:
可以看出执行的顺序是:
around before -> @Before ->目标方法-> @AfterReturning ->@After -> around return ->around after
当我们配上拦截器的时候执行顺序则是:
拦截器preHandle ->around before -> @Before ->目标方法-> @AfterReturning ->@After -> around return ->around after ->postHandle->afterCompletion
五种方式详解:
- 前置通知 @Before:
- 在目标方法之前执行
- 无论目标方法是否会抛异常,都执行
- 后置通知 @After:
- 在目标方法执行之后执行
- 目标方法遇到异常后会执行
- 最终通知 @AfterReturning:
- 在目标方法执行之后执行,在后置通知之前执行
- 当目标方法遇到异常后不会执行
- 能够接收目标方法的返回值,但是需要注意接收参数的类型为Object,且参数名与配置的名称一致(上述代码注解中的returning和参数中一致)
- 异常通知 @AfterThrowing:
- 在目标方法执行时监测,若遇到 异常将会立即执行
- 能够接收目标方法抛出的异常,类型为Exception,命名也和最终通知要求一样
- 环绕通知 @Around:
- 可以控制目标方法的执行,即是否调用proceed方法
- 可获得目标方法的返回值
- 当在执行目标方法时出现异常,则不会执行proceed方法之后的代码