spring框架之AOP面向切面编程

547 阅读7分钟

今天是刘小爱自学Java的第123天。

感谢你的观看,谢谢你。

学过很多面向XX编程,比如:

面向过程编程,面向对象编程,面向接口编程,现在又是面向切面编程。

但是不管如何,说来说去最终都是面向搜索引擎编程:面向百度编程,面向谷歌编程。

今日学习内容安排:

  • AOP的引入,它到底是干嘛的?
  • AOP面向切面编程的思想概述,以及其常见术语的解释说明。
  • 两种AOP底层实现机制,同时也是对动态代理的再一次回顾学习。

本来是打算尽可能将AOP知识点糅合到一篇文章中说明的,但是内容实在是太多了,写了近三千字一半都还没有学完,看来还是得慢慢来了。

一、AOP的引入

在学它之前,我们先要搞清楚它是干嘛的?

dao层的方法基本都是增删改查,现在需要将所有方法都增加打印日志的功能,怎么办?

如果我们每个方法里面都实现打印日志的功能,那也太复杂了,所以选择封装:

①方法的封装

我们将打印日志的功能封装到一个特有方法中,只需要在其它方法中调用该方法即可。

但是这样就有一个很大的问题:

dao层不只有userDao这个类,还有其它的类,也需要打印日志的功能,那怎么办?

②继承

我们将打印日志的功能封装到一个类中,哪个类需要该方法就继承它即可,根据继承的原则:子类可以直接使用父类的方法。

但是代码还是有问题,会出现代码的侵入。

有没有方法可以不用修改类中方法的任何内容,就能实现方法的拓展?

有,就是代理类的使用。

注意:我举的这些例子都是伪代码,并不代表本身的业务逻辑,只是为了引出AOP的概念。

③代码的侵入

我们想给方法增加功能,使用继承的话都需要在对应方法中调用一个打印日志的方法。

对方法本身修改了,有代码侵入,这是不符合OCP原则的,即对扩展开放,对修改关闭:你增强我的功能可以,但你不可以修改我

④使用代理

在被代理类方法的基础上,拓展了一个打印日志的方法,本身的方法并没有发生任何变化。

当然这里也是伪代码,并没有使用到动态代理,文章后面有更详细的一步步说明。

我们以继承->代理的这种代码变化过程,引出AOP面向切面编程的概念。

二、AOP概述及相关术语

AOP全称Aspect Oriented Programing,翻译为面向切面编程,它是一种编程思想。

我们都知道Java是一门面向对象编程的,即OOP全称Object Oriented Programming。

AOP是OOP思想上的延续,采取横向抽取机制,取代了传统纵向继承体系重复性代码的编写。

简单的理解就是,它的作用和继承很像,但是它比继承要更强,用一句来说明AOP就是:

基于原有目标对象,创建代理对象,在不修改原对象代码情况下,通过代理对象调用增强功能的代码,从而对原有方法进行增强 。

关于AOP编程相关术语

这些术语太生涩难懂了,每一个概念涉及到的知识面还很广,想要完全弄懂太难了。

这里用一个例子来做说明,当然说明并不是很准确,但是对于新手来说方便理解记忆。

①目标对象Target

也就是需要被增强的对象。

②织入Weaving

根据目标对象来创建代理对象的整个过程。

③代理对象Proxy

即根据目标对象生成的代理对象。

④连接点JoinPoint

所谓连接点是指那些被拦截到的点。

就可以理解成对象中的方法,因为在Spring中,只支持方法类型的连接点。

⑤切入点PointCut

所谓切入点就是连接点的一部分,即需要被拦截的连接点就是切入点。

就可以理解成对象中需要增强的方法。

⑥通知Advice

也就是增强的方法,例子中就是记录日志。

通知分为前置通知、后置通知、异常通知、最终通知、环绕通知,这些后续会讲述。

⑦切面Aspect

是通知和切入点的结合,通知和切入点共同定义了关于切面的全部内容。它的功能、在何时和何地完成其功能?说白了也就是:

如何将增强方法添加到对应的方法中?

此外还有一个术语叫:引介Introduction

在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或属性,这个实际开发中基本涉及不到。

AOP是基于动态代理的,基于两种动态代理机制:JDK动态代理和CGLIB动态代理。

三、JDK动态代理实现AOP

当然JDK动态代理很少使用,但是还是都写下,就当是对动态代理知识点的一个回顾。

创建工厂类,该类可以获取代理类对象:

①获取代理对象方法

通过代理工厂的该方法就可以获取一个代理对象,为了通用性将返回值设定为Object。

②实例化代理类对象

Proxy类的静态方法newProxyInstance(),根据方法名也能知道它是干嘛的,基本上动态代理的核心就是这个方法,参数有三个:

  • 目标对象的类加载器。
  • 目标对象实现的接口有哪些。
  • 调用处理器。

当然,其代码编写有更优的方式,在Cglib动态代理中会说明,此处就使用最原始的方式。

③调用处理器

InvocationHandler是一个接口,使用匿名内部类的方式获取其对象,其有一个方法叫invoke,该方法也有三个参数。

如果方法名是我们需要增强的方法,那么我们给它增加一个功能,也就是④。

如果不是,那么调用自己就好了,也就是method.invoke(target,args)。

代码写完,做个测试

④功能测试

因为在动态代理中我们只选择对queryAll方法增强,所以用代理对象调用queryAll方法时会额外输出“记录日志”。

而update方法不增强,就只会执行本身的功能,也就是“更新数据”。

当然Jdk动态代理有一个局限,就是必须要有接口才行,所以就引出了CGLIB的使用。

四、CGLIB动态代理

CGLIB(Code Generation Library)是一个强大的,高性能的开源项目。

其作用最直接的解释就是:不需要接口也可以实现动态代理。

①获取代理对象生成器

Enhancer,增强器的意思,也就是通过它来实现方法的增强。

②设置目标对象的Class对象

该参数是目标对象的Class对象,不是类加载器,和Jdk动态代理有一定的区别。

③设置回调函数

Jdk动态代理中的三个参数:类加载器、接口以及调用处理器,Cglib中不需要接口,该参数就相当于jdk动态代理中的调用处理器。

setCallback方法的参数需要该接口的实现类对象,我们可以直接使用匿名内部类的方式作为参数,就和调用处理器一样。

但是在本类中实现这个接口,不就有了一个现成的实现类么?而this表示谁调用我就是谁,本类或者本类的子类都行。

这里进一步优化代码的编写,上述Jdk动态代理中也可以这样优化。

④intercept方法

这是MethodInterceptor接口中的一个方法,intercept,翻译就是拦截的意思。

其参数和Jdk中的调用处理器基本一样。

⑤生成代理对象

enhancer调用create()生成代理对象。

代码写完,做个测试

⑥方法测试

通过运行结果我们可以发现:和Jdk动态代理能达到一样增强选定方法的效果。

注意:目标对象CustomerServicePlus并没有实现接口,如果使用Jdk动态代理是不行的,得使用Cglib动态代理才可以。

最后

谢谢你的观看。

如果可以的话,麻烦帮忙点个赞,谢谢你。