背景
AOP,面向切面编程。
1.概念
上面有pmonitor性能监视代码,transManager事务管理代码和业务代码。将业务类看成一段圆木,将createForum()方法
看成圆木的一截,如下所示:
我们无法通过抽象父类的方式消除如上所示的重复性横切代码,因为这些横切逻辑依附在业务类方法的流程中。AOP通过
横向抽取机制为这类无法通过纵向继承体系进行抽象的重复性代码提供了解决方案。
AOP希望将这些分散在各个业务逻辑代码中的相同代码通过横向切割的方式抽取到一个独立的模块中,还业务逻辑类
一个清新的世界。但是如何将这些独立的逻辑融合到业务逻辑中以完成和原来一样的业务流程,是AOP要解决的主要
问题。
2.AOP术语
(1)连接点(Joinpoint):由两个信息确定,一是用方法表示的程序执行点;二是用相对位置表示的方位。如Test.foo()方法
执行前的的连接点,执行点为Test.foo(),方位为该方法执行前的位置。Spring使用切点对执行点进行定位,而方位则在
增强类型中定义。Spring 仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时及方法调用前后这
些程序执行点织入增强;
(2)切点(Pointcut):在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点
的查询条件,Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的执行点。切点只定位到某个方法上,
如果希望定位到具体的连接点上,还需要提供方位信息。
(3)增强(Advice):是织入目标类连接点上的一段程序代码,还拥有执行点的方位。结合执行点的方位信息和切点信息,就
可以找到特定的连接。Spring提供的增强接口都是带方位名的,如BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等;
所以只有结合切点和怎强,才能确定特定的连接点并实施增强逻辑。
(4)目标对象(Target):增强逻辑的织入目标类。
(5)引介(Introduction):一种特殊的增强,它为类添加一些属性和方法。
(6)织入(Weaving):将增强添加到目标类的具体连接点上的过程。AOP有三种织入方式:编译期织入,要求使用特殊的Java
编译器;类装载期织入,要求使用特殊的类装载器;动态代理织入,在运行期为目标类添加增强生成子类的方式。Spring采
用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
(7)代理(Proxy):一个类被AOP织入增强后,就产生了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理
方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以可以采用与原类相同的方式调用代理类。
(8)切面(Aspect):由切点和增强(引介)组成,它既包括横切逻辑的定义,也包括连接点的定义。Spring AOP就是负责实施切
面的框架,它将切面所定义的横切逻辑织入切面所指定的连接点中。
AOP的工作重心在于如何将增强应用于目标对象的连接点上:第一,如何通过切点和增强定位到连接点上;第二,如何在增强
中编写切面的代码。
3.AOP实现者
(1)AspectJ (2)AspectWerkz (3)JBoss AOP (4)Spring AOP
4.基础知识
ThreadLocal可以将非线程安全类改造为线程安全类。
Spring AOP使用动态代理技术在运行期织入增强的代码。使用了两种代理机制:一种是基于JDK的动态代理;另一种是基于
CGLib的动态代理。因为JDK只提供接口代理,不支持类的代理,所以才需要CGLib的动态代理。
(1)JDK动态代理:主要涉及java.lang.reflect包中的Proxy和InvocationHandler类。我们可以通过实现InvocationHandler定义横
切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。而Proxy利用InvocationHandler动态
创建一个符合某一接口的实例,生成目标类的代理对象。
(2)CGLib:采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织
入横切逻辑。
CGLib所创建的动态代理对象的性能比JDK所创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却
比JDK动态代理多。对于singleton的代理对象比较适合CGLib动态代理技术,反之则适合采用JDK动态代理技术。
Spring AOP通过Pointcut(切点)指定在哪些类的哪些方法上织入横切逻辑,通过advice(增强)描述横切逻辑和方法的具体织
入点(方法前、后、两端)。此外,Spring通过Advisor(切面)将Pointcut和Advice组装起来。
5.创建增强
(1)增强接口继承关系:
前置增强:MethodBeforeAdvice 后置增强:AfterReturningAdvice 环绕增强:MethodInterceptor
异常抛出增强:ThrowsAdvice 引介增强:IntroductionInterceptor
(2)ProxyFactory:使用JDK或CGLib动态代理技术将增强应用到目标类中。Spring定义了AopProxy接口:
如果通过ProxyFactory的setInterfaces(Class[] interfaces)方法指定目标接口进行代理,则ProxyFactory使用
JdkDynamicAopProxy;如果针对类的代理,则使用Cglib2AopProxy。另外,可以通过ProxyFactory的setOptimize(true)
方法让ProxyFactory启动优化代理方式,这样,针对接口的代理也会使用Cglib2AopProxy。
6.创建切面
(1)Spring通过Pointcut接口描述切点:
Spring支持两种方法匹配器:静态方法匹配器,仅对方法名签名(包括方法名和入参类型及顺序)进行匹配;而
动态方法匹配器会在运行期检查方法入参的值。静态匹配仅会判别一次,而动态匹配每次调用方法都必须判断。
方法匹配器的类型由isRuntime()方法的放回值决定,返回false表示静态方法匹配器,返回true表示动态方法匹配器。
(2)切点类型:静态方法切点,动态方法切点,注解切点,表达式切点,流程切点,复合切点。其中注解和表达式切
点,使用AspectJ的切点表达式语言。
(3)切面类型: 切面继承关系如下所示:
Advisor:代表一般切面 ; PointAdvisor:代表具有切点的切面;IntroductionAdvisor:代表引介切面。
PointcutAdvisor的主要实现类体系如下:
DefaultPointcutAdvisor:可以通过任意Pointcut和Advice定义一个切面,唯一不支持引介的切面类型,可以通过扩展
该类实现自定义切面。
NameMatchMethodPointcutAdvisor:可以定义按方法名定义切点的切面。
RegexMethodPointcutAdvisor:按正则表达式匹配方法名进行切点定义的切面。
StaticMethodMatcherPointcutAdvisor:静态方法匹配器切点定义的切面。
AspectJExpressionPointcutAdvisor:用于AspectJ切点表达式定义切点的切面。
AspectJPointcutAdvisor:用于AspectJ语法定义切点的切面。
对于动态切点,Spring采用这样的机制:在创建代理时对目标类的每个连接点使用静态切点检查,如果仅通过静态切点
检查就可以知道连接点是不匹配的,则在运行时就不再进行动态检查;如果静态切点检查是匹配的,则在运行时才进行
动态切点检查。所以在动态切点类中定义静态切点检查的方法可以避免不必要的动态检查操作,提高运行效率。
(4)流程切面:由DefaultPointcutAdvisor和ControlFlowPointcut实现。流程切点代表由某个方法直接或间接发起调用的其
他方法。
(5)复合切点切面:CompasablePointcut把两个切点组合起来,通过切点的复合运算表示连接点。
(6)引介切面:引介切面的类继承关系:
7.自动创建代理
Spring使用BeanPostProcessor自动完成自动代理的工作。BeanPostProcessor是Bean后处理器。
基于BeanPostProcessor的自动代理创建器的实现类,将根据一些规则自动在容器实例化Bean时为匹配的Bean
生成代理实例。
代理创建器可以分为三类:
基于Bean配置名规则的自动代理创建器;
基于Advisor匹配机制的自动代理创建器;
基于Bean中AspectJ注解标签的自动代理创建器。
DefaultAdvisorAutoProxyCreator能够扫描容器中的Advisor,并将Advisor自动织入匹配的目标Bean中。
8.AOP无法增强疑难问题剖析
具体看书
总结
学习了面向切面的编程,感觉对于切面逻辑的简化是一个质的提升,非常棒。