当前位置: 首页 > article >正文

第十二章 Spring之不得不了解的内容——AOP概念篇

Spring源码阅读目录

第一部分——IOC篇

第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇

第二部分——AOP篇

第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇


文章目录

  • Spring源码阅读目录
    • 第一部分——IOC篇
    • 第二部分——AOP篇
  • 前言
  • 尝试动手写IOC容器
      • 啥?代理,连接点,切点。。。脑壳嗡嗡的
        • 一、Joinpoint(连接点)—— 哪些地方可以增强?
        • 二、Pointcut(切点)—— 如何找到这些地方?
        • 三、Advice(通知)—— 找到后要怎么增强?
        • 四、Aspect(切面)—— 多个增强如何管理?
        • 五、Weaver(织入器)—— 如何对其进行增强?
      • JDK代理
      • CGLIB代理
        • 六、Target Object(目标对象)
  • 总结


前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
在这里插入图片描述

    所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


    书接上回,在上篇 第十一章 Spring之不太熟的熟人——AOP 中,A君 已经大致了解了 AOP 的用途。接下来看看 A君 又会面临什么样的挑战吧

尝试动手写IOC容器

    出场人物:A君(苦逼的开发)、老大(项目经理)

    背景:老大要求A君在一周内开发个简单的 IOC容器

    前情提要: 老大 要求 A君 先去了解下 AOP 的相关知识,后续准备相关内容的开发 。。。

啥?代理,连接点,切点。。。脑壳嗡嗡的

    想要自己整个 AOP 单单了解其用途是远远不够的。这不,A君 又去了解 AOP 实现的一些具体细节。不看不要紧,什么 连接点、切点、切面、通知。。。 这还怎么玩?吓得 A君 差点当场离职,连夜跑路

    “冷静、冷静” A君 心里默念,深吸了一口气,A君 借鉴之前的学习经验,不再去尝试直接理解这些概念,而是从开发的角度,扪心自问:“如果让我来写,我会怎么做?” A君 略微思索了下,简单列出几步:

  1. 哪些地方可以增强?
  2. 如何找到这些地方?
  3. 找到后要怎么增强?
  4. 多个增强如何管理?
  5. 如何对其进行增强?

提出问题后,接下来就是如何解决了,对此 A君 给出自己的答复:

  1. 方法可以增强
  2. 通过方法名参数、正则等进行匹配
  3. 方法调用前、方法调用后、方法调用前后、出现异常时候可以增强
  4. 可以定义一个管理者,用来管理增强信息
  5. 继承目标类,重写方法,对其增强

有了思路后, A君 再去看那么定义,瞬间觉得眉清目秀了。接下来就是把概念和自己的想法相互印证,看看自己有什么不足的地方。由于 A君 主要抄袭 Spring (o゚▽゚)o ,所以下面的概念主要以 Spring 为主。当然,如果追求更具体的规范,可以参考 AOP联盟

一、Joinpoint(连接点)—— 哪些地方可以增强?

Joinpoint(连接点) 指程序执行中的一个点,AOP 可以在这个点上应用某种增强或切面逻辑。它定义了在程序运行时的哪些时刻可以插入切面行为

虽然概念依旧拗口, A君 却心如明镜,Joinpoint(连接点) 就是定义 AOP 对目标对象可以增强的地方。Spring 中只支持方法级别,当然,这是 Spring 的定义,只要你想,不止方法,还有:属性、构造器、异常等。更细致的也可以,比如循环这些也成。不过凡事讲究一个性价比,买东西如此,写代码同理。我们希望的是用20%的投入,获得80%的收益。而不是80%投入,20%的收益。简而言之,可以,但是没必要

二、Pointcut(切点)—— 如何找到这些地方?

Pointcut(切点) 指用于定义一组 Joinpoints 的表达式,它决定了在程序执行的哪些特定点上应用增强逻辑

Pointcut(切点) 应该是最好理解的一个概念了,例如 A君 想增强 A.a(),自然而然就会想到要怎么去匹配到 A.a() 这个方法了,用方法名也好,正则也好,亦或是表达式,这些都是具体实现的问题了,而它们统一的名字叫做 Pointcut(切点) 。当规定好哪些地方可以可以作为 Joinpoint(连接点) 后,Pointcut(切点) 就是用来匹配到对应的 Joinpoint(连接点)

三、Advice(通知)—— 找到后要怎么增强?

Advice(通知) 定义了在方法执行前、执行后、或者在方法抛出异常时应该采取的操作。它是连接横切关注点(如事务管理、日志记录、权限检查等)与程序逻辑的一种机制

Advice(通知) 也比较好理解,依旧以增强 A.a() 为例,你希望在哪里增强?方法调用前,还是方法调用后,还是出现异常的时候?当然这些都是俗语,在 AOP 中,它们有自己的名字:

  1. Before Advice(前置通知):方法调用前触发

  2. After Advice(后置通知):这个就有意思了,后置通知,方法出现异常执行吗?还是只有在方法返回后执行。所以 After Advice(后置通知) 还有更细致的区分

    • After Returning Advice:在方法正常返回后触发
    • After Throwing Advice:在方法抛出异常后触发
    • After (Finally) Advice:无论方法是否正常返回或抛出异常,都会在方法结束后触发
  3. Around Advice(环绕通知):顾名思义,把方法环绕起来,包围目标方法的调用,也就是前置+后置

  4. Introduction(引介):这个增强最为特殊,它不是根据方法执行时机来进行划分的。而是以类为维度,赋予类新功能,如:实现新的接口

四、Aspect(切面)—— 多个增强如何管理?

Aspect(切面) 是一段横切逻辑的模块化封装,它封装了横切关注点(cross-cutting concerns),并将这些逻辑应用到不同的目标对象或方法上

Aspect(切面) 可以理解成 Advice(通知)Pointcut(切点) 的管理者,显然,一个 Advice(通知) 可以对于多个 Pointcut(切点) ,一个 Pointcut(切点) 也可以对于多个 Advice(通知) ,二者是多对多的关系。不过在 Spring 中有它自己的设计,这个后边也会慢慢接触到

五、Weaver(织入器)—— 如何对其进行增强?

Weaver(织入器) 用于将 Aspect(切面) 应用到目标对象或类上。在 AOP 中,Weaver(织入器) 负责将横切 Advice(通知) 动态或静态地织入到目标类的指定 Joinpoint(连接点)

经过上面一系列的铺垫,接下来只要用 Pointcut(切点) 匹配到对应的 Joinpoint(连接点) ,将对应的 Advice(通知) 织入即可,这也就是 Weaver(织入器) 要干的活,在 Spring 中的具体实现是 ProxyFactory

    说到 ProxyFactory 就不得不了解一下 代理模式 了,对于 代理模式A君 背面试题的时候,都快看吐了,什么 静态代理动态代理 云云。为了实现 AOPA君 只能硬着头皮看下去,这一切还得从 代理模式 说起。。。

     代理模式 主要是用于做一些访问权限的控制,避免目标类直接暴露出来。A君 写了个简单的例子,首先定义一个目标类,如下:

public class A {
    public void invoke() {
        System.out.println("A君正在搞飞机。。。");
    }
}

再定义一个代理类,如下:

public class ProxyModel extends A {
    private A a = new A();

    @Override
    public void invoke() {
        System.out.println("进行安全检查#######");
        super.invoke();
    }
}

最终测试结果如下:

在这里插入图片描述

这个就是最简单的 代理模式 ,也可以称作 静态代理静态代理 虽然好,但像安全检查这类功能,一般来说,大部分类都需要涉及到。这样子每个类都要定义一个代理类,人都要疯掉了,这时候 动态代理 就应运而生了

     动态代理 正如它的名字一样,重点在于动态,那问题来了,要怎么动态呢?主要分为:编译期、运行期。编译期代理就是在编译的时候就生成代理类,而运行期则是在程序运行时候创建对应的代理(额,好像是废话)。编译期代理和 A君 这次要做的没多大关联,所以这边不进行阐述,这里 A君 主要了解的是运行期代理,即是大家都耳熟能详的 JDK代理CGLIB代理

JDK代理

JDK代理JDK1.3 之后引入的,这个代理可以根据指定接口,在运行时动态生成一个代理对象。A君 依旧写了个简单的例子,先定义一个简单接口,如下:

public interface IA {
    void invoke();
}

实现类如下:

public class A1 implements IA {
    @Override
    public void invoke() {
        System.out.println("A君在jdk代理搞飞机。。。。");
    }
}

接下来就是关键的部分了,需要实现 InvocationHandler接口 对目标类进行增强,如下:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class AInvocationHandler implements InvocationHandler {
    private IA target;

    public AInvocationHandler(IA ia) {
        this.target = ia;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("进行jdk安全检查##########");
        return method.invoke(target, args);
    }
}

测试也简单,如下:

在这里插入图片描述

JDK代理 设想很美好,也是 Spring 首选的代理方式(如果可以的话),但是 JDK代理 也有其不足,那就是很多类不一定都有实现接口,这时候 JDK代理 就鞭长莫及了

CGLIB代理

CGLIB代理 是基于继承实现的代理,依赖于动态字节码技术,通过覆写父类的方法来进行拓展。A君 也写了个例子,因为 CGLIB代理 依赖于第三方jar包,所以先导包,如下:

<dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
       <version>3.3.0</version> <!-- 或其他你需要的版本 -->
</dependency>

在定义一个简单的目标类,如下:

public class A2 {
    public void invoke() {
        System.out.println("A君在cglib代理搞飞机。。。。");
    }
}

在添加个增强类,需要实现 MethodInterceptor接口,如下:

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class AInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("进行cglib安全检查 ##########");
        return proxy.invokeSuper(obj, args);
    }
}

测试结果,如下:

在这里插入图片描述

六、Target Object(目标对象)

Target Object(目标对象) 指的是被 AOP 增强(或者说,被织入横切关注点)的原始对象

Target Object(目标对象) 这个更不用说,被增强的目标对象。比如:要增强 A.a() ,那么 A 就是 Target Object(目标对象)

在这里插入图片描述

至此,A君 已经大致了解了整个 AOP 工作流程,可以开始着手具体的开发了。对此,A君 还是喜不自胜的,能把不懂的东西弄懂,不也是一种乐趣吗?

在这里插入图片描述

总结

    正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)


http://www.kler.cn/a/350454.html

相关文章:

  • 利用免费GIS工具箱实现高斯泼溅切片,将 PLY 格式转换为 3dtiles
  • InVideo AI技术浅析(五):生成对抗网络
  • AUTOSAR从入门到精通-城市NOA(领航辅助驾驶)
  • Amazon MSK 开启 Public 访问 SASL 配置的方法
  • Go 切片:用法和本质
  • vscode 设置
  • ssh为什么是安全的?ssh怎么预防中间人攻击?
  • 【C语言】数据输出格式控制
  • 2.4 STM32启动过程
  • 操作系统(2) (进程调度/进程调度器类型/三种进程调度/调度算法)
  • Qt-系统线程安全(63)
  • 【系统架构设计师】案例专题二:系统开发基础考点梳理
  • Python 如何使用 Bert 进行中文情感分析
  • Linux网络命令:用于管理和显示网络路由表的工具route详解
  • KAN论文
  • PCL滤波器之面试总结
  • linux----cd和pwd命令
  • 基于yolov5的手机屏幕缺陷检测系统,支持图像、视频和摄像实时检测【pytorch框架、python源码】
  • 前端vue-获取验证码和重新获取验证码倒计时
  • 【数据结构】栈和队列 + 经典算法题
  • C语言 编程练习:解决五个有趣的问题
  • Lumerical学习——分析工具(Analysis tools)
  • 0047__【python打包分发工具】setuptools详解
  • Gin框架操作指南10:服务器与高级功能
  • 移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——6.vector(无习题)
  • React native之全局变量存储AsyncStorage