一切福田,不離方寸,從心而覓,感無不通。

面向切面编程

AOP 概述

面向切面编程(aspect-oriented programming),是一种将横切关注点与业务逻辑分离的编程方式。每个横切关注点都集中在一个地方,而不是分散在多处代码中。这样使我们的服务模块更加简洁,因为它们只包含了主要关注点的代码,而次要的功能或者说辅助的功能被转移到切面中了。

aop-1.png

上图表示划分为三个服务模块的应用,每个模块提供相应的服务,而且这些模块都需要类似的辅助功能,例如日志、安全、事务等等。我们并不想在各个模块中写重复的日志、安全、事务的代码,那么就可以使用选用切面这个方案,来解决这个问题。

AOP 术语

aop-2.png
  • advice – 通知
    • 切面的具体行为,即要切入执行的代码

  • pointcut – 切点
    • 通知被应用的具体位置

  • join point – 连接点
    • 程序运行时,能够应用通知的所有点

  • aspect – 切面
    • 什么时候在什么地方做什么事情,是切点和通知的结合

  • target – 目标对象
    • 被切入功能的目标对象

  • introduction – 引入
    • 将新的方法或属性引入到现有的类中

  • weaving – 织入
    • 把切面应用到目标对象并创建新的代理对象的过程

代理模式

代理模式是使用代理对象完成用户请求,屏蔽用户对真实对象访问的一种设计模式。现实生活中,代理人被授权执行当事人的一些事宜,无需当事人出面,从第三方的角度看,他只和代理人通信。而事实上代理人是要有当事人的授权,并且在核心问题上还需要请示当事人。

AOP 就是使用代理模式实现的,其中的代理类就相当于AOP中的切面。

aop-3.png

静态代理

之所以称为静态代理,是因为在程序运行前,代理类就已经存在了。

举个例子

一般艺人都需要助理,来帮他跑腿,演出前谈价格,演出后收钱,只有表演的时候艺人才亲自出马。

  1. 定义一个艺人接口

     
  2. 定义艺人实现类刘德华

     
  3. 编写代理类

     
  4. 运行main方法,将艺人实例传入代理类的构造方法,然后调用代理类的perform()

     
  5. 运行结果

     

静态代理的缺点

假设主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则真实主题和代理类都要修改,不利于系统维护;

动态代理

动态代理是在程序运行时,利用Java反射机制动态的生成代理类的代理模式。

Jdk动态代理

  • JDK的动态代理依靠接口实现
  • 如果类并没有实现接口,则不能使用Jdk的动态代理
  1. 定义图书服务接口

     
  1. 编写图书服务实现类

     
  2. 编写InvocationHandler实现类

     
  3. 运行测试程序

     
  4. 运行结果

     

CGLIB动态代理

JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这时就要使用cglib动态代理了。使用cglib需要依赖cglib的jar,使用maven为例

 

  1. 定义图书服务类

     
  1. 编写MethodInterceptor实现类

     
  1. 运行测试程序

     
  1. 运行结果

     

Spring AOP

Spring 提供了4中 aop 的支持,基于代理的经典SpringAOP,纯POJO切面,@Aspect注解驱动的切面,注入式AspectJ切面。前三种都是SpringAOP实现的变体,SpringAOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。第四种实际是使用AspectJ的解决方案。提供了SpringAOP所不能支持的许多类型的切点。我们可以借助Spring的依赖注入把bean装配进AspectJ切面中。Spring借鉴了AspectJ的切面,以提供注解驱动的AOP。本质上,它依然是Spring基于代理的AOP。

使用@Aspect创建切面类

Audience类使用@Aspect注解进行了标注。该注解表明Audience不仅仅是一个POJO,还是一个切面。Audience类中的方法都使用注解来定义切面的具体行为。Audience有四个方法,定义了一个观众在观看演出时可能会做的事情。在演出之前,观众要就坐(takeSeats())并将手机调至静音状态(silenceCellPhones())。如果演出很精彩的话,观众应该会鼓掌喝彩(applause())。不过,如果演出没有达到观众预期的话,观众会要求退款(demandRefund())。

![pointcut.png](https://upload-images.jianshu.io/upload_images/3568417-544939bd80baedb0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
aspect-annotation.png

Advice的5种注解

注解 通知
@After 通知方法会在目标方法返回或抛异常后调用
@AfterReturning 通知方法会在目标方法返回后调用
@AfterThrowing 通知方法会在目标方法抛异常后调用
@Around 通知方法会将目标方法封装起来
@Before 通知方法会在目标方法调用之前执行

使用@Pointcut声明切点表达式

@Pointcut 声明使用频繁的切点表达式后,在通知注解中引用即可

pointcut.png

总结

  • 面向切面编程是面向对象编程的一个强大的补充
  • 通过切面可以把分散在应用各处的行为放入可重用的模块中
  • 通过注解显示的声明在什么地方应用该行为
  • 有效减少冗余代码,让我们的类关注自身的主要功能
  • 动态代理模式和静态代理模式
  • 通过使用@Aspect注解和简单的配置,可以很简单的在Spring中装配advice和pointcut

参考文献

  • Spring 实战(第4版)
spring-in-action.png
  • Head First 设计模式
head-first-design-pattern.png

 

from:https://www.jianshu.com/p/be6cb39dbeb7