Springboot集成AOP

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

  1. ProceedingJoinPoint的 proceed方法的作用:执行目标方法,或者说是去调用了目标方法,得到目标方法的返回值。
  2. 环绕通知 ProceedingJoinPoint 执行proceed方法的作用是让目标方法执行,这也是环绕通知和前置、后置通知方法的一个最大区别。
  3. Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。
  4. 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

五种方式详解:

  1. 前置通知 @Before:
    • 在目标方法之前执行
    • 无论目标方法是否会抛异常,都执行
  2. 后置通知 @After:
    • 在目标方法执行之后执行
    • 目标方法遇到异常后会执行
  3. 最终通知 @AfterReturning:
    • 在目标方法执行之后执行,在后置通知之前执行
    • 当目标方法遇到异常后不会执行
    • 能够接收目标方法的返回值,但是需要注意接收参数的类型为Object,且参数名与配置的名称一致(上述代码注解中的returning和参数中一致)
  4. 异常通知 @AfterThrowing:
    • 在目标方法执行时监测,若遇到 异常将会立即执行
    • 能够接收目标方法抛出的异常,类型为Exception,命名也和最终通知要求一样
  5. 环绕通知 @Around:
    • 可以控制目标方法的执行,即是否调用proceed方法
    • 可获得目标方法的返回值
    • 当在执行目标方法时出现异常,则不会执行proceed方法之后的代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值