今天是刘小爱自学Java的第123天。
感谢你的观看,谢谢你。
学过很多面向XX编程,比如:
面向过程编程,面向对象编程,面向接口编程,现在又是面向切面编程。
但是不管如何,说来说去最终都是面向搜索引擎编程:面向百度编程,面向谷歌编程。
今日学习内容安排:
本来是打算将AOP知识点糅合到一篇文章中说明的,但是内容实在是太多了,写了近三千字一半都还没有学到,看来还是得慢慢来了。
在学它之前,我们先要搞清楚它是干嘛的?
dao层的方法基本都是增删改查,现在需要将所有方法都增加打印日志的功能,怎么办?
如果我们每个方法里面都实现打印日志的功能,那也太复杂了,所以选择封装:
①方法的封装
我们将打印日志的功能封装到一个特有方法中,只需要在其它方法中调用该方法即可。
但是这样就有一个很大的问题:
dao层不只有userDao这个类,还有其它的类,也需要打印日志的功能,那怎么办?
②继承
我们将打印日志的功能封装到一个类中,哪个类需要该方法就继承它即可,根据继承的原则:子类可以直接使用父类的方法。
但是代码还是有问题,会出现代码的侵入。
有没有方法可以不用修改类中方法的任何内容,就能实现方法的拓展?
有,就是代理类的使用。
注意:我举的这些例子都是伪代码,并不代表本身的业务逻辑,只是为了引出AOP的概念。
③代码的侵入
我们想给方法增加功能,使用继承的话都需要在对应方法中调用一个打印日志的方法。
对方法本身修改了,有代码侵入,这是不符合OCP原则的,即对扩展开放,对修改关闭:你增强我的功能可以,但你不可以修改我。
④使用代理
在被代理类方法的基础上,拓展了一个打印日志的方法,本身的方法并没有发生任何变化。
当然这里也是伪代码,并没有使用到动态代理,文章后面有更详细的一步步说明。
我们以继承->代理的这种代码变化过程,引出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动态代理很少使用,但是还是都写下,就当是对动态代理知识点的一个回顾。
创建工厂类,该类可以获取代理类对象:
①获取代理对象方法
通过代理工厂的该方法就可以获取一个代理对象,为了通用性将返回值设定为Object。
②实例化代理类对象
Proxy类的静态方法newProxyInstance(),根据方法名也能知道它是干嘛的,基本上动态代理的核心就是这个方法,参数有三个:
当然,其代码编写有更优的方式,在Cglib动态代理中会说明,此处就使用最原始的方式。
③调用处理器
InvocationHandler是一个接口,使用匿名内部类的方式获取其对象,其有一个方法叫invoke,该方法也有三个参数。
如果方法名是我们需要增强的方法,那么我们给它增加一个功能,也就是④。
如果不是,那么调用自己就好了,也就是method.invoke(target,args)。
代码写完,做个测试
⑤功能测试
因为在动态代理中我们只选择对queryAll方法增强,所以用代理对象调用queryAll方法时会额外输出“记录日志”。
而update方法不增强,就只会执行本身的功能,也就是“更新数据”。
当然Jdk动态代理有一个局限,就是必须要有接口才行,所以就引出了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动态代理才可以。
谢谢你的观看。
如果可以的话,麻烦帮忙点个赞,谢谢你。