Welcome everyone

Spring必知必会 面向切面编程

java 汪明鑫 477浏览 2评论

前言

什么是面向切面编程???

传统的编程是纵向编程,面向切面编程是横向编程
对于业务逻辑而言,横向切入的逻辑是用户无感的

应用场景:事务、日志等

 

初学Spring时,切面编程配一堆xml,被各种概念弄的苦不堪言,很蒙蔽

这种疑惑和不解,一直带到现在,

本来有时候好不容易搞明白了,又忘了,难顶,

有必要整理篇文章,搞定AOP

 

这个东西可以说是除了IOC外,面试最爱问的之一了

面试Spring最爱问TOP(我觉得):

1,IOC/DI

2,AOP

3,说下Spring和SpringBoot

4,事务

 

再进阶的问题就是设计模式和源码相关了,等我操磨2年再去研究吧,太难了

 

AOP

Aspect-Oriented Programming   面向切面编程

AOP是OOP的完善和补充

 

 

术语详解

  1. Join point: 程序执行过程中的一个点,比如方法的执行或者异常的捕获。在Spring AOP中,目前所有的Join Point指向一个方法的执行。
  2. Point cut: 简单来说,程序中有许许多多的可以切入的点,就像溪流那么长,但我们通常只需要在我们关注的地方,做我们想做的事情,所以PointCut就是帮我们缩小范围,找到关键位置。
  3. Advice:很多人翻译为通知或者建议,我更倾向于使用“增强”来描述。因为一个Advice就是在原有逻辑上,再做一点额外的工作,类似升级,加强的意思。Spring AOP支持五种不同类型的Advice,分别为
类型 作用
Before 在目标方法被调用前执行
After 在目标方法被调用后执行
AfterReturning 在目标方法成功返回后执行
AfterThrowing 在目标方法抛出异常后执行
Around 包围目标方法,在被调用前,后执行,相当于同时Before & After

 

  • Aspect: 前面的故事中已经提到了叙事三要素,时间(When),地点(Where),做什么(What)。我们看到了,Advice定义了When 和 What,PointCut定义了Where,两者一结合就是一个Aspect。
  • Introduction:上面提到的都是对于方法的增强,除此之外,还有一种“引入增强”,可以给一个类增加额外的属性和方法。(Spring AOP目前仅支持在目标对象上引入一个新的接口,以及对应的接口实现)
  • Target Object: 无需多解释,就是需要被增强的目标对象,Spring的话,基本上都是容器内的Bean,而且Spring AOP是在运行时通过动态代理来实现切面的封装,所以TargetObject最终是一个代理对象。
  • Weaving: 最后提一下Weaving的概念,就是把切面绑定到一个目标对象从而生成新的代理这样一个过程

 

图解AOP

看了这个图我感觉基本都清楚了

 

  • PointCut 是一个描述信息,它修饰的是 JoinPoint ,通过 PointCut ,我们就可以确定哪些 JoinPoint 可以被织入 Advice 。
  • Advice 是在 JoinPoint 上执行的,而 PointCut 规定了哪些 JoinPoint 可以执行哪些 Advice 。

 

 

Weaving ,编织

切面绑定到一个目标对象从而生成新的代理

在 Spring AOP 中,编织在运行时执行,即动态代理。

 

举例说明

举例一加深印象:

我们把业务的逻辑流程想象为一条小溪流水,而上面提到的日志,安全,事务是观察水质,确保水源不被污染的操作;那么我们的辖区范围内有着数量不等的河流,都需要相同的操作来检测水质。我们并不希望断闸截流,这样既繁琐又影响水的向下流动(业务的处理步骤),那么简单的方法是在水流中,舀取一瓶带回实验室,记录所关心的核心指标就可以了。

所以,一个水质检验工人的工作内容是:在河流(Target Object)的流经之处(Joint Point),选择一个合理的取水点(Point Cut),采样部分水质样品并记录下指标(Advice)。

 

举例二加深理解:

看完了上面的理论部分知识, 我相信还是会有不少朋友感觉到 AOP 的概念还是很模糊, 对 AOP 中的各种概念理解的还不是很透彻. 其实这很正常, 因为 AOP 中的概念是在是太多了, 我当时也是花了老大劲才梳理清楚的.

下面我以一个简单的例子来比喻一下 AOP 中 Aspect, Joint point, Pointcut 与 Advice之间的关系.

让我们来假设一下, 从前有一个叫爪哇的小县城, 在一个月黑风高的晚上, 这个县城中发生了命案. 作案的凶手十分狡猾, 现场没有留下什么有价值的线索. 不过万幸的是, 刚从隔壁回来的老王恰好在这时候无意中发现了凶手行凶的过程, 但是由于天色已晚, 加上凶手蒙着面, 老王并没有看清凶手的面目, 只知道凶手是个男性, 身高约七尺五寸. 爪哇县的县令根据老王的描述, 对守门的士兵下命令说: 凡是发现有身高七尺五寸的男性, 都要抓过来审问. 士兵当然不敢违背县令的命令, 只好把进出城的所有符合条件的人都抓了起来.

来让我们看一下上面的一个小故事和 AOP 到底有什么对应关系.

首先我们知道, 在 Spring AOP 中 Joint point 指代的是所有方法的执行点, 而 point cut 是一个描述信息, 它修饰的是 Joint point, 通过 point cut, 我们就可以确定哪些 Joint point 可以被织入 Advice. 对应到我们在上面举的例子, 我们可以做一个简单的类比, Joint point 就相当于 爪哇的小县城里的百姓,pointcut 就相当于 老王所做的指控, 即凶手是个男性, 身高约七尺五寸, 而 Advice 则是施加在符合老王所描述的嫌疑人的动作: 抓过来审问.

为什么可以这样类比呢?

Joint point : 爪哇的小县城里的百姓: 因为根据定义, Joint point 是所有可能被织入 Advice 的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 Joint point. 而在我们上面的例子中, 命案发生在小县城中, 按理说在此县城中的所有人都有可能是嫌疑人.

Pointcut :男性, 身高约七尺五寸: 我们知道, 所有的方法(joint point) 都可以织入 Advice, 但是我们并不希望在所有方法上都织入 Advice, 而 Pointcut 的作用就是提供一组规则来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice. 同理, 对于县令来说, 他再昏庸, 也知道不能把县城中的所有百姓都抓起来审问, 而是根据凶手是个男性, 身高约七尺五寸, 把符合条件的人抓起来. 在这里 凶手是个男性, 身高约七尺五寸 就是一个修饰谓语, 它限定了凶手的范围, 满足此修饰规则的百姓都是嫌疑人, 都需要抓起来审问.

Advice :抓过来审问, Advice 是一个动作, 即一段 Java 代码, 这段 Java 代码是作用于 point cut 所限定的那些 Joint point 上的. 同理, 对比到我们的例子中, 抓过来审问 这个动作就是对作用于那些满足 男性, 身高约七尺五寸 的爪哇的小县城里的百姓.

Aspect::Aspect 是 point cut 与 Advice 的组合, 因此在这里我们就可以类比: “根据老王的线索, 凡是发现有身高七尺五寸的男性, 都要抓过来审问” 这一整个动作可以被认为是一个 Aspect.

 

配置切面编程实例

<!-- 3.事务管理 -->
    <!-- 3.1 事务管理器 -->
    <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    
    <!-- 3.2 事务详情  
        * 增删改:读写;查询:只读
    -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="**" propagation="REQUIRED"/>  
            <tx:method name="add*" />
            <tx:method name="update*"/>
            <tx:method name="delete*"/>
            <tx:method name="find*"  />
        </tx:attributes>
    </tx:advice>
    <!-- 3.3 aop编程  
        * 强制cglib :  proxy-target-class="true"
    -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* pers.wmx.*.service..*.*(..))"/>
    </aop:config>

这是大学做项目时参考网上写的一个事务aop的xml配置

Spring中AOP的配置一般有两种方法,一种是使用上述 <aop:config> 标签在xml中进行配置,

一种是使用注解以及@Aspect风格的配置。

 

目标类使用@Target

切面类使用@Aspect + @Pointcut + Advice(@Before/@After/@Around/@AfterReturning/@AfterThrowing)

 

多个切面执行顺序

 

可以设置多个切面,指定切面的执行顺序,order越小越先执行

 

AOP实现方式

① 静态代理 – 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强。

② 动态代理 – 在运行时在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。目前 Spring 中使用了两种动态代理库:

  • JDK 动态代理
  • CGLIB

 

AOP织入时期

 

方法 作用时机 操作对象 优点 缺点 为了上手,我需要掌握什么?
APT 编译期:还未编译为 class 时 .java 文件 1. 可以织入所有类;2. 编译期代理,减少运行时消耗 1. 需要使用 apt 编译器编译;2. 需要手动拼接代理的代码(可以使用 Javapoet 弥补);3. 生成大量代理类 设计模式和解耦思想的灵活应用
AspectJ 编译期、加载时 .java 文件 功能强大,除了 hook 之外,还可以为目标类添加变量,接口。也有抽象,继承等各种更高级的玩法。 1. 不够轻量级;2. 定义的切点依赖编程语言,无法兼容Lambda语法;3. 无法织入第三方库;4. 会有一些兼容性问题,如:D8、Gradle 4.x等 复杂的语法,但掌握几个简单的,就能实现绝大多数场景
Javassist 编译期:class 还未编译为 dex 时或运行时 class 字节码 1. 减少了生成子类的开销;2. 直接操作修改编译后的字节码,直接绕过了java编译器,所以可以做很多突破限制的事情,例如,跨 dex 引用,解决热修复中 CLASS_ISPREVERIFIED 问题。 运行时加入切面逻辑,产生性能开销。 1. 自定义 Gradle 插件;2. 掌握groovy 语言
ASM 编译期或运行期字节码注入 class 字节码 小巧轻便、性能好,效率比Javassist高 学习成本高 需要熟悉字节码语法,ASM 通过树这种数据结构来表示复杂的字节码结构,并利用 Push 模型来对树进行遍历,在遍历过程中对字节码进行修改。
ASMDEX 编译期和加载时:转化为 .dex 后 Dex 字节码,创建 class 文件 可以织入所有类 学习成本高 需要对 class 文件比较熟悉,编写过程复杂。
DexMaker 同ASMDEX Dex 字节码,创建 dex 文件 同ASMDEX 同ASMDEX 同ASMDEX
Cglib 运行期生成子类拦截方法 字节码 没有接口也可以织入 1. 不能代理被final字段修饰的方法;2. 需要和 dexmaker 结合使用
xposed 运行期hook 能hook自己应用进程的方法,能hook其他应用的方法,能hook系统的方法 依赖三方包的支持,兼容性差,手机需要root
dexposed 运行期hook 只能hook自己应用进程的方法,但无需root 1. 依赖三方包的支持,兼容性差;2. 只能支持 Dalvik 虚拟机
epic 运行期hook 支持 Dalvik 和 Art 虚拟机 只适合在开发调试中使用,碎片化严重有兼容性问题

 

这一块也不是都懂,觉得了解下即可

还待后期了解

转载请注明:汪明鑫的个人博客 » Spring必知必会 面向切面编程

喜欢 (0)

说点什么

2 评论 在 "Spring必知必会 面向切面编程"

提醒
avatar
排序:   最新 | 最旧 | 得票最多
GUEST
游客

大佬,AOP切面编程有什么推荐的学习资料么?

wpDiscuz