AOP(Aspect Oriented Programming)面向切面编程,简单来说就是实现横切(crossing)的工具。在一个程序的很多部分重复使用的某一块逻辑我们可以将其抽离出来作为一个切面,重构为一个独立的类,通过声明式配置在那些组件的方法调用前后织入这个切面。AOP将作为OOP的一种辅助,使得代码更加整洁灵活、逻辑更加清晰。
AOP的种类:静态和动态。静态AOP是指横切的逻辑是在编译期间织入到程序中去的,如果要修改横切就必须重新编译代码。动态AOP是在运行时织入程序中去的。Spring AOP框架使用动态代理技术来实现,包括了JDK动态代理和CGLIB动态代理两种方式。
JDK动态代理和CGLIB动态代理—
JDK自带的动态代理使用java.lang.reflect.Proxy类,核心方法是getProxyClass(ClassLoader, Class<?> interfaces)。另外构造Proxy的时候需要绑定一个实现了InvocationHandler接口的实例。而InvocationHandler唯一需要实现的方法就是invoke(Object proxy,Method method, Object[] args)。整个流程是Proxy会根据传入需要代理的类的interfaces构造一个代理类对象proxy,而针对这个proxy调用接口中的任何方法都会通过触发InvocationHanlder实例的invoke方法返回真实的调用结果。关于代理对象如何形成这里不讨论,笔者会以后专门总结。JDK动态代理需要被代理的对象实现某些接口并职能代理这些接口中的方法。
CGLIB动态代理使用CGLIB包,这是一个强大的高性能字节码生成包,底层使用字节码处理框架ASM来转换字节码生成新类。除了ASM,脚本语言如Groovy和BeanShell也使用ASM生成Java代码。本质上,CGLIB是通过生成代理类的一个子类覆盖代理类的所有非final方法,并且还设置了callback。对代理类的每个方法调用会转变成用户定义的拦截方法,比JDK的动态代理要快一些。核心类是Enhancer,一般通过它的setSuperClass(targetClass)和setCallback(final int model)来配置代理类,最后通过enhancer.create()方法开始产生新类的字节码。与JDK动态代理相比,CGLIB没有被代理类必须实现接口的限制。
AOP的几个核心概念—
联结点(JointPoint):定义在程序的哪个位置,织入逻辑
通知(Advice):应该在联结点织入的代码
方面(Aspect):通知和切入点的组合叫做方面,在Spring AOP里面对应的类是Advisor
切入点(Pointcut):织入的代码在什么情况下将被执行,定义各种情况(方法调用是最基本的一种)
引入(Introduction):通过引入,可以在一个对象中加入新的方法或者属性、以改变它的结构,甚至增加实现一个接口的逻辑
Spring AOP各组件—(强调:Spring AOP只支持方法拦截的切入点)
Spring中5种Advice
MethodBeforeAdvice (前置通知):在联结点之前自定义的操作
AfterReturningAdvice (后置通知):在联结点之后执行的操作
MethodInterceptor (包围通知):模仿AOP联盟的“方法拦截”标准,包装一个方法的调用前后
ThrowAdvice (抛出通知):仅当方法调用调用抛出一个异常才执行
IntroductionInterceptor (引入):特殊拦截器,为了实现引入功能
Spring提供的7种Pointcut
ComposablePointcut (可组合切入点):通过union()和intersection()组合操作两个或两个以上的切入点
ControlFlowPointcut (流程切入点):匹配另一个方法流程中包含的所有方法(直接或间接调用)
DynamicMethodMatcherPointcut (动态方法匹配器切入点):提供动态切入点的基类
JdkRegexpMethodPointcut (JDK正则表达式切入点):支持JDK1.4以上版本正则表达式匹配的方法
NameMatchMethodPointcut (名称匹配器方法切入点):根据一个方法名列表匹配
Perl5RegexpMethodPointcut (Perl5正则表达式切入点):支持Perl5的正则表达式语法
StaticMethodMatcherPointcut (静态方法匹配器切入点):提供静态切入点的基类
Spring AOP提供的几种Advisor (Spring AOP通过Advisor关联Pointcut和Advice)
NameMatchMethodPointcutAdvisor:最基本的PointcutAdvisor,用以提供Spring中静态的Pointcut实例
RegexpMethodPointcutAdvisor:支持用正则表达式来编写Pointcut表达式
DefaultPointcutAdvisor:典型的最简单组件,结合一个Pointcut和一个Advice的Advisor实例
DefaultIntroductionAdvisor:特别针对引入而提供的Advisor实例
Spring还提供自动代理建立者,比如DefaultAdvisorAutoProxyCreator,只要在定义文件中加上对它的设置,在Bean配置文件读取完事之后DefaultAdvisorAutoProxyCreator会自动搜索所有的Advisor,自动将Advisor应用至能匹配的Pointcut目标上。
下面举例说明AOP的用法,硬编码的情况下完全可以不通过配置文件实现各种组件的关联,但一般在大工程里都是通过声明式配置文件方式来实现AOP的。在Spring2.0也引入了<aop>标签和基于Annotationde的方式使得配置AOP更加便利。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="logAdvisor" class="com.business.aop.advisor.LogAdvisor"/> <bean id="sellService" class="com.business.service.sellService"/> <bean id="buyService" class="com.business.service.buyService"/> <aop:config> <aop:aspect id="logAspect" ref="logAdvisor"> <aop:pointcut id="businessService" expression="execution (*.business.service.*.*(...))"/> <aop:after ref="businessService" method="sell"/> <aop:before ref="businessService" method="buy"/> </aop:aspect> </aop:config> </beans>
Spring AOP是Spring很强大的功能,如果合理利用,可以对大型工程的代码管理和流程控制上起到非常好的效果。实际上在Spring内部已经大量使用了AOP技术,比如Spring的远程调用支持里,还有Spring的事务管理框架里。我们学习AOP重在体会这种思想,至少在学习Spring其它方面的特性时能够分辨出哪些是明显的AOP部分,这也能有助于深入理解其它方面的特性。
Spring AOP只是实现了AOP联盟的全部AOP技术的一小部分,但是根据帕累托法则已经能满足80%的需求,这也是Spring的设计思想之一,力求简单灵活轻量级无侵入,不提倡EJB时代的为小部分需求集成大规模架构。