Spring Boot学习(十七):Spring Boot的AOP教学,一看就会用

前言

Spring Boot系列: 点击查看Spring Boot系列文章



AOP

Aspect Oriented Programming(AOP):意为:面向切面编程。通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。是Spring框架中的一个重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

主要功能

日志记录,性能统计,安全控制,事务处理,异常处理等等。

我们知道AOP是通过动态代理实现的,而Spring的AOP是实现有两种,一个是JDK动态代理,一个是CGLIB实现。这个以后会出文章进行详细介绍。


SpingBoot整合AOP

1、Pom文件添加依赖,依赖只需要一个spring-boot-starter-aop,妈妈再也不用担心我导错依赖了

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、在引入AOP依赖包后,我们就不需要去做其他配置了(是不是很方便)。接下来我们就可以使用aop了

通知(Advice):也有叫增强,就是你想要执行的一些的功能,比如上面说的日志记录,性能统计,安全控制等。我们把功能定义好,然后在想用的地方就使用,这就叫通知。其实增强这个形容更加贴切,其实我们使用aop,大多就是为了在切入点执行一些额外的操作,就相当于增强了切入点。

连接点(JoinPoint):就是我们允许使用通知的一些地方,比如我们代码中的方法,如果我们将某个方法接口作为连接点。那么我们就可以在方法接口执行前、后或者环绕执行我们的通知功能。

切入点(Pointcut):切入点是在连接点的前提下,我们真正想切入的地方。比如你的一个类里,有十几个个方法,那就有几十个连接点(前,后,环绕)了对把,但是你并不想在所有方法附近都使用通知(使用也叫织入),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。

切面(Aspect):切面就相当于我们java中一个类,类里面会有属性和方法等。而切面是Pointcut和Advice的结合体。一个切面就相当于定义了,我们在哪里切入,切入之后执行什么功能(通知)

织入(weaving):把切面应用到目标对象并创建新的代理对象的过程。

目标(target):织入 Advice 的目标对象,也就是要被通知的对象。

代理(proxy):AOP是通过代理机制来对切入点进行通知操作的


下面,我定义一个简单的切面类,来讲解代码中aop的使用*

@Aspect
@Component
public class LogAop {

    private final Logger logger = LoggerFactory.getLogger(LogAop.class);


    //定义切入点
    @Pointcut("execution(public * com.example.demo.aop.InterceptorTestController.interceptorTest(..))")
    public void controllerLog(){}

    @Pointcut("execution(public * com.example.demo.mytest.JdbcTestController.*(..))")
    public void testLog(){}

    @Before("controllerLog() || testLog()")
    public void logBefore(JoinPoint joinPoint){
        //JoinPoint对象封装了SpringAop中切面方法的信息
        System.out.println("请求"+joinPoint.getSignature().getDeclaringTypeName()+"方法时间:"+new Date());
        logger.info(joinPoint.getSignature().getDeclaringTypeName()+"请求时间:"+new Date());
    }

}

切面:

@Aspect:用于定义一个切面类,要定义切面类,才能让aop生效
@Component:将该类交给spring管理

@Aspect+@Component就相当于定义了一个切面类,然后我们就可以在这个类定义PointCut和Advice 了

切入点:

@PointCut:用于定义切入点

@PointCut(…)括号里面那些就是表达式,我们一般使用execution表达式

execution语法

execution(方法修饰符(可选) 返回类型 类路径 方法名 参数 异常模式(可选))

在execution中,* 表示全部, ()匹配没有参数; (. .)代表任意多个参数; (*)代表一个参数,但可以是任意型; ( *,String)代表第一个参数为任何值,第二个为String类型。

下面来看几个常用的例子

execution(public * * *(..))   //表示切入所有public方法

execution(public * com.example.demo.aop.InterceptorTestController.interceptorTest(..)) //表示切入 com.example.demo.aop.InterceptorTestController类下的interceptorTest方法

execution(public * com.example.demo.aop.*.*(..))  //表示切入aop包下的所有方法

execution(public * com.example.demo.aop..*.*(..))//表示切入aop包和它的子包下的方法,注意:aop后面多了一个点.

切入点表达式下面定义的方法就相当于是切入的标签,在advice中用方法名()表示切入点,比如controllerLog()

例:

public void controllerLog(){}


Advice

Advice 的类型

before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)

after return advice, 在一个 join point 正常返回后执行的 advice

after throwing advice, 当一个 join point 抛出异常后执行的 advice

after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.

around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.

下面是简介

常用AOP通知(增强)类型
before(前置通知): 在方法开始执行前执行

after(后置通知): 在方法执行后执行(无论正常返回还是抛出异常都可以执行)

afterReturning(返回后通知): 在方法返回后执行

afterThrowing(异常通知): 在抛出异常时执行

around(环绕通知): 在方法执行前和执行后都会执行

我们根据自己的需求来选择Advice 的类型,在例子中我使用的是@Before,即在方法执行前执行通知

//controllerLog() || testLog()表示两个切入点,我们有多个切入点,使用||连接即可,这样我们的Advice 就可以对这些切入点进行增强
@Before("controllerLog() || testLog()")

注:我们除了可以使用||定义切点,还可以使用&&, ! 来定义切点

测试

当我访问我的接口,会打印出以下结果

在这里插入图片描述


JoinPoint

大家可以看到,在我的Advice 方法上有一个JoinPoint 参数,那么JoinPoint 是什么呢?

JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.

以下是一些JoinPoint对象的常用方法

Signature getSignature();	获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息

如我在例子中,使用的joinPoint.getSignature().getDeclaringTypeName()
输出方法的详细信息:com.example.demo.aop.InterceptorTestController

joinPoint.getSignature().getName()
输出的是方法名:interceptorTest


joinPoint.getSignature().getDeclaringType()
输出的是class类型:class com.example.demo.aop.InterceptorTestController


Object[] getArgs();	获取传入目标方法的参数对象

Object getTarget();	获取被代理的对象

Object getThis();	获取代理对象

ProceedingJoinPoint对象

ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中,
添加了

Object proceed() throws Throwable //执行目标方法 

Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法 

两个方法.


@Around

@Around类型的Advice ,应该是功能最强的Advice 了。它可以让我们目标方法之前织入增强操作,也可以在执行目标方法之后织入增强操作。还可以使用 proceed()方法控制切点方法在什么时候执行,还可以使用proceed(Object[] var1) 传入的新的参数去执行目标方法。

例子:

 @Around("controllerLog() || testLog()")
    public void logBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("方法执行前时间:"+new Date());
        //JoinPoint对象封装了SpringAop中切面方法的信息
        System.out.println("第"+(++count)+"次访问");
        System.out.println("请求"+joinPoint.getSignature().getDeclaringTypeName()+"方法时间:"+new Date());
//        logger.info(joinPoint.getSignature().getDeclaringTypeName()+"请求时间:"+new Date());
//        让切点方法执行
        joinPoint.proceed();
        System.out.println("方法执行后时间:"+new Date());
    }

使用Around类型的通知,我们可以在调用proceed方法前,做一些对目标方法的增强操作。也可以在proceed()执行后,做一些增强操作,很强大。但是大多时候,我们想要的是不影响业务代码的执行,所以Around类型不一定就是最合适的,要根据自己的需求选择。


Advice 的执行顺序

@Around→@Before→@Around→@After→@AfterRunning(如果有异常→@AfterThrowing)

在网上看到两张图,很形象的演示了Advice 执行的顺序
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值