我们的程序从编写到执行,单个模块一般都是从上到下、垂直、连续的。
AOP是一种“横切”技术,能够在合适的地方“拦腰截断”、插入一些“代码”,使得原有功能进行增强。
为什么需要AOP
当我们要进行一些日志记录、权限控制、性能统计等时,在传统应用程序当中我们可能在需要的对象或方法中进行,而且比如权限控制、性能统计大部分是重复的,这样代码中就存在大量重复代码,即使有人说我把通用部分提取出来,那必然存在调用还是存在重复,像性能统计我们可能只是在必要时才进行,在诊断完毕后要删除这些代码;还有日志记录,比如记录一些方法访问日志、数据访问日志等等,这些都会渗透到各个要访问方法中;还有权限控制,必须在方法执行开始进行审核,想想这些是多么可怕而且是多么无聊的工作。如果采用Spring,这些日志记录、权限控制、性能统计从业务逻辑中分离出来,通过Spring支持的面向切面编程,在需要这些功能的地方动态添加这些功能,无需渗透到各个需要的方法或对象中;有人可能说了,我们可以使用“代理设计模式”或“包装器设计模式”,你可以使用这些,但还是需要通过编程方式来创建代理对象,还是要耦合这些代理对象,而采用Spring 面向切面编程能提供一种更好的方式来完成上述功能,一般通过配置方式,而且不需要在现有代码中添加任何额外代码,现有代码专注业务逻辑。所以,Spring AOP面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制。
AOP全名Aspect-Oriented Programming,中文直译为面向切面(方面)编程,当前已经成为一种比较成熟的编程思想,可以用来很好的解决应用系统中分布于各个模块的交叉关注点问题。在轻量级的J2EE中应用开发中,使用AOP来灵活处理一些具有横切性质的系统级服务,如事务处理、安全检查、缓存、对象池管理等,已经成为一种非常适用的解决方案。
AOP主要是它以横截面的方式插入到主流程中。
AOP能做什么?
使用AOP可以做的事情有很多。
- 性能监控,在方法调用前后记录调用时间,方法执行太长或超时报警。
- 缓存代理,等方法执行结束、缓存某方法的返回值,下次执行该方法时,直接从缓存里获取。
- 软件破解,使用AOP修改软件的验证类的判断逻辑。
- 记录日志,在方法执行前后记录系统日志。
- 工作流系统,工作流系统需要将业务代码和流程引擎代码混合在一起执行,那么我们可以使用AOP将其分离,并动态挂接业务。
- 权限验证,方法执行前验证是否有权限执行当前方法,没有则抛出没有权限执行异常,由业务代码捕捉。
- 数据库事务管理,在方法执行前后,开启、提交/回滚事务。
AOP其实就是从中划分出来了一个切面,然后在这个切面里面插入一些“增强”,最后产生一个增加了新功能的代理对象,注意,是代理对象,这是Spring AOP实现的基础。这个代理对象只不过比原始对象(Bean)多了一些功能而已,比如Bean预处理,Bean后处理,异常处理等。 AOP代理的目的就是将切面织入到目标对象。
在上面的举例中,很多次提到“在方法执行前、执行后”,用AOP进行增加了一些新功能。这些新功能就是AOP中的增强(Advice)
,很多资料也翻译成通知。
“在方法执行前、执行后”,可以看作是AOP的连接点(Join point)
,连接点是程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。一个连接点总表示一个方法的执行。
“等方法执行结束、缓存某方法的返回值”,这里的“某方法”,其实就是切入点(Pointcut)
,切入点是从连接点中选择的一个或多个,即切入点是特定的某些连接点。就拿缓存代理为例,也只有某些频繁执行的查询方法,适合把结果缓存起来,供以后快速访问。
切入点和增强(Advice)的连接者是切面(Aspect)
。切入点和增强(Advice)共同定义了关于切面的全部内容,它是什么时候,在何时、何处完成新功能。
Spring提供了在特定的切入点上,进行增加新功能的2种方式:引入(Introduction)、织入(Weaving)。详细内容继续看下节AOP重要概念。
AOP重要概念
描述AOP常用的一些术语有通知(Adivce)、切点(Pointcut)、连接点(Join point)、切面(Aspect)、引入(Introduction)、织入(Weaving)、增强/通知(Advice)等。
Spring提供了在特定的切入点上,进行增加新功能的2种方式:引入(Introduction)、织入(Weaving)。
引入(Introduction,有的翻译成引介):是指给一个现有类添加方法或字段属性,引介还可以在不改变现有类代码的情况下,让现有的Java类实现新的接口,或者为其指定一个父类从而实现多重继承。相对于增强(Advice)可以动态改变程序的功能或流程来说,引介(Introduction)则用来改变一个类的静态结构。比如我们可以让一个现有类实现java.lang.Cloneable接口,从而可以通过clone()方法复制这个类的实例。
织入(Weaving):组装切面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
AOP框架种类
AspectJ: 对java进行了扩展,形成一个功能非常强大、灵活、实用的AOP语言。AspectJ在java的基础上,加入一些AOP相关的关键字、语法结构形成一门AOP语言,其编译出来的程序是普通的Java字节码,因此,可以运行于任何Java平台,AspectJ被誉为AOP领域的急先锋。前身是AspectWerkz。
JBoss-AOP:JBoos公司开发的基于方法拦截及源码级数据的AOP实现框架,最开始属于JBoos服务器的一部分,可以脱离JBoos单独作为一个AOP框架使用。
Spring-AOP:Spring框架中也提供了一个AOP实现,使用基于代理及拦截器的机制,与Spring IOC容器融入一体的AOP框架。Spring AOP采用运行时织入方式,使得可以在基于Spring框架的应用程序中使用各种声明式系统级服务。
它们都遵循AOP联盟规范。
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。
AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。
Spring对AOP的支持局限于方法拦截。如果AOP需求超过了简单的方法拦截范畴,那么应该考虑在ASpectJ里实现切面,利用Spring的IOC把Spring Bean注入到ASpectJ切面中。
Spring AOP及JBoos AOP实现都是采用拦截器来实现的。拦截器是用来实现对连接点进行拦截,从而在连接点前或后加入自定义的切面模块功能。在大多数JAVA的AOP框架实现中,都是使用拦截器来实现字段访问及方法调用的拦截(interception)。所有作用于同一个连接点的多个拦截器组成一个连接器链(interceptor chain),链接上的每个拦截器通常会调用下一个拦截器。
Spring AOP配置&使用方式
基于XML Schema的AOP
基于Schema的AOP从Spring2.0之后通过“aop”命名空间来定义切面、切入点及声明通知。
在Spring配置文件中,所有AOP相关定义必须放在<aop:config>标签下,该标签下可以有<aop:pointcut>、<aop:advisor>、<aop:aspect>标签,配置顺序不可变。
- <aop:pointcut>:用来定义切入点,该切入点可以重用;
- <aop:advisor>:用来定义只有一个通知和一个切入点的切面;
- <aop:aspect>:用来定义切面,该切面可以包含多个切入点和通知,而且标签内部的通知和切入点定义是无序的;和advisor的区别就在此,advisor只包含一个通知和一个切入点。
注解方式AOP
Spring除了支持Schema方式配置AOP,还支持注解方式:使用@AspectJ风格的切面声明。
<aop:aspectj-autoproxy/>
开启AspectJ方式aop,这样Spring就能发现@AspectJ风格的切面并且将切面应用到目标对象。