什么是面向切面编程 _面向切面编程是什么意思

🌺 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
    public void makeMilkTea(){
        super.makeMilkTea();
        System.out.println(" + 加椰奶");
    }

}

2、装饰者方式增强

/\*\*
 \* 抽象组件:定义一个抽象接口,来规范准备附加功能的类
 \*/
public interface IMikeTea {
    void makeMilkTea();
}

/\*\*
 \* 具体组件:将要被附加功能的类,实现抽象构件角色接口
 \*/
public class MikeTeaImpl implements IMikeTea{
    @Override
    public void makeMilkTea() {
        System.out.print("原味奶茶");
    }
}

/\*\*
 \* 抽象装饰者:持有对具体构件角色的引用并定义与抽象构件角色一致的接口
 \*/
public abstract class DecoratorMikeTea implements IMikeTea{

    private IMikeTea mikeTea;

    public DecoratorMikeTea(IMikeTea mikeTea) {
        this.mikeTea = mikeTea;
    }


    public void setIMikeTea(IMikeTea mikeTea){
        this.mikeTea = mikeTea;
    }

    @Override
    public void makeMilkTea(){
        mikeTea.makeMilkTea();
    }

}

/\*\*
 \* 具体装饰:实现抽象装饰者角色,负责对具体构件添加额外功能。
 \*/
public class ConcreteCreamMilkTea extends DecoratorMikeTea{

    public ConcreteCreamMilkTea(MikeTeaImpl mikeTea) {
        super(mikeTea);
    }

    @Override
    public void makeMilkTea(){
        super.makeMilkTea();
        System.out.print(" + 加椰奶");
    }
}
...

测试代码如下:

/\*\*
 \* 测试装饰者模式
 \*/
public class DecorarorMilkTeaTest {
    public static void main(String[] args) {
        MikeTeaImpl man = new MikeTeaImpl();
        ConcreteCreamMilkTea md1 = new ConcreteCreamMilkTea(man);
        ConcretePearlMilkTea md2 = new ConcretePearlMilkTea(man);

        // 珍珠奶茶
        md1.makeMilkTea();
        System.out.println();
        // 椰奶奶茶
        md2.makeMilkTea();
        System.out.println();
        // 珍珠椰奶奶茶
        md1.setIMikeTea(md2);
        md1.makeMilkTea();
    }
}

对比上面继承的方式,我们会发现,即使我们新增一种排列组合,不需要通过新的子类来维护对应的代码,通过装饰着模式可以动态给对象添加新的职责(注:上面案例只放部分代码,全部代码已经上传到《Spring-in-action》)。

3、继承、装饰者模式、AOP(动态代理)小结

(1)、继承

面向对象编程中三大特性之一,它允许我们子类继承父类特有的属性或行为,从而提高代码的重用性。

优点: 可以进行功能增强和代码重用

缺点: JAVA是单继承,继承提高了类之间的耦合性,这样容易导致一个脆弱的对象体系,如果场景变复杂,延伸出来的子类会爆炸,难以进行维护。

(2)、装饰者模式(Decorator Pattern)

它可以动态地拓展一个对象的功能,比继承更具有弹性,本质是一种组合的思想。继承是在编译时就已经决定了子类的一个行为,如果是使用组合的方式来拓展对象的功能,可以在运行时动态地对对象拓展。

优点: 符合开闭原则,比继承具有更多灵活性,可以进行设计不同的装饰类和对它们进行排列组合,从而得到满足不同场景的组合。

缺点: 相比继承的使用,会显得更加复杂,开发难度更大。

(3)、AOP(动态代理)

将非核心业务与核心业务分离解耦,在需要的地方动态织入,大大提高了代码的复用性和可拓展性。

(4)、继承、装饰者模式、AOP之间的区别

最后

Python崛起并且风靡,因为优点多、应用领域广、被大牛们认可。学习 Python 门槛很低,但它的晋级路线很多,通过它你能进入机器学习、数据挖掘、大数据,CS等更加高级的领域。Python可以做网络应用,可以做科学计算,数据分析,可以做网络爬虫,可以做机器学习、自然语言处理、可以写游戏、可以做桌面应用…Python可以做的很多,你需要学好基础,再选择明确的方向。这里给大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!

👉Python所有方向的学习路线👈

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

👉Python必备开发工具👈

工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。

👉Python全套学习视频👈

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。

👉实战案例👈

学python就与学数学一样,是不能只看书不做题的,直接看步骤和答案会让人误以为自己全都掌握了,但是碰到生题的时候还是会一筹莫展。

因此在学习python的过程中一定要记得多动手写代码,教程只需要看一两遍即可。

👉大厂面试真题👈

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

需要这份系统化学习资料的朋友,可以戳这里无偿获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值