菜鸟之路——Spring MVC(十四)Spring AOP

     AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程。可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。

  我们现在做的一些非业务,如:日志、事务、安全等都会写在业务代码中(也即是说,这些非业务类横切于业务类),但这些代码往往是重复,复制——粘贴式的代码会给程序的维护带来不便,AOP就实现了把这些业务需求与系统需求分开来做。这种解决的方式也称代理机制。

  一、什么是 AOP

        AOP, 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角。在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)。

  切面(Aspect)

  官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”。aspect 由 pointcount 和 advice 组成, 它既包含了横切逻辑的定义, 也包括了连接点的定义. Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中。
  AOP的工作重心在于如何将增强织入目标对象的连接点上, 这里包含两个工作:
  1、如何通过 pointcut 和 advice 定位到特定的 joinpoint 上
  2、如何在 advice 中编写切面代码
  可以简单地认为, 使用 @Aspect 注解的类就是切面。“切面”在ApplicationContext中<aop:aspect>来配置。

  增强(advice)

  “切面”对于某个“连接点”所产生的动作。一个“切面”可以包含多个“Advice”。也就是说由 aspect 添加到特定的 join point(即满足 point cut 规则的 join point) 的一段代码。
  许多 AOP框架, 包括 Spring AOP, 会将 advice 模拟为一个拦截器(interceptor), 并且在 join point 上维护多个 advice, 进行层层拦截。
  例如 HTTP 鉴权的实现, 我们可以为每个使用 RequestMapping 标注的方法织入 advice, 当 HTTP 请求到来时, 首先进入到 advice 代码中, 在这里我们可以分析这个 HTTP 请求是否有相应的权限, 如果有, 则执行 Controller, 如果没有, 则抛出异常。这里的 advice 就扮演着鉴权拦截器的角色了。

  Advice的类型:

  前置通知(Before advice):在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。
  后置通知(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。
  返回后通知(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
  环绕通知(Around advice):包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。
  抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。

  注:可以将多个通知应用到一个目标对象上,即可以将多个切面织入到同一目标对象。

  连接点(join point)

  程序执行过程中的某一行为,是程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理。需要注意的是:在 Spring AOP 中, join point 总是方法的执行点, 即只有方法连接点。

  切点(point cut)

  匹配连接点的断言,在AOP中通知和一个切入点表达式关联。Advice 是和特定的 point cut 关联的, 并且在 point cut 相匹配的 join point 中执行。在 Spring 中, 所有的方法都可以认为是 joinpoint, 但是我们并不希望在所有的方法上都添加 Advice, 而 pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice。

  目标对象(Target Object)

  被一个或者多个切面所通知的对象,是织入 advice 的目标对象。 目标对象也被称为 advised object,因为 Spring AOP 使用运行时代理的方式来实现 aspect, 因此 adviced object 总是一个代理对象(proxied object)。注意, adviced object 指的不是原来的类, 而是织入 advice 后所产生的代理类。

  AOP proxy

  在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情况下,TargetObject实现了接口时,则采用JDK动态代理;反之,如果需要为一个类实现代理,采用CGLIB代理。当一个业务逻辑对象没有实现接口时, 那么Spring AOP 就默认使用 CGLIB 来作为 AOP 代理,即如果我们需要为一个方法织入 advice, 但是这个方法不是一个接口所提供的方法, 则此时 Spring AOP 会使用 CGLIB 来实现动态代理。鉴于此, Spring AOP 建议基于接口编程, 对接口进行 AOP 而不是类。如强制使用CGLIB代理,需要将 <aop:config>的 proxy-target-class属性设为true。

  织入(Weaving)

  将 aspect 和其他对象连接起来, 并创建 adviced object 的过程。根据不同的实现技术, AOP织入有三种方式:
  1、编译器织入, 这要求有特殊的Java编译器。
  2、类装载期织入, 这需要有特殊的类装载器。
  3、动态代理织入, 在运行期为目标类添加增强(Advice)生成子类的方式。
  Spring 采用动态代理织入, 而AspectJ采用编译器织入和类装载期织入.

  彻底理解 aspect, join point, point cut, advice

  关于join point 和 point cut 的区别:
  在 Spring AOP 中, 所有的方法执行都是 join point。 而 point cut 是一个描述信息, 它修饰的是 join point, 通过 point cut, 我们就可以确定哪些 join point 可以被织入 Advice。 因此 join point 和 point cut 本质上就是两个不同纬度上的东西。即advice 是在 join point 上执行的, 而 point cut 规定了哪些 join point 可以执行哪些 advice。

  一个形象的比喻来描绘 AOP 中 aspect, jointpoint, pointcut 与 advice 之间的关系:

  让我们来假设一下, 从前有一个叫爪哇的小县城, 在一个月黑风高的晚上, 这个县城中发生了命案. 作案的凶手十分狡猾, 现场没有留下什么有价值的线索. 不过万幸的是, 刚从隔壁回来的老王恰好在这时候无意中发现了凶手行凶的过程, 但是由于天色已晚, 加上凶手蒙着面, 老王并没有看清凶手的面目, 只知道凶手是个男性, 身高约七尺五寸. 爪哇县的县令根据老王的描述, 对守门的士兵下命令说: 凡是发现有身高七尺五寸的男性, 都要抓过来审问. 士兵当然不敢违背县令的命令, 只好把进出城的所有符合条件的人都抓了起来.
  来让我们看一下上面的一个小故事和 AOP 到底有什么对应关系。
首先我们知道, 在 Spring AOP 中 join point 指代的是所有方法的执行点, 而 point cut 是一个描述信息, 它修饰的是 join point, 通过 point cut, 我们就可以确定哪些 join point 可以被织入 Advice。对应到我们在上面举的例子, 我们可以做一个简单的类比, join point 就相当于 爪哇的小县城里的百姓, point cut 就相当于 老王所做的指控, 即凶手是个男性, 身高约七尺五寸, 而 advice 则是县令所下的命令凡是发现有身高七尺五寸的男性, 都要抓过来审问。
  为什么可以这样类比呢?
  join point --> 爪哇的小县城里的百姓: 因为根据定义, join point 是所有可能被织入 advice 的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 join point。而在我们上面的例子中, 命案发生在小县城中, 按理说在此县城中的所有人都有可能是嫌疑人。
  point cut --> 男性, 身高约七尺五寸: 我们知道, 所有的方法(joint point) 都可以织入 advice, 但是我们并不希望在所有方法上都织入 advice, 而 pointcut 的作用就是提供一组规则来匹配joinpoint, 给满足规则的 joinpoint 添加 advice。同理, 对于县令来说, 他再昏庸, 也知道不能把县城中的所有百姓都抓起来审问, 而是根据凶手是个男性, 身高约七尺五寸, 把符合条件的人抓起来。在这里 凶手是个男性, 身高约七尺五寸 就是一个修饰谓语, 它限定了凶手的范围, 满足此修饰规则的百姓都是嫌疑人, 都需要抓起来审问。
  advice --> 抓过来审问, advice 是一个动作, 即一段 Java 代码, 这段 Java 代码是作用于 point cut 所限定的那些 join point 上的。同理, 对比到我们的例子中, 抓过来审问 这个动作就是对作用于那些满足 男性, 身高约七尺五寸 的爪哇的小县城里的百姓。
  aspect: aspect 是 point cut 与 advice 的组合, 因此在这里我们就可以类比: "根据老王的线索, 凡是发现有身高七尺五寸的男性, 都要抓过来审问" 这一整个动作可以被认为是一个 aspect。

  或则我们也可以从语法的角度来简单类比一下。我们在学英语时, 经常会接触什么 定语, 被动句 之类的概念, 那么可以做一个不严谨的类比, 即 joinpoint 可以认为是一个 宾语, 而 pointcut 则可以类比为修饰 joinpoint 的定语, 那么整个 aspect 就可以描述为: 满足 pointcut 规则的 joinpoint 会被添加相应的 advice 操作。

  二、怎么使用AOP    

     使用Spring AOP可以基于两种方式,一种是比较方便和强大的注解方式,另一种则是中规中矩的xml配置方式。

   注解方式:

  注解方式使用的是注解@AspectJ 。这是一种使用 Java 注解来实现 AOP 的编码风格。@AspectJ 可以以 XML 的方式或以注解的方式来使用, 不论以哪种方式使能@ASpectJ, 我们都必须保证 aspectjweaver.jar 在 classpath 中。

  使用 Java Configuration 方式使能@AspectJ

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
       使用 XML 方式使能@AspectJ。
<aop:aspectj-autoproxy/>
   使用CGLIB代理:


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值