再也不怕面试官问我Spring AOP了

347 阅读8分钟

干货有质量,水文有情怀,微信搜索【程序控】,关注这个有趣的灵魂

在这里插入图片描述

前言

上一篇Spring IOC知识点一网打尽咱们讲了Spring IOC,这篇我们学习Spring中另一个重要的特性AOP,以及其中的代理特性,同样是重点~~

先来几个名词熟悉下,AOP面向切面编程:(Aspect Object Programming),静态代理和动态代理,静态代理以AspectJ为代表,动态代理分为JDK动态代理和Cglib动态代理

湿兄这B,这是搞事情啊,上一节才说了不要注重太多名词,这节上来给我整这么多名词,嫌我脑子太好使是吗?先给大家搞个图来解释下

在这里插入图片描述

开个玩笑,小哥哥们别急,上面是让大家熟悉一下名词,有个印象即可,接下来我会慢慢给大家介绍的,轻点碰,怕疼,么么~~

AOP概述

AOP,面向切面编程,一句话说明,把一些公共的、和业务无关的功能性代码抽取出来,在运行的时候动态的往业务方法上植入。

在这里插入图片描述 举个例子: 比如我们常见的开启和关闭事务,我们现在有很多业务方法add()、delete()、get()等,在每一个方法中都有开启事务和关闭事务操作。

这种代码是重复的,我们可以把这种代码提取出来。如果我们单纯的用OOP面向对象的思想来优化掉,是这个样子的: 在这里插入图片描述

这样子即使我们的代码看着少了,但是呢?在delete()、get()等方法还会有开启事务和关闭事务这些重复的代码。

这个时候AOP上场了,我们可以通过动态代理,把对象增强,将非业务代码写在要增强的逻辑上。下面我们会介绍动态代理的底层实现~ 在这里插入图片描述

再举个例子: 现在有个系统有很多逻辑功能已经写好上线了,客户新提出一个需求,在每一个方法调用的时候都要写日志,顺便写到数据库中(上周湿兄碰到一个类似的客户~~)

咋办吧?总不能找到每一个业务方法,在业务代码中再一个个去添加吧,太麻烦了。

聪明的你肯定想到办法了,AOP啊,直接面向切面,把方法通过动态代理增强,这样就不用修改原有的业务逻辑了。

到目前为止,简单了解了AOP的思想和作用,我们可以简单的认为:

  • AOP思想就是面向切面编程,解开对象内部,以增强的方式影响原有功能

  • AOP是一个横向关系,可以降低模块间的耦合度,提取公共代码部分

Spring AOP 解决的就是 对象的功能增强。 在不改动原有逻辑的基础上去增强新的功能,以切面的形式开辟一个新角度。

Spring AOP的几个基本概念:

  • Aspect(切面):通常是一个类,里面可以定义切入点和通知

  • JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用

  • Advice(通知):在切入点上执行的增强处理,有before、after等

  • Pointcut(切入点):带有通知的连接点,在程序中主要为书写切入点表达式

通知方法的几种类型:

  • 前置通知(@Before):在我们执行目标方法之前运行

  • 后置通知(@After):在我们目标方法运行结束之后 ,不管有没有异常

  • 返回通知(@AfterReturning):目标方法正常返回值后运行

  • 异常通知(@AfterThrowing):目标方法出现异常后运行

  • 环绕通知(@Around): 需要手动执行joinPoint.procced(),前置通知和后置通知的结合

静态代理

代理,一种设计模式,即提供了通过代理对目标对象的访问方式,可以在目标对象的实现基础上增强

在这里插入图片描述

用一个例子来说明静态代理~:

现在一个接口User,里面有save()方法;一个实现了User接口的实现类UserImpl,用来保存数据;现在我想要在其保存数据的操作上加上开启和关闭事务,当然我们可以直接在业务代码上加即可。

但是,如果现在有很多的方法都要加呢?

于是我们找了一个代理对象,也是实现User接口的实现类,内部包含属性对象UserImpl,如下: 在这里插入图片描述

这样,很多公共的事情可以交给代理去做了,不用自己做了~

我们下面来说说AspectJ扩展一下知识面:

AspectJ是语言级别的AOP实现,扩展了Java语言,定义了AOP语法,能够在编译期提供横切代码的织入,所以它有专门的编译器用来生成遵守Java字节码规范的Class文件。

静态代理是有不足的:

  • 如果接口改了,代理需要跟着修改,比较麻烦

  • 代理对象需要和目标实现一样的接口,会有很多接口实现类对象

接下来我们看动态代理如何做~

在这里插入图片描述

动态代理

在Spring AOP底层中就是动态代理,有两种实现: JDK动态代理和CGlib动态代理。

来源《精通Spring4.x 企业应用开发实战》一段话:

Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要特殊的类装载器,它在运行期通过代理方式向目标类织入增强代码。在Spring中可以无缝地将Spring AOP、IoC和AspectJ整合在一起。

Spring AOP构建在动态代理之上,因此Spring对AOP的支持局限于方法的拦截。什么意思呢?就是增强只会到方法层面,无法具体到代码块级别。

JDK动态代理

JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。Spring AOP默认使用JDK动态代理,我们先来看下下面代码:

在这里插入图片描述

测试下效果:

在这里插入图片描述

湿兄,你搞我哦,上来搞一堆不懂得代码看个屁啊,你再也不是我的湿兄了。 在这里插入图片描述

这样,是的,我变了,变得比以前 更爱腻们了,木啊~

接下来给大家解释下:

Proxy.newProxyInstance是JDK提供的动态代理,传入的参数是要增强类的类加载器,以及类的接口和一个InvocationHandler对象,这个对象的内部的invoke()函数则可以用来实现增强功能。

JDK动态代理底层是通过修改实体类对象Class文件的字节码来实现增强功能的。

说两个常见问题:为什么 JDK 动态代理要基于接口实现,而不是基于继承来实现?JDK 动态代理中,目标对象调用自己的另一个方法,会经过代理对象么?

大家可以先自行思考下~~

在这里插入图片描述

第一个,为什么基于接口实现?

动态代理对象默认继承了proxy对象,而且Java不支持多继承,所以JDK代理要基于接口实现。

第二个,目标对象调用其余方法会调用代理对象吗?

答案是不会,内部调用等于this.A()这种,this指的是原对象,而不是代理对象,所以A方法并未得到增强。

CGlib动态代理

由于JDK动态代理需要有接口才可以实现增强,但是我们在开发中可能没有接口,这时AOP便默认采用CGlib动态代理实现。

CGlib代理也叫作子类代理,从内存中构建出一个子类来扩展目标对象的功能。

CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。

直接分几点来介绍下:

  • 如Spring核心包已经包括了CGlib功能,所以直接引入了Spring-core-3.2.5.jar包即可

  • 代理的类不能为final,否则报错【在内存中构建子类来做扩展,当然不能为final,有final就不能继承了】

  • 目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。

其实使用CGlib就是为了弥补JDK动态代理的不足之处【代理对象必须要实现接口】;

絮叨

你知道的越多,你不知道的也越多。

建议:Spring中IOC和AOP是基础的,也是很重要的,也是面试中的宠儿

觉得不错的可以关注我的微信公众号【程序控】,湿兄会一直坚持给大家分享技术文章,让大家悄悄拔尖,然后惊艳所有人~~

在这里插入图片描述