读不在三更五鼓,功只怕一曝十寒。 —— 郭沫若html
1、导言随着软件世界不断复杂化,传统的OOP(面向对象)建模思路已经不足以很好的处理好开发时所面临的种种挑战,AOP(Aspect Orient Programming)应运而生,它和OOP建模方式并不冲突,它是OOP编程的一种有效补充。java
OOP面向名词领域建模,使用类做为单位来模块化目标系统,而AOP面向动词领域建模,其模块化单位则是Aspect:切面。编程
常见于处理一些具备==横切性质==的系统级服务,例如: 日志管理、事务管理、安全检查缓存、对象池管理等。segmentfault
本文首先介绍了AOP主要概念和常见实现原理,后就动态代理引入SpringAOP,并简单介绍了SpringAOP的原理和其支持的切面,最后作了一个总结。缓存
2、AOP说到AOP不得不说说代理模式。代理模式顾名思义,不直接访问目标对象,而是经过一个代理对象来间接访问目标对象。客户端代码隔着一层代理对象访问目标对象,代理对象能够对访问过程作各类加工、控制。以下类图简洁明了的展现了代理模式的依赖关系、类结构。安全
ProxyPatternDemo类是客户端代码,Image是抽象接口,代理类ProxyImage和目标类ReadlImage都实现Image,客户端代码经过代理对象才能访问目标对象。框架
常见的AOP框架都使用代理的方式给JointPoint下面会介绍添加“行为加强”,例如日志加强、事务加强等,具体能够理解为使用代理模式==透明的==的封装了客户端代码对被代理对象的访问。eclipse
Aspect对标OOP中的class的含义,封装==横切点的逻辑==和横切点逻辑的==做用范围==。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。具体的日志、事务、线程池管理等逻辑都在横切逻辑的Advice(通知)下面有介绍里实现。编程语言
PointCut是用于==限定横切逻辑做用范围==的谓词限定式,其核心是切点表达式。由“切点表达式匹配JointPpoint”的概念是AOP的核心,SpringAOP 默认使用AspectJ切点表达式语言,在实际使用上有如下两种形式,前者能够将切入点表达式抽象出来,在SpringAOP中就是抽象成一个方法:ide
Advice封装具体的横切逻辑。不一样的横切逻辑能够以around、before、after、return、afterThrow等形式体现。Spring AOP以拦截器的方式实现,并维护了一个以链接点jointPoint为中心的拦截器链。
JoinPoint是横切逻辑执行时所关联的点。在JointPoint的“先后左右等”方向上执行Advice逻辑。PointCut表达式指定了JointPoint位置,Advice的体现形式after、before等指定了以什么形式在JointPoint“附近”执行 。
能够简单的理解为被代理对象添加方法或字段。
被代理对象。被AOP框架处理的对象
织入是建立Advice代理对象并将Aspect代理对象和业务逻辑对象链接起来的过程。
织入能够在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。
常见的AOP实现都是基于代理模式的,能够分为:1.静态代理 2.动态代理两种。
静态代理: 在java领域中,最多见的基于静态代理的实现是AspectJ(官网连接),目前已经更新到AspectJ 9了。AspectJ支持编译时、编译后、类加载时织入。具体原理和使用方法暂且略过不表。
动态代理: 动态代理能够分为基于JDK动态代理和CGLIB动态代理。都是在内存中临时为目标对象生成一个代理对象,客户端代码==透明的==经过调用代理对象提供的方法来访问目标对象的方法。
而目前市面上最多见的基于动态代理的AOP实现是SpringAOP。
3、SpringAOPSpringAOP是Spring框架的一个关键组件,其是基于Spring IOC容器而实现,与IOC容器无缝衔接。SpringAOP基于动态代理、纯java方式实现,不须要特定的编译过程,也不须要关心classLoader类加载机制,本质上就是使用JDK动态代理或CGLIB来动态生成一个代理类。
当被代理对象有对应接口全部的要被代理的方法在这个接口里都有声明时,SpringAOP使用JDK动态代理实现,当被代理对象没有对应的接口时,使用CGLIB实现。
CGLIB能够理解为继承被代理类而动态产生一个代理对象,这和传统的代理模式有一点区别传统代理模式有共有接口。因为是使用继承机制来实现AOP,因此SpringAOP不支持对final方法的代理,更不支持对fianl类的代理,由于子类没法对父类方法进行override(重写)。
SpringAOP目前只支持方法级别的AOP支持,我没有实现字段拦截,若是须要对字段访问进行AOP处理的话,能够考虑使用AspectJ语言。
SpringAOP不一样于大多其余的AOP实现,SpringAOP目标不是提供最完整的AOP实现。它的亮点在于AOP和SpringIOC之间的紧密结合(虽然已经很好用了),它非侵入的实现了AOP,不依赖于AspectJ编译器和AspectJ织入器。在SpringAOP中使用普通的bean定义来定义Aspect,SpringAOP和AspectJ是互补的,不是非此即彼的。
下表是摘自这里的SpringAOP和AspectJ的关键区别:
Spring AOP | AspectJ |
---|---|
在纯 Java 中实现 | 使用 Java 编程语言的扩展实现 |
不须要单独的编译过程 | 除非设置 LTW,不然须要 AspectJ 编译器 (ajc) |
只能使用运行时织入 | 运行时织入不可用。支持编译时、编译后和加载时织入 |
功能不强-仅支持方法级编织 | 更强大 - 能够编织字段、方法、构造函数、静态初始值设定项、最终类/方法等......。 |
只能在由 Spring 容器管理的 bean 上实现 | 能够在全部域对象上实现 |
仅支持方法执行切入点 | 支持全部切入点 |
代理是由目标对象建立的, 而且切面应用在这些代理上 | 在执行应用程序以前 (在运行时) 前, 各方面直接在代码中进行织入 |
比 AspectJ 慢多了 | 更好的性能 |
易于学习和应用 | 相对于 Spring AOP 来讲更复杂 |
在JointPoint前执行的Advice,不可阻止JointPoint处方法的执行Around型能够。使用@Before定义,具体源码以下所示。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface Before {String value(); // 结合SpringAOP的命名绑定机制(name binding)使用,支持的PCD有: target、this、argsString argNames() default ""; }复制代码
附: PCD(PointCut Designators 切点指示器),是切点表达式的重要组成部分。
无论被代理方法是正常结束仍是异常结束,都会执行这个Advice。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface After {String value(); // 同上String argNames() default ""; }复制代码
在JointPoint后执行的Advice,当JointPoint处的方法正常执行结束后,会执行这个Advice,若是是异常执行话则不会。使用@AfterReturning定义,具体源码以下所示。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface AfterReturning {String value() default ""; // 和value同一个做用String pointcut() default ""; // 用于声明返回值String returning() default ""; // 同上String argNames() default ""; }复制代码
能够简单的当作带返回值的Afteradvice。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface AfterThrowing {String value() default "";String pointcut() default ""; // 当throwing指定的异常发生时会调用这个AdviceString throwing() default ""; // 同上String argNames() default ""; }复制代码
正常结束不会调用这个Advice,当指定异常发生时会调用这个Advice。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface Around {String value(); // 同上String argNames() default ""; }// 常和ProceedingJoinPoint类结伴使用复制代码
==最强大的Advice==,几乎全部的其余类型的Advice均可以使用环绕通知来实现。但Spring官方建议选用“能实现所需行为的功能最小的通知类型”: 提供最简单的编程模式,减小了出错的可能性。
4、总结通常来讲,咱们能够经过手动编写的形式来实现代理模式,但当须要被代理的对象、类==有不少==时,手动编写的方式就不太合适,AOP就是适合这种有不少横切点的且横切点能够抽象出统一逻辑的一种编程范型。
举一个在实际生产中很常见的例子:
假如某些业务逻辑接口的调用都须要在方法开始前和方法开始以后打日志,可使用AOP切面的方式在全部的业务逻辑方法JoinPoint处定义两个Advice: before和after类型的。