AOP详解及spring对AOP的支持

      AOP详解及spring对AOP的支持无评论

AOP

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为”横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为”Aspect”,即切面。所谓”切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

下面先说AOP是什么样的思想,我们一步一步慢慢来,先看一下传统程序的流程,比如银行系统会有一个取款流程

我们可以把方框里的流程合为一个,另外系统还会有一个查询余额流程,我们先把这两个流程放到一起:

有没有发现,这个两者有一个相同的验证流程,我们先把它们圈起来再说下一步:

有没有想过可以把这个验证用户的代码是提取出来,不放到主流程里去呢,这就是AOP的作用了,有了AOP,你写代码时不要把这个验证用户步骤写进去,即完全不考虑验证用户,你写完之后,在另我一个地方,写好验证用户的代码,然后告诉Spring你要把这段代码加到哪几个地方,Spring就会帮你加过去,而不要你自己Copy过去,这里还是两个地方,如果你有多个控制流呢,这个写代码的方法可以大大减少你的时间,不过AOP的目的不是这样,这只是一个“副作用”,真正目的是,你写代码的时候,事先只需考虑主流程,而不用考虑那些不重要的流程,懂C的都知道,良好的风格要求在函数起始处验证参数,如果在C上可以用AOP,就可以先不管校验参数的问题,事后使用AOP就可以隔山打牛的给所有函数一次性加入校验代码,而你只需要写一次校验代码。不知道C的没关系,举一个通用的例子,经常在debug的时候要打log吧,你也可以写好主要代码之后,把打log的代码写到另一个单独的地方,然后命令AOP把你的代码加过去,注意AOP不会把代码加到源文件里,但是它会正确的影响最终的机器代码。

现在大概明白了AOP了吗,我们来理一下头绪,上面那个方框像不像个平面,你可以把它当块板子,这块板子插入一些控制流程,这块板子就可以当成是AOP中的一个切面。所以AOP的本质是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面,这句话应该好理解吧,我们把纵向流程画成一条直线,然把相同的部分以绿色突出,如下图左,而AOP相当于把相同的地方连一条横线,如下图右,这个图没画好,大家明白意思就行。

 

这个验证用户这个子流程就成了一个条线,也可以理解成一个切面,aspect的意思我认为是方面,你用什么实物去类比,只要你能理解都可以。这里的切面只插了两三个流程,如果其它流程也需要这个子流程,也可以插到其它地方去。

Spring对AOP的支持

Spring AOP中使用了两种动态代理,一种是JDK的动态代理,一种CGLIB的动态代理。JDK的动态代理必须指定接口,这些接口都是已经被代理对象实现了的;而CGLIB代理则不需要指定接口。

1.1 JDK动态代理

JDK的动态代理网上有很多资料,这里只说我自己的理解。

JDK动态代理必须实现InvocationHandler接口,然后通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)获得动态代理对象。

 

所有方法都会被导向调用InvocationHandler接口的唯一的方法invoke,这个就是我们在被代理对象前后插入相关逻辑的地方。

1.2 CGLIB动态代理

1.2.1 CGLIB的代理用法

使用CGLib动态代理,被代理类不需要强制实现接口。

CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

intercept方法中的参数:Object obj为由CGLib动态生成的代理类实例,Method method为被代理类中的方法引用,Object[] args为参数值列表,MethodProxy为生成的代理类对方法的代理引用。

1.2.2 CGLIB的过滤功能

CallbackFilter可以实现不同的方法使用不同的回调方法,用于分发不同的拦截器。

2  Spring AOP中的动态代理机制

Spring AOP中的代理根据被代理对象是否实现了接口选择不同的生成代理对象的方式,即第1部分中的两个情况。

2.1 JdkDynamicAopProxy

如果被代理对象实现了需要被代理的接口,则使用JDK的动态代理,在Spring AOP中对应的包装类为JdkDynamicAopProxy。

JdkDynamicAopProxy类实现了InvocationHandler接口,将被代理对象和拦截器作为参数传入,然后生成代理对象。

当代理对象被调用时,JdkDynamicAopProxy的invoke方法作为Proxy对象的回调函数而被触发,从而通过invoke的具体实现,来完成对目标对象方法调用的拦截或者说功能增强的工作。

在invoke方法中,设置了包括获取目标对象、拦截器链,同时把这些对象作为输入,创建了ReflectiveMethodInvocation对象,通过这个ReflectiveMethodInvocation对象来完成对AOP功能实现的封装。在这个invoke方法中,包含了一个完整的拦截器链对目标对象的拦截过程,比如获得拦截器链并对其中的拦截器进行配置,逐个运行拦截器链里的拦截增强,直到最后对目标对象方法的运行。具体可以看源代码。

View Code

2.2 CglibAopProxy

Cglib2AopProxy的intercept回调方法的实现和JdkDynamicAopProxy的回调实现是非常类似的,只是在Cglib2AopProxy中构造的是CglibMethodInvocation对象来完成拦截器链的调用,而在JdkDynamicAopProxy中是通过构造ReflectiveMethodInvocation对象来完成这个功能的。

Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:

1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了

2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB

AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:

1、定义普通业务组件

2、定义切入点,一个切入点可能横切多个业务组件

3、定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作

所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。

 

 

 

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注