锁定老帖子 主题:对Spring AOP框架实现的结构分析
精华帖 (1) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-09-13
从实现的角度来认识SpringAOP框架。 观察的角度: 从外部接口,内部实现,组成部分,执行过程四个方面来认识SpringAOP框架。 本文的风格: 首先列出AOP的基本概念; 其次介绍框架所涉及到的核心组件列表,组件之间的结构关系图; 然后细化结构图中的部分; 接下来是一个简单的sample; 最后是后记部分。 注: 1.本文的源代码基于Spring2.x。Spring的源代码也处于演变中,但对基础代码的影响并不大。 2.本文是对Spring IoC容器实现的结构分析的姊妹帖。 正文: Spring AOP框架涉及的基本概念介绍: 关注点(concern):一个关注点可以是一个特定的问题、概念、或是应用程序的兴趣区间--总而言之,应用程序必须达到的一个目标。 核心关注点(core concern):业务功能模块,如:存款模块,取款模块,转账模块等, 横切关注点(crosscutting concern):非功能性的、横切性模块,如:安全性管理,事务管理,性能监控等。 方面(aspect):一个方面是对一个横切关注点的模块化,它将那些原本散落在各处的、用于实现这个关注点的代码归整到一处。 连接点(join point):程序执行过程中的一点,如: 字段访问:读、写实例变量; 方法调用:对方法(包括构造方法)的调用; 异常抛出:特定的异常被抛出。 切入点(pointcut):一组连接点的总称,用于指定某个增强应该在何时被调用。切入点常用正则表达式或别的通配符语法来描述,有些AOP实现技术还支持切入点的组合。 增强(advice):在特定连接点执行的动作。很多AOP框架都以拦截器(interceptor)的形式来表现增强--所谓拦截器是这样的一个 对象:当连接点被调用时,它会收到一个回调消息。基本的增强有: 前增强(BeforeAdvice):在连接点调用之前,首先调用增强; 后增强(AfterAdvice):在连接点调用之后,再调用增强,在AspectJ中,后增强又分为三种: AfterReturningAdvice:在调用成功完成(没有异常抛出)之后。 AfterThrowingAdvice:在抛出某种特定类型(或其子类型)的异常之后。 AfterAdvice:在连接点的任何调用之后,不管调用是否抛出异常。 环绕增强(AroundAdvice):这类增强可以完全控制执行流程。除了完成本身的工作之外,它还需要负责主动调用连接点,促使真实的操作发生(proceed)-- 这通常是通过调用某个特定的方法来完成的。 引介(introduction):为一个现有的Java类或接口添加方法或字段。这种技术可以用于实现Java中的多继承,或者给现有对象模型附加新的API。 混入继承(mixin inheritance):一个“混入类”封装了一组功能,这组功能可以被"混入"到现有的类当中,并且无须使用传统的继承手段。在AOP这里,混入是通过引介来实现的。在Java语言中,可以通过混入来模拟多继承。 织入(weaving):将方面整合到完整的执行流程(或完整的类,此时被织入的便是引介中)。 拦截器(initerceptor):很多AOP框架用它来实现字段和方法的拦截(interception)。随之而来的就是在连接点(如方法拦截)处挂接一条拦截器链(interceptor chain),链条上的每个拦截器通常会调用下一个拦截器。 AOP代理(AOP proxy):即被增强(advise)的对象引用--也就是说,AOP增强将在其上执行的这样一个对象引用。 目标对象(target object):位于拦截器链末端的对象实例--这个概念只存在于那些使用了拦截机制的框架之中。 注:上述概念描述引自《Expert One-on-One J2EE Development without EJB》中第八章对AOP概念描述部分,更多精彩部分可以参阅本章的完整内容。 上述概念已被Spring AOP框架很好的实现,相关组件: Advisor 组件, Advice 组件, Pointcut 组件, Advised 组件, AopProxy 组件, AopProxyFactory 组件, 图1. 图1是对增强、切入点、方面、AOP代理之间依赖关系的全景图。 增强和切入点组成一个方面,方面信息与目标对象信息被组织到Advised中,AopProxyFactory通过Advised中保存的信息生成AopProxy 对象,调用AopProxy.getProxy()方法即可获得增强后的对象。 这里要着重了解的是不同的增强子类型,不同的切入点子类型, 对于不同的切入点子类型最重要的两种子类型:静态切入点,动态切入点, 静态切入点:根据部署阶段的信息选择增强,如“拦截特定类的所有getter方法”; 动态切入点:根据运行时的信息选择增强,如“如果某方法的返回值为null,则将其纳入某切入点”。 图2. 图2是对图1中Advisor与Pointcut的实现细化,图中类之间的关系直观上有点乱,但细看下关系还是相当清晰的, 以Advisor结尾的是方面类型,以Pointcut结尾的是切入点类型, Advisor与Pointcut的复用关系分两类:一类是组合复用,另一类是具体继承复用, 组合复用例子 如:RegexpMethodPointcutAdvisor 与 AbstractRegexpMethodPointcut之间的关系, NameMatchMethodPointcutAdvisor 与 NameMatchMethodPointcut之间的关系, 具体继承复用例子 如:StaticMethodMatcherPointcutAdvisor 与 StaticMethodMatcherPointcut 之间的关系, DynamicMethodMatcherPointcutAdvisor 与 DynamicMethodMatcherPointcut 之间的关系, 图3. 图3是对图1中生成AopProxy对象的实现细化, AopProxyFactory通过AdvisedSupport提供的信息生成AopProxy对象,AopProxy对象的生成分两类方式:一类是动态代理,另一类是字节码增强; 需要注意的是,ProxyFactory与ProxyFactoryBean并不是功能实现的必要部分,主要目的为编程式使用代理提供便利的API。 下面是一个简单的sample: //目标对象接口. public interface Target { public String play(int arg); } //目标对象实现. public class TargetImpl implements Target { public String play(int arg) { System.out.println("play method...."); return "[Target:]" + arg; } } //前置增强 public class MyBeforeAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getName()); System.out.println("before method!"); } } //后置增强 public class MyAfterAdvice implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(returnValue + ":after method"); } } //切入点实现 public class MyPointcut implements Pointcut { public ClassFilter getClassFilter() { return new ClassFilter() { public boolean matches(Class arg0) { if (arg0 == TargetImpl.class) { return true; } return false; } }; } public MethodMatcher getMethodMatcher() { return new MethodMatcher() { public boolean isRuntime() { return false; } public boolean matches(Method arg0, Class arg1) { if ("play".equals(arg0.getName())) { return true; } return false; } public boolean matches(Method arg0, Class arg1, Object[] arg2) { System.out.println("aaaaaa"); if ("play".equals(arg0.getName())) { return true; } return false; } }; } } public class Main { public static void main(String[] args) { Target target = new TargetImpl(); //目标对象 Advice beforeAdvice = new MyBeforeAdvice(); //增强 Pointcut pointcut = new MyPointcut(); //切入点 DefaultPointcutAdvisor dda = new DefaultPointcutAdvisor(); //切面 dda.setAdvice(beforeAdvice); dda.setPointcut(pointcut); AdvisedSupport advisedSupport = new AdvisedSupport(); //提供基本的编程方式, advisedSupport.addAdvisor(dda); advisedSupport.addAdvice(new MyAfterAdvice()); advisedSupport.addInterface(Target.class); advisedSupport.setTarget(target); AopProxy aopProxy = new DefaultAopProxyFactory().createAopProxy(advisedSupport); Target proxy = (Target)aopProxy.getProxy(); System.out.println(proxy.play(200)); ProxyFactory proxyFactory = new ProxyFactory(); //提供便利的编程方式. proxyFactory.addAdvisor(dda); proxyFactory.addInterface(Target.class); proxyFactory.setTarget(target); Target proxy2 = (Target)proxyFactory.getProxy(); System.out.println(proxy2.play(201)); ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); //提供便利的配置方式 proxyFactoryBean.addAdvisor(dda); proxyFactoryBean.addInterface(Target.class); proxyFactoryBean.setTarget(target); Target proxy3 = (Target)proxyFactoryBean.getObject(); System.out.println(proxy3.play(232)); } } 注:此处为了简单忽略了一些概念描述: 1.对引介,混入继承没有涉及。 2.对通知链的管理,如:同一切入点多个通知对象之间的执行顺序问题; 3.上述的描述是Spring AOP框架本身,所提供的使用方式是编程式,这种使用方式太过于低级,以至于我们在九成的情况下不会使用到,主流的使用方式是配置化的声明式使用方式,将AOP与IoC结合起来使用才能发挥出最大威力,ProxyFactoryBean提供了有限的声明式使用方式,但严格来说它仍是编程式,因为ProxyFactoryBean是一个FactoryBean,一个FactoryBean的目标就是以编程式替换复杂的配置式,而且最重要的是它暴露的API太过低级,配置文件中bean元素的abstract属性对配置文件的长度提供有限的帮助,自动代DefaultAdvisorAutoProxyCreator很好的隐藏了低级的API,DefaultAdvisorAutoProxyCreator是一个BeanPostProcessor,用于完成AOP框架与IoC容器的集成工作,但是这种方式依然没有解决需要同XXXAdvisor这样的低级API打交道的问题; 随着spring2.x引入的xml元素<aop:config>及@Aspect注解的AspectJ描述性风格的出现,使用Spring AOP框架的使用达到完全的声明式标准,这种风格也使得Spring 事务框架受益,从TransactionProxyFactoryBean类到xml元素<tx:advice>、<aop:config>及@Transactional注解, 使得我们只需关注高层描述,而无需涉及低级API。 附上DefaultAdvisorAutoProxyCreator的类结构图: 总结: 要全面理解AOP相关概念,回答下述问题是必须的。 1。AOP概念产生的背景,AOP所要解决的问题,AOP所涉及的概念, 2。实现一个AOP框架所需要注意的问题是什么, 3。不同AOP框架实现之间的比较, 4。AOP的一些副作用讨论。 参考目录: 《Expert One-on-One J2EE Development without EJB》 《Professional Java Development with the Spring Framework》 《Mastering AspectJ》 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-09-14
讲的还不错、就是采用的风格不好啦
|
|
返回顶楼 | |
发表时间:2011-09-14
增强,你确定是这个翻译?
增强(advice):在特定连接点执行的动作。很多AOP框架都以拦截器(interceptor)的形式来表现增强--所谓拦截器是这样的一个 前增强(BeforeAdvice):在连接点调用之前,首先调用增强; 后增强(AfterAdvice):在连接点调用之后,再调用增强,在AspectJ中,后增强又分为 前置通知和后置通知会不会好点? |
|
返回顶楼 | |
发表时间:2011-09-14
楼主, 你的图是用逆向工程生成的, 还是手工绘制的。。?
|
|
返回顶楼 | |
发表时间:2011-09-14
LZ用的什么画图工具??
介绍一下 |
|
返回顶楼 | |
发表时间:2011-09-14
讲的不错,看过aop的源码,体会很多,再根据你的讲解豁然开朗
|
|
返回顶楼 | |
发表时间:2011-09-14
我也比较习惯用‘通知’这个翻译。
|
|
返回顶楼 | |
发表时间:2011-09-14
不错,这图应该是用什么工具画的吧,,,
|
|
返回顶楼 | |
发表时间:2011-09-14
H_eaven 写道 本文的目标:
从实现的角度来认识SpringAOP框架。 观察的角度: 从外部接口,内部实现,组成部分,执行过程四个方面来认识SpringAOP框架。 本文的风格: 首先列出AOP的基本概念; 其次介绍框架所涉及到的核心组件列表,组件之间的结构关系图; 然后细化结构图中的部分; 接下来是一个简单的sample; 最后是后记部分。 注: 1.本文的源代码基于Spring2.x。Spring的源代码也处于演变中,但对基础代码的影响并不大。 2.本文是对Spring IoC容器实现的结构分析的姊妹帖。 正文: Spring AOP框架涉及的基本概念介绍: 关注点(concern):一个关注点可以是一个特定的问题、概念、或是应用程序的兴趣区间--总而言之,应用程序必须达到的一个目标。 核心关注点(core concern):业务功能模块,如:存款模块,取款模块,转账模块等, 横切关注点(crosscutting concern):非功能性的、横切性模块,如:安全性管理,事务管理,性能监控等。 方面(aspect):一个方面是对一个横切关注点的模块化,它将那些原本散落在各处的、用于实现这个关注点的代码归整到一处。 连接点(join point):程序执行过程中的一点,如: 字段访问:读、写实例变量; 方法调用:对方法(包括构造方法)的调用; 异常抛出:特定的异常被抛出。 切入点(pointcut):一组连接点的总称,用于指定某个增强应该在何时被调用。切入点常用正则表达式或别的通配符语法来描述,有些AOP实现技术还支持切入点的组合。 增强(advice):在特定连接点执行的动作。很多AOP框架都以拦截器(interceptor)的形式来表现增强--所谓拦截器是这样的一个 对象:当连接点被调用时,它会收到一个回调消息。基本的增强有: 前增强(BeforeAdvice):在连接点调用之前,首先调用增强; 后增强(AfterAdvice):在连接点调用之后,再调用增强,在AspectJ中,后增强又分为三种: AfterReturningAdvice:在调用成功完成(没有异常抛出)之后。 AfterThrowingAdvice:在抛出某种特定类型(或其子类型)的异常之后。 AfterAdvice:在连接点的任何调用之后,不管调用是否抛出异常。 环绕增强(AroundAdvice):这类增强可以完全控制执行流程。除了完成本身的工作之外,它还需要负责主动调用连接点,促使真实的操作发生(proceed)-- 这通常是通过调用某个特定的方法来完成的。 引介(introduction):为一个现有的Java类或接口添加方法或字段。这种技术可以用于实现Java中的多继承,或者给现有对象模型附加新的API。 混入继承(mixin inheritance):一个“混入类”封装了一组功能,这组功能可以被"混入"到现有的类当中,并且无须使用传统的继承手段。在AOP这里,混入是通过引介来实现的。在Java语言中,可以通过混入来模拟多继承。 织入(weaving):将方面整合到完整的执行流程(或完整的类,此时被织入的便是引介中)。 拦截器(initerceptor):很多AOP框架用它来实现字段和方法的拦截(interception)。随之而来的就是在连接点(如方法拦截)处挂接一条拦截器链(interceptor chain),链条上的每个拦截器通常会调用下一个拦截器。 AOP代理(AOP proxy):即被增强(advise)的对象引用--也就是说,AOP增强将在其上执行的这样一个对象引用。 目标对象(target object):位于拦截器链末端的对象实例--这个概念只存在于那些使用了拦截机制的框架之中。 注:上述概念描述引自《Expert One-on-One J2EE Development without EJB》中第八章对AOP概念描述部分,更多精彩部分可以参阅本章的完整内容。 上述概念已被Spring AOP框架很好的实现,相关组件: Advisor 组件, Advice 组件, Pointcut 组件, Advised 组件, AopProxy 组件, AopProxyFactory 组件, 图1. 图1是对增强、切入点、方面、AOP代理之间依赖关系的全景图。 增强和切入点组成一个方面,方面信息与目标对象信息被组织到Advised中,AopProxyFactory通过Advised中保存的信息生成AopProxy 对象,调用AopProxy.getProxy()方法即可获得增强后的对象。 这里要着重了解的是不同的增强子类型,不同的切入点子类型, 对于不同的切入点子类型最重要的两种子类型:静态切入点,动态切入点, 静态切入点:根据部署阶段的信息选择增强,如“拦截特定类的所有getter方法”; 动态切入点:根据运行时的信息选择增强,如“如果某方法的返回值为null,则将其纳入某切入点”。 图2. 图2是对图1中Advisor与Pointcut的实现细化,图中类之间的关系直观上有点乱,但细看下关系还是相当清晰的, 以Advisor结尾的是方面类型,以Pointcut结尾的是切入点类型, Advisor与Pointcut的复用关系分两类:一类是组合复用,另一类是具体继承复用, 组合复用例子 如:RegexpMethodPointcutAdvisor 与 AbstractRegexpMethodPointcut之间的关系, NameMatchMethodPointcutAdvisor 与 NameMatchMethodPointcut之间的关系, 具体继承复用例子 如:StaticMethodMatcherPointcutAdvisor 与 StaticMethodMatcherPointcut 之间的关系, DynamicMethodMatcherPointcutAdvisor 与 DynamicMethodMatcherPointcut 之间的关系, 图3. 图3是对图1中生成AopProxy对象的实现细化, AopProxyFactory通过AdvisedSupport提供的信息生成AopProxy对象,AopProxy对象的生成分两类方式:一类是动态代理,另一类是字节码增强; 需要注意的是,ProxyFactory与ProxyFactoryBean并不是功能实现的必要部分,主要目的为编程式使用代理提供便利的API。 下面是一个简单的sample: //目标对象接口. public interface Target { public String play(int arg); } //目标对象实现. public class TargetImpl implements Target { public String play(int arg) { System.out.println("play method...."); return "[Target:]" + arg; } } //前置增强 public class MyBeforeAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getName()); System.out.println("before method!"); } } //后置增强 public class MyAfterAdvice implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(returnValue + ":after method"); } } //切入点实现 public class MyPointcut implements Pointcut { public ClassFilter getClassFilter() { return new ClassFilter() { public boolean matches(Class arg0) { if (arg0 == TargetImpl.class) { return true; } return false; } }; } public MethodMatcher getMethodMatcher() { return new MethodMatcher() { public boolean isRuntime() { return false; } public boolean matches(Method arg0, Class arg1) { if ("play".equals(arg0.getName())) { return true; } return false; } public boolean matches(Method arg0, Class arg1, Object[] arg2) { System.out.println("aaaaaa"); if ("play".equals(arg0.getName())) { return true; } return false; } }; } } public class Main { public static void main(String[] args) { Target target = new TargetImpl(); //目标对象 Advice beforeAdvice = new MyBeforeAdvice(); //增强 Pointcut pointcut = new MyPointcut(); //切入点 DefaultPointcutAdvisor dda = new DefaultPointcutAdvisor(); //切面 dda.setAdvice(beforeAdvice); dda.setPointcut(pointcut); AdvisedSupport advisedSupport = new AdvisedSupport(); //提供基本的编程方式, advisedSupport.addAdvisor(dda); advisedSupport.addAdvice(new MyAfterAdvice()); advisedSupport.addInterface(Target.class); advisedSupport.setTarget(target); AopProxy aopProxy = new DefaultAopProxyFactory().createAopProxy(advisedSupport); Target proxy = (Target)aopProxy.getProxy(); System.out.println(proxy.play(200)); ProxyFactory proxyFactory = new ProxyFactory(); //提供便利的编程方式. proxyFactory.addAdvisor(dda); proxyFactory.addInterface(Target.class); proxyFactory.setTarget(target); Target proxy2 = (Target)proxyFactory.getProxy(); System.out.println(proxy2.play(201)); ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); //提供便利的配置方式 proxyFactoryBean.addAdvisor(dda); proxyFactoryBean.addInterface(Target.class); proxyFactoryBean.setTarget(target); Target proxy3 = (Target)proxyFactoryBean.getObject(); System.out.println(proxy3.play(232)); } } 注:此处为了简单忽略了一些概念描述: 1.对引介,混入继承没有涉及。 2.对通知链的管理,如:同一切入点多个通知对象之间的执行顺序问题; 3.上述的描述是Spring AOP框架本身,所提供的使用方式是编程式,这种使用方式太过于低级,以至于我们在九成的情况下不会使用到,主流的使用方式是配置化的声明式使用方式,将AOP与IoC结合起来使用才能发挥出最大威力,ProxyFactoryBean提供了有限的声明式使用方式,但严格来说它仍是编程式,因为ProxyFactoryBean是一个FactoryBean,一个FactoryBean的目标就是以编程式替换复杂的配置式,而且最重要的是它暴露的API太过低级,配置文件中bean元素的abstract属性对配置文件的长度提供有限的帮助,自动代DefaultAdvisorAutoProxyCreator很好的隐藏了低级的API,DefaultAdvisorAutoProxyCreator是一个BeanPostProcessor,用于完成AOP框架与IoC容器的集成工作,但是这种方式依然没有解决需要同XXXAdvisor这样的低级API打交道的问题; 随着spring2.x引入的xml元素<aop:config>及@Aspect注解的AspectJ描述性风格的出现,使用Spring AOP框架的使用达到完全的声明式标准,这种风格也使得Spring 事务框架受益,从TransactionProxyFactoryBean类到xml元素<tx:advice>、<aop:config>及@Transactional注解, 使得我们只需关注高层描述,而无需涉及低级API。 附上DefaultAdvisorAutoProxyCreator的类结构图: 总结: 要全面理解AOP相关概念,回答下述问题是必须的。 1。AOP概念产生的背景,AOP所要解决的问题,AOP所涉及的概念, 2。实现一个AOP框架所需要注意的问题是什么, 3。不同AOP框架实现之间的比较, 4。AOP的一些副作用讨论。 参考目录: 《Expert One-on-One J2EE Development without EJB》 《Professional Java Development with the Spring Framework》 《Mastering AspectJ》 |
|
返回顶楼 | |
发表时间:2011-09-14
Advice:大多数的翻译都为“通知”,但也有翻译为“增强”,同一个概念的两个译法;
本文中的“增强”译法引用自《Expert One-on-One J2EE Development without EJB》 中译本的译法,因此延用了这一叫法; 我本身也更习惯“通知”的译法。 文中类结构图的绘图工具使用的是MyEclipse的UML插件绘画的, 文中的图是手工绘制的。 |
|
返回顶楼 | |