Spring & Spring MVC 5. Spring AOP

一、什么是 AOP

我们最开始说 Spring 的时候,说了两个东西,一个是 IoC,另一个就是 AOP,我们百度百科看一波。

AOP,面向切面编程,通过预编译的方式,在运行期间动态代理实现程序功能,而且啊这个东西可以达到统一维护。

这个 AOP 可以说是 Spring 框架中又一个核心,可以通过这个 AOP 对业务代码的各部分进行隔离,使得业务逻辑的各部分之间的耦合度降低。可以说这个东西非常厉害。

其实说了这么多我相信各位还是蒙圈状态。我们画个图来感受一下。

这就好比我们业务代码,我们想让每次运行到任何的业务代码的时候,都去获取一下当前时间,然后运行完之后,再去获取一下当前时间,这个怎么去做?

有的人可能会说了,每次进入业务代码的时候,写一个 System.currentTimeMillis(); 这个去拿时间,运行完再拿一次。

这么做确实可以,但是,这的需要在我们所有的业务代码上都得去写。那这玩意要是写的多了,不得吐血。。

有的人可能又说了,弄成一个公共方法,每次写的时候我们调用一下不就好了。这么做也可以,但是还得我们在所有的业务代码上去写。

这里,我们的 AOP 就派上用场了,我们可以定义一个 AOP,然后对整个业务层的代码进行切入,这样,我们写一次获取时间就可以,不需要每次都去写了。

二、AOP 的实现

想使用 AOP,首先需要 AOP 的 jar 包,当然啊,我们项目中其实已经有了。(PS:但是还差一个哦,我们后面会说)

接着再来说一下一会会用到的名词,先认识一下,然后对照着慢慢理解

Aspect:切面。这个就是给我们的这个操作,或者是这一组操作一个总的名称。好比这个就是获取执行时间的切面。

Joint point:连接点。其实这个翻译的不准确,准确的说这个就是一个代理对象,这个对象中存放了很多东西,我们可以后期看到它。讲述环绕的时候可以看到

Pointcut:切点。这个就是切点就是从哪里切开,然后到时候会往这个切点中放入很多的 Advice。

Advice:增强。这个可以说是一个重点,Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。

Target:目标对象。织入 Advice 的目标对象.。

Weaving:织入。将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

总的来说,这乱七八糟的一大堆定义很多,不慌,我们来实现一下上面的那个需求。

首先,我们先定义一个切面,但是需要在配置文件 bean.xml 中修改点东西。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

有了这些还不行,还得需要一个 jar 包的支持,没有这个 jar 会报错的。

下载路径:https://repo1.maven.org/maven2/org/aspectj/aspectjweaver/1.9.6/aspectjweaver-1.9.6.jar

接下来我们就来定义切面

这样我们就定义了一个名字叫 timeAspect 的切面,当然,这还没有写完,我们还得写切点。

切点我们定义了之后,我们可以看到有一个东西没有填写,这个 expression 属性是指,我们具体要切的地方。从那个切点切进去。我们这里需要使用它的一个固定语法去写

这里我来解释。

execution():这个是表达式的主体,必须要写的,不写不行。

括号中第一个 * :这个是代表返回值类型为任意的类型。

接着是 com.lemon1234.abcd 这个就是我项目的包路径,当然,现在还没有创建这个包路径,我们一会去创建。

abcd 后面的 .* :这个是包下面的任意类

再往后的 .* 是表示类下面的任何方法

(..) :这个括号表示参数,两个点表示任何参数类型

这也就是说,* 代表的是一个单词,.. 代表的是任何,可以表示多个。(慢慢理解,不着急会)

写好切点之后,我们就可以写通知了。

在 Spring AOP 中,有五种常用通知,分别是:在方法执行前,在方法执行后,在方法抛出异常的时候,在方法执行的最后,环绕通知。共五个,我们一个一个写

先去创建一个类,我们叫 Notice 类。

里面写五个方法。

写好之后,弄成 Bean 注册到 Spring 中。(3 和 4 的参数我们后面说)

接着我们让我们的 aop 切面接入这个 notice。

这样就 OK 了,下面开始说这些个通知怎么用。

前置通知和后置通知

接着我们写一个测试类,写几个方法进行测试。

自己注册一下 Bean,然后开始测试。

可以看到哈,日志是正确的哈,最后一个 1 是我们主函数输出的,中间的 1 是我们调用方法输出的。

最终通知

通过名字就能知道,在 return 之后执行,我们再来测试。

可以看到,在主函数输出 1 之前,我们的最终通知出现了。

当然啊,这里的这个 ret 就是我们调用方法的返回值。所以我们在通知里写的是 Object 类型。

异常通知

异常通知配置中的这个 ex,就是对应的我们方法 printLog3 中的那个入参,我们来测试一下

可以看到,前置、后置还是都有的,然后就是这个异常通知。

环绕通知

环绕就是使用的 around 这个,然后我们来测试一下

Null return value from advice does not match primitive return type for: public int com.lemon1234.abcd.AopTest.found1()

报错了,异常是上面这个。。。

这里的原因是:

环绕没有返回值为null,select方法返回类型不是void ,而是int,刚好满足条件,抛出此异常。int是无法匹配null的(除非是包装类型Integer,不过接到的一直是null)。

原文博客连接:https://blog.csdn.net/baidu_19473529/article/details/84963104

我们这里将 int 改成 Integer。

再来测试。

OK的。

但是,这里会有人问了,不是环绕吗,为啥就只有一个 666。

这里是因为你并没有理解人家环绕通知的具体意思。

环绕通知需要携带 ProceedingJoinPoint 这个类型的参数,ProceedingJoinPoint 类型的参数可以决定是否执行目标函数。

当我们执行 t.found1() 这个方法的时候,其实是先去环绕通知的方法,因为环绕方法中没有这个 ProceedingJoinPoint 参数,所以根本没有执行 AopTest 的 found1() 这个方法。

不相信的话,我们可以修改一下 AopTest 的 found1() 方法。

我们再来执行。

这个 666 是 环绕通知的输出,虽然我们调用了 found1() 这个方法,但是并没有执行,这个是因为我们没有在环绕中执行目标函数。这里是一个重点

而执行目标函数是由 ProceedingJoinPoint 去调用执行的。我们来修改一下环绕的通知。

这里需要解释一下,我们传入一个参数,参数就是 ProceedingJoinPoint,这个是固定写法,记住即可。

使用 jp 调用 proceed() 这个方法,这个就是调用目标函数。返回了一个 Object ,这个就是目标函数的返回值,这里我们返回的是一个 int 类型的值。

然后我们将 a 返回。

再来执行以下测试方法。

可以看到哈,我们这个环绕就完成了。

好了,这一篇就先到这里,先看着,慢慢消化。

各位,还有一件事,我的官网要准备从新制作,有没有什么好的提议,用什么框架,有什么功能,欢迎各位小伙伴提供~~~(直接评论在评论区即可,我到时候会整理起来)

猜你喜欢

转载自blog.csdn.net/weixin_45908370/article/details/114678125