三、初次见面-面向切面编程 |
💕 三、初次见面-面向切面编程
前一篇文章我们介绍了什么是面向接口编程,现在又来一个面向切面编程,两者到底存在什么联系呢? 下面慢慢道来!
AOP(Aspect-Oriented Programming的简称),也就是面向切面编程的意思,它是一种编程思想,在Spring的官方文档中描述: 面向切面编程(AOP)提供了另一种思考程序结构的方式来对面向对象编程(OOP)的进行补充和完善,面向对象编程(OOP)中关键的是对象,而面向切面编程(AOP)中关键的是切面。
AOP中切面可以实现关注点的模块化(即统一抽取,提高复用),例如跨越多种类型和对象的事务管理,这种关注点在 AOP 文献中通常被称为横切关注点。
单单看文字描述可能比较抽象,下面我们通过具体例子结合图片来形象化这些概念。
例子: 洗澡(声明,以下步骤是个人构想,不代表大家,如果觉的我的设想不够丰富的,欢迎评论留言,送你上热搜)
步骤(男): 脱衣服、唱歌、洗脸、洗头、洗身体、擦干身体、穿衣服
步骤(女): 脱衣服、洗脸、洗头、护发、洗身体、擦干身体、护肤、穿衣服
发现问题: 通过上面的例子,我们会发现无论男女,脱衣服、穿衣服是洗澡不可缺少的步骤,而且这两个步骤在“洗澡”这个业务不是核心,它只是一个关注点,因为脱衣服和穿衣服的场景并不只是在洗澡中存在(还有什么场景自己联想),就比如: 天气热我们需要脱衣服,天气冷我们需要穿衣服,所以,将衣服的管理定义成一个模块,然后在需要的地方调用才是是一个更加合理的设计,具体如下图:
小结
通过上一篇文章我们能够知道,面向对象编程(OOP)的出现让开发者能够实现纵向的业务逻辑处理,但面向对象编程(OOP)并不适合用于定义横向业务逻辑的关系,这样的设计会导致系统出现大量重复代码,复用性极差,如最常用的日志以及事务功能,它们都可能是横向的分布在不通的业务层级(对象层级)中,但是又和具体的核心业务无直接关系,诸如这样类型的代码,在程序中被称作横切(cross cutting),我们应该考虑将这一类代码进行统一管理,提高复用性。
面向切面编程(AOP)就是将这类与核心业务无关的,但又影响着多个类的公共行为抽取、封装到一个可重用模块,从而实现代码复用和模块解耦的目的,这种开发思想则被称为面向切面编程。
四、面向切面编程的作用 |
💖 四、面向切面编程的作用
通过上面例子和图形,大家心里多少对面向切面编程有了初步的了解,那下面就来看看面向切面编程能够给我们什么好处。
💐 4.1、降低模块间的耦合度
系统实现“高内聚、低耦合”一直是我们程序开发者的追求。AOP技术将软件系统划分成了核心关注点和横切关注点两部分,业务的核心功能则为核心关注点,与业务无关或者关系不大的则为横切关注点。
横切关注点总是作用于核心关注点周围,且对应的业务含义类似,系统开发中常见的如:权限认证、事务管理、日志记录(如所有请求接口的入参都需要记录到日志中)等都属于横切关注点。
AOP技术的出现,将系统的核心关注点和横切关注点分离,避免了非核心业务耦合在核心业务中,降低了模块间的耦合度,提高了系统的可读性、可操作性和可维护性。
🌸 4.2、代码复用
AOP技术将与核心业务无关或者关系不大却为不同业务模块公用或者需要的逻辑抽取成新的模块,在需要的地方再引入,大大减少的系统的重复代码。(在这里有个问题需要大家思考: 要实现代码的复用,直接通过继承或者抽取公共类不就行了,为什么还要引入AOP这种技术?)
五、面向切面编程的实现分类 |
💗 五、面向切面编程的实现分类
面向切面编程(AOP)实现的效果就是在不修改源代码的情况下,给系统中的某些组件添加某些与核心业务无关的通用逻辑,AOP框架本质就是对目标对象源代码修改,生成具有新功能的代理对象,根据对目标对象源代码的修改时机,可以将AOP框架划分为以下两大类:
🌹 5.1、静态AOP实现
采用了这种方案的AOP框架会在编译阶段对程序的需要增强的代码进行修改,从而生成静态的AOP代理类,即这样实际上编译出来的类的class文件实际上已经是增强后的代理类,因为编译期的特殊性,所以要实现静态AOP需要用特殊的编译器,常见的静态AOP框架如:AspectJ
🥀 5.2、动态AOP实现
采用这种方案的AOP框架是在运行时才会对目标对象生成代理对象(如在内存: 通过JDK的动态代理技术或者CGlib动态字节码技术生成AOP代理对象),这种方案常见的框架:Spring AOP(就是本文的主角)
🌺 5.3、不同AOP框架之间的特点比较
六、面向切面编程的术语 |
💙 六、面向切面编程的术语
通过上文,我们已经对面向切面编程有了大概的印象,下面我们就开始真正了解关于面向切面编程的相关知识点。
俗话说得好: “见人说人话,见鬼说鬼话”,想要理解面向切面编程的精髓,那就要先读懂面向切面编程中的术语,连术语都不懂是什么含义,谈何认识、使用。下面先通过一个图片直观的认识AOP中常用术语之间的一个联系。
看完上图大家可能一头雾水,没关系,下面来具体介绍AOP相关的术语,然后再重复看上面的联系图,相信你会有更深刻的理解。
🌷 6.1、相关概念
1、增强
可以理解为通过一些操作让类可以完成原来做不到的事情,如: 在上面的洗澡的例子中,洗澡方法中不包含"脱衣服"和“穿衣服”的功能,但是通过AOP可以让"洗澡方法"具有这两个功能,那我们就可以说“洗澡方法”被增强了(再简单点你就看看美国队长,打了个药能锤出地球…)。
常见的增强方法: 继承、装饰者模式、动态代理(留个伏笔,你猜文章有使用到哪个方式实现增强呢?)
2、目标对象:
即我们需要增强的类生成的对象,如上面例子中包含“洗澡”方法的类对应的对象。
3、代理对象:
通过AOP框架,对目标对象增强生成的新对象,它可以拥有目标对象没有的行为和属性。
🌱 6.2、通知(Advice)
切面的具体功能和使用场景,它定义了切面的具体功能是什么以及何时被使用。 在上面洗澡的例子中,切面的"具体功能"则为:“脱衣服和穿衣服”,"何时被使用"则为: “在洗澡前要脱衣服”,“在洗澡后要穿衣服”。
🌿 6.3、通知类型
在Spring中,根据使用时机的不同,将通知划分为了5种类型,下面来一个个认识下吧!
- 前置通知(Befor): 在目标方法被调用之前调用的通知功能。如上文例子中在调用“洗澡方法”前先执行“脱衣服”通知。
- 后置通知(After): 在目标方法完成后调用的通知功能,即使在执行目标方法出现异常,也照常执行。如上面的例子中在洗澡时喷洒炸了,但是照样在洗完澡后继续完成“穿衣服”的通知功能。
- 返回通知(After-returning): 在目标方法成功执行之后才调用通知功能。如上面的例子,在洗澡过程中厕所爆炸了,直接送去医院了,“穿衣服”的通知功能就不会被调用(味道太大…)
- 异常通知(After-throwing): 在目标方法抛出异常后执行通知功能。如上面例子,在洗澡中厕所又炸了,但是我提前准备了厕所炸后应该怎么办的方案,一旦出现了这个异常,就会去调用这个通知功能,总不能光溜溜的出去吧。
- 环绕通知(Around): 通知包围了目标方法,在调用目标方法前后可以执行自定义的通知功能。如上面例子,在洗澡前脱衣服,在洗澡后穿衣服的两个功能可以直接通过环绕通知完成。
☘️ 6.4、连接点(Join point)
它表示在业务逻辑执行过程中能够插入切面通知的一个点。在Spring中,这个点可以是调用方法时、调用方法后、抛出异常时。 切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
Spring AOP 是基于动态代理的,所以它只支持方法的连接点,但像其他的AOP框架如AspectJ在修改属性时或者构造器执行时都可以织入通知,达到更细颗粒度的控制拦截。
🍀 6.5、切点(Pointcut)
通俗理解: 通知作用于哪些连接点,这个点可以称为切点,通过上面通知介绍可知,“通知”定义了切面是"什么"和"何时使用"作用,切点则定义了"切面"在"何处"使用。 通过配置切点,可以将通知织入到一个或者多个连接点中。如上文的例子中,洗澡方法的执行前后就可以理解为切点。
在实际应用中,是通过指定类名称、方法名称、正则表达式进行来匹配指定切点,甚至还存在一些AOP框架允许动创建动态的切点,根据运行时的决策(如:方法的参数值)来确定是否需要使用通知增强(这些是拓展的一些知识点,本文暂不涉及)。
🌿 6.6、切面(Aspect)
切面即切点和通知的集合,它定义了它是什么,在何时、何处完成这个功能。 如上文的例子中,洗澡方法的执行前后就可以理解为切点,"脱衣服"和"穿衣服"就可以理解为通知。
☘️ 6.7、引入(Introduction)
允许向类中添加新的属性和方法,实际上就是增强类(让类拥有原来没有的功能或者属性)。 具体可以理解成将切面应用到目标类上,从而实现不修改类的代码,而让它具有新的功能和属性。
🍀 6.8、织入(Weaving)
织入是把切面应用到目标对象并创建新的代理对象的过程,这是一个动态的过程, 切面在指定的切点被织入到目标对象中,实际上在目标对象的生命周期中,存在以下的多个点可以进行织入:
1、编译期: 切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ 的织入编译器就是以这种方式织入切面的。
2、类加载期: 切面在目标类加载到 JVM 时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5 的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面。
3、运行期: 切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP 容器会为目标对象动态地创建一个代理对象。Spring AOP 就是以这种方式织入切面的。
七、AOP、Spirng AOP和代理之间的关系 |
💚 七、AOP、Spirng AOP和代理之间的关系
前面介绍了比较多理论知识,大家可能会混淆一些概念,下面来具体介绍这些概念的一个联系,帮助大家更好的例子这些概念。
🍁 7.1、代理
可以理解成委托别人帮忙处理事情,生活中最常见的例子就是二手东。 真正房东一般会给二手东工资然后将房租的出租等事情委托给二手东帮忙处理,在这个例子中二手东就是房东的代理,负责帮房东跑腿。
🍂 7.2、动态代理
在程序运行期,动态创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术,根据实现方式可以划分为:JDK动态代理和CGlib动态代理。
🍃 7.3、Spring AOP
通过上文介绍我们知道AOP是一个编程的思想,并不是一个具体的技术或者框架,这种思想延伸出来的框架叫做AOP框架,Spring AOP就是其中的一种,Spring AOP是构建在动态代理之上的。
它包含了JDK动态代理和CGlib动态代理,当一个类如果实现了接口,那么Spring AOP会使用JDK动态代理给它生成代理对象,反之则使用CGlib代理技术生成代理对象。
🚉 7.4、Spring支持的AOP类型
- 基于代理的经典 Spring AOP
- 纯 POJO 切面(使用xml结合pojo实现Spring AOP,重点介绍)
- @AspectJ 注解驱动的切面(基于注解实现Spring AOP,重点介绍)
- 注入式 AspectJ 切面(适用于 Spring 各版本)。
基于代理的经典 Spring AOP是通过ProxyFactory Bean实现,在引入了了简单的声明式 AOP 和基于注解的 AOP 之后,看起来会显得比较笨重和复杂,所以本文不介绍,第四种方案使用需要学习一些额外的知识,本文也暂不介绍。
🚊 7.5、Spring AOP框架的一些特点
1、Spring AOP框架和AspectJ框架的对比
Spring AOP的通知是由Java编写的,这样使用起来就可以跟普通的JAVA功能一样快速入手,并且提供了XML和注解两种方式,可以供开发者有更多的选择。 而AspectJ则与之相反,它最初是以JAVA语言拓展的,虽然支持了更强大的功能和更细颗粒度的控制,但是使用的话需要学习新的工具和语法。
2、Spring AOP如何实现增强目标对象
实际上在代理对象中包含了切面和目标对象,并且可以进行拦截通知方法的调用,当调用者进行调用时,先执行切面逻辑,然后再将调用转发给目标对象的bean,从而实现增强效果,因为在运行时才创建代理对象,所以Spring AOP不像AspectJ一样需要特殊的编译器来实现切面织入,如下图所示:
3、Spring AOP只支持方法级别的连接点
并非所有的AOP框架都是相同的,不同的框架在连接点模型上就可能存在强弱之分。如AspectJ除了支持方法切点外,还提供了字段和构造器的切点,因为Spring AOP是基于动态代理的,所以只支持方法级别的连接点,但是方法拦截已经可以满足绝大多数的需求,如果是需要方法拦截之前的连接点拦截,则需要使用其他的AOP框架如AspectJ来实现。
八、Spring AOP支持的切点指示器 |
💜 八、Spring AOP支持的切点指示器
注: 虽然本文介绍的是Spring AOP,但实际上Spring 和 AspectJ 项目之间有大量的协作,而且 Spring 对 AOP 的支持在很多方面借鉴了 AspectJ 项目,比如切点指示器。
在上文中有提到,切面就是通知和切点结合,切点定义了应该在哪里应用通知,而在Spring AOP中,切点是通过切点指示器来实现具体匹配到哪些连接点的,下面就来认识下Spring AOP支持的切点指示器吧!
小结:上面虽然列出了一堆指示器,但是只有execution 指示器是实际执行匹配的,其他的指示器都只是用来限制匹配的, 就像男厕所只能进入,女厕所只能女生进入,人妖只能站外边了!
因为切点指示器的类别比较多,下面只举例一些常使用到的指示器,关于每个指示器的运用,下面会专门开一篇文章进行讲解,大家记得点下订阅专栏哦!
温馨提示: 上面的指示器并不需要大家死记硬背,在使用到的时候如果忘记直接查询一下就可以,我们学习AOP更重要的是理解这种编程思想和运用,并不是去死记硬背这里面的一些关键词,这样不仅浪费时间而且效率也低。
🚋 8.1、Spring AOP 切点表达式举例剖析
写在切点指示器中用于匹配连接点的表达式叫做切点表达式,有了切点表达式,我们就可以根据自己的需要去匹配需要增强的方法。 为了能够让大家更清晰的理解关于切点表达式,还是上面洗澡的例子,我们将它抽象成下面的代码,不同使用者去使实现它:
package wash;
public interface Wash{
void takeAWash();
}
假设我们要实现只要调用"洗澡"这方法,就要触发"脱衣服,穿衣服"的行为,那切点表达式定义就如下:
由此,我们可以总结出切点表达式的一个格式语法如下:
格式:execution( [修饰符] 返回值类型 包名.类名.方法名 (参数) ),其中*表示任意类型,方法参数中 . . 表示任意的参数。
🚌 8.2、Spring AOP 常用指示器解析
常用注解
1、@Pointcut:封装相同的切点表达式,提供统一的引用
2、@within:用于匹配所以持有指定注解类型内的方法;而within是用于匹配指定类型内的方法执行;
切点通配符
- *通配符 : 表示任意数量的字符,如:
// 匹配com.demo.IPerson类中任意返回值类型和任意参数的takeAWash方法
execution(\* com.demo.IPerson.takeAWash(..))
- …通配符:表示匹配系统中任意包和任意方法中的任意参数,如:
// 匹配任意名称,任意返回值,任意参数的方法
execution(\* \*(..))
// 匹配com.demo包及其子包下的所有类的所有方法
within(com.demo.*)
- +通配符:表示给定类的任意子类
// 匹配Demo接口下的任意子类
within(com.elvis.springaopinaction.Demo+)
九、Spring AOP中的一些问题 |
💝 九、Spring AOP中的一些问题
🚖 9.1、AOP和OOP有什么关联
OOP(面向对象编程)侧重的是在业务处理中对问题实体的属性和行为的抽象封装,从而达到更清晰高效的逻辑单元划分,提高系统的可理解性、易维护性。
AOP(面向切面编程)侧重的是将系统的核心关注点和横切关注点分离,核心关注点关注核心业务处理,横切关注点关注与核心业务无关却被核心业务引用的公共行为,将它们单独抽取成新的模块,和核心业务分离,降低模块之间的耦合度,提高代码的复用性,可以说它是面向对象编程(OOP)的进行补充和完善。
🚐 9.2、后置通知(After)和返回通知(After-returning)有什么区别
后置通知(After):表示在目标方法执行完成后执行的通知功能,即使在执行目标方法出现异常,也照常执行这个逻辑。
返回通知(After-returning)表示目标方法正常执行完成后置的通知功能,如果目标方法执行过程中出现异常,那么这个通知的逻辑就不执行。
🚑 9.3、五种通知的一个执行顺序是怎样
执行业务中不包含异常时的执行顺序:
环绕通知前部分(Around-Before) =》 前置通知(Before) =》业务逻辑 =》返回通知(AfterReturning) =》后置通知(After) =》环绕通知后(Around-After)
执行业务中包含异常时的执行顺序:
环绕通知前部分(Around-Before) =》 前置通知(Before) =》异常业务 =》异常通知(AfterThrowing) =》后置通知(After) =》环绕通知后(Around-After) =》环绕通知中捕获的异常输出
🚒 9.4、既然可以通过继承或者装饰者模式增强,为什么要引入AOP
这个问题上面就有留下一个悬念,让大家思考,不知道你有没有猜对呢?
在JAVA中,要是实现对对象增强,最常用的三种方法就是:继承、装饰者模式、动态代理。 下面我们通过具体的例子来理解三种方式的区别。
以喝奶茶举例,我们可以往奶茶中添加珍珠,椰果、红豆等配料,从而得到我们喜欢的口味,这些配料改变了原味奶茶的味道,我们可以说奶茶被“增强”了,使用JAVA代码表示如下:
1、继承方式增强
/\*\*
\* 奶茶基础类
\*/
public class MilkTea {
public void makeMilkTea(){
System.out.print("原味奶茶");
}
}
/\*\*
\* 加珍珠奶茶
\*/
class PearlMilkTea extends MilkTea{
@Override
public void makeMilkTea(){
super.makeMilkTea();
System.out.println(" + 加珍珠");
}
}
/\*\*
\* 加椰奶奶茶
\*/
class CreamMilkTea extends MilkTea{
@Override
public void makeMilkTea(){
super.makeMilkTea();
System.out.println(" + 加椰奶");
}
}
如果此时又有一个人来说要喝珍珠椰奶奶茶,我们又需要创建一个PearlCreamMilkTea子类(代码如下),如此类推,每一种排列组合我们就需要通过一个新的类去维护,这样非常容易出现类爆炸的情况,那以维护,此时,我们可以通过装饰者模式来解决这个问题:
// 珍珠椰奶奶茶
class CreamMilkTea extends PearlMilkTea {
@Override
文末有福利领取哦~
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
👉**一、Python所有方向的学习路线**
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。![img](https://img-blog.csdnimg.cn/c67c0f87cf9343879a1278dfb067f802.png)
👉**二、Python必备开发工具**
![img](https://img-blog.csdnimg.cn/757ca3f717df4825b7d90a11cad93bc7.png)
👉**三、Python视频合集**
观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
![img](https://img-blog.csdnimg.cn/31066dd7f1d245159f21623d9efafa68.png)
👉 **四、实战案例**
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。**(文末领读者福利)**
![img](https://img-blog.csdnimg.cn/e78afb3dcb8e4da3bae5b6ffb9c07ec7.png)
👉**五、Python练习题**
检查学习结果。
![img](https://img-blog.csdnimg.cn/280da06969e54cf180f4904270636b8e.png)
👉**六、面试资料**
我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
![img](https://img-blog.csdnimg.cn/a9d7c35e6919437a988883d84dcc5e58.png)
![img](https://img-blog.csdnimg.cn/5db8141418d544d3a8e9da4805b1a3f9.png)
👉因篇幅有限,仅展示部分资料,这份完整版的Python全套学习资料已经上传
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里无偿获取](https://bbs.csdn.net/topics/618317507)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**