2-面向切面编程

Charlie

1. 软件编程方法

  • 面向过程编程 POP
  • 面向对象编程 OOP
  • 面向切面编程 AOP
  • 函数式编程 FP
  • 反应式编程 RP

2. AOP: Aspect Oriented Programming

2.1. 👍横切关注点

image.png

2.1.1. 定义

  • 横切关注点:公共性的功能,但需要嵌入到业务代码的许多地方
    • Cross-cutting concern
    • 例:来了一个请求,需要权限认证。权限认证与业务逻辑无关,且许多地方要用到。

Spring会帮开发者嵌入横切关注点

2.1.2. 类型

  1. 日志
  2. 安全
  3. 事务
  4. 缓存

2.2. AOP的可选方案

以日志为例

  • 继承
    • 让业务类继承日志类
    • 缺点:感知到日志代码存在,强耦合,对业务代码造成了侵入
  • 委托
    • 委托日志对象的引用来调用日志方法
    • 缺点:需要知道日志模块的存在,侵入业务代码

3. 👍AOP术语

  1. 通知 Advice:切面要做什么以及何时做
    • 时间:方法前/后…
  2. 切点 Pointcut:指定在何处切
    • Spring只支持在方法的前后切
    • 切点表达式指定逻辑
  3. 切面 Aspect:Advice和Pointcut的结合
    • 包含切的所有逻辑
  4. 连接点 Join point:方法、字段修改、构造方法
  5. 引入 introduction:引入新的行为和状态
    • 给对象加入新的方法/状态,但不需要实现新的子类。在原来的对象上动态加入新的方法/状态。
  6. 织入 Weaving:切面应用到目标对象的过程

3.1. 通知 Advice

在aspect类的方法上注解

  • @Before
  • @After:不区分是否正常结束
  • @AfterReturning
  • @AfterThrowing
  • @Around:结合前面所有类型

被切方法成功运行之后会执行:After、AfterReturning、Around

3.2. 切点 Pointcut

  • @Pointcut,空方法
  • 可以在不同的Advice的切点表达式中直接重用
    • 消除重复
    • 不是方法调用
  • 相当于对比较复杂的切点表达式进行封装,简单的就可以直接写在Advice里
    image.png

3.3. 👍切面 Aspect

  • java类上加@Aspect注解,是一个单独的类
    • @Aspect // 并不具有 @Component 的效果,不能在扫描时实例化,因此需要添加 @Component 注解或在 JavaConfig 类中主动实例化
  • 包含切的时间、切点和逻辑
  • 定义可重用的切点

==不需要修改业务代码==

3.3.1. 创建步骤

  1. 实现一个java类,在类上加@Aspect注解,定义逻辑和行为。用@Before等注解定义Advice和切点表达式
  2. 在配置类增加注解@EnableAspectJProxy
    • 启用AspectJ代理
  3. 实例化:在配置类中实例化切面类对应的Bean,或者使用@Component

3.3.2. 👍切点表达式/切点指示器

  1. 借助AspectJ切点指示器的语法
  2. 在方法上加@Before(<expression>)@Pointercut(<expression)
  • 可以实现👍
    1. 指定在哪些方法上切入
    2. 获取参数
    3. 限定包路径
    4. 限定bean名称,白名单或黑名单
    5. 限定在特定注解上切入

3.3.2.1. `Pointcut

1
2
3
4
5
@Pointcut(
"execution(* soundsystem.CompactDisc.playTrack( int )) " +
"&& args(trackNumber)") //获取参数
&& within(soundsystem.*) //限定包路径
&& bean(sgtPeppers) //限定bean名称,或者: && !bean(sgtPeppers)
  1. 指定方法execution
  2. 可以指定包/路径within
  3. 可以带参数args
  4. 限定在哪些Bean上植入(可以取反)bean

3.3.2.2. Around

  1. @annotation关键字指定在特定注解上植入
    • 注解可以自己定义,和Spring无关
  • Around
    • 需要一个ProceedingJoinPoint方法参数
    • 对ProceedingJoinPoint方法的调用实际上就是对切点的调用
1
2
3
4
5
6
@Around("@annotation(innerAuth)") //
限定注解
public Object innerAround(ProceedingJoinPoint point, InnerAuth innerAuth) { ... }

@InnerAuth
public R<Boolean> register(@RequestBody SysUser sysUser){...}
  • Around优势
    • 可以自由控制是否调用/调用次数
    • 可以有返回值,灵活性更高

3.3.3. 实现原理

  • 切与不切通过getBean获得的对象类型并不一样。
  • 通过创建代理实现
    • JDK本身就具备代理能力
    • 针对该对象的调用都会转到代理对象

image.png

3.4. 织入 Weaving

切面应用到目标对象的过程。

3.4.1. 织入时机

  1. 编译期: 需要特殊的编译器
  2. 类加载期: 需要类加载器的处理
  3. 运行期: Spring所采纳的方式,使用代理对象,只支持方法级别的连接点

3.4.2. 织入方式

使用代理机制。

image.png

3.5. 引入 👍introduction

引入新的行为和状态。

3.5.1. 引入方式

  1. 创建需要增加的接口和实现类
  2. 新建一个切面类,加@Aspect
  3. 在里面定义一个新增实现类的static接口,加上@DeclareParents注解
  4. 实例化切面类Bean

3.5.2. 例子

image.png

定义切面,为对象增加新的行为。Encoreable接口里包含要引入的新的行为的定义。新的行为引入到原有类中的时候,每个类都会实例化一个新的DefaultEncoreable类,一对一。

切面本身是单实例的,但是实现新行为的类对应的实例和被切入类是一对一的。

  • @DeclareParents
    • value:给实现了这个接口的对象,引入新的行为
    • defaultImpl:实现了需要引入的新行为。在该例子中实现了Encoreable
  • main和测试

image.png

  • 标题: 2-面向切面编程
  • 作者: Charlie
  • 创建于 : 2023-10-10 18:10:00
  • 更新于 : 2024-07-05 12:55:04
  • 链接: https://chillcharlie357.github.io/posts/e2853af6/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论