AOP从功能的角度来讲,可能看作OOP编程方式的一种补充,提供了一种不同的代码或者系统组织方式。OOP中的核心概念是Class,而在AOP中则是Aspect。
spirng-aop模块是Spring框架中的核心模块,虽然Spring Ioc container并不依赖AOP,但AOP给Ioc的实现提供了一种强大而灵活的解决方案。
在Spring Framework中,AOP主要是用于两种目的:
提供一些企业级的声明式服务,典型的应用如 declarative transaction management .
允许用户实现自己的aspects,用AOP的方式来帮助和补充OOP的功能及实现
Spring AOP由纯Java实现,无须特殊的编译处理,也不需要控制类加载器的层次结构,所以它可以适用于Servlet Container和其它application server.
Spring AOP目前只支持方法级别的切换或拦截,属性的拦截现在不支持,如果想要拦截属性,可以考虑使用AspectJ语言。
Spring AOP的使用不同于其它大多数AOP框架。它的主要目的不是提供一套大而全的AOP实现,而是集成AOP不同实现协同Spring Ioc来帮助解决一些普遍性问题。
需要注意的是,一些细粒度的advised(如domain model),Spring AOP往往不能提供很好的支持,这场景也还是考虑AspectJ。即使如此,就普遍经验来说Spring AOP的强大机制依然能够解决大多数场景的问题。
那么该如何看待Spring AOP和AspectJ,引用Spring官方文档的原文:
Spring AOP will never strive to compete with AspectJ to provide a comprehensive AOP solution. We believe that both proxy-based frameworks like Spring AOP and full-blown frameworks such as AspectJ are valuable, and that they are complementary, rather than in competition. Spring seamlessly integrates Spring AOP and IoC with AspectJ, to enable all uses of AOP to be catered for within a consistent Spring-based application architecture. This integration does not affect the Spring AOP API or the AOP Alliance API: Spring AOP remains backward-compatible.
在Spring框架所有的模块设计中,始终遵守的核心信条之一是——无侵入性。
所以在使用Spring AOP时,不会强制我们在业务代码中引入特定类或者接口,可以最大限度的保持代码 clean and decouple。然而Spring也提供了另一种选择,如果有特定的场景需要的话,你可以在你的代码中直接引入Spring AOP。几乎所有Spring框架中的模块,在使用的方式上都会给你多种选择,以便让用户选择更适合自己场景的方式。使用AspectJ还是Spring AOP,使用annotation方式还是xml的配置方式,Depends On U。
了解了Spring AOP的初衷和使用场景,来看下它的大致实现原理
在软件世界中的绝大多数问题,都可以通过加一层来解决。
这里所说的层,当然 是广义上的,可以是一层抽象,也可以是一层cache,大致含义是隔离和解耦的范畴。
在Spring的世界里,每一个模块的引入,或者第三方技术的集成,总会提供一个抽象层 ,对用户提供统一的API,屏蔽了所有的实现细节以及不同实现的差异。例如 spring-cache , spring-jdbc , spring-jms 以及 spirng-messaging 等模块都提供了一层抽象。
Spring AOP的实现是基于代理的机制,默认是采用Jdk dynamic proxy,也可以采用cglib的proxy。两者的区别主要是在于被代理的对象的不同。当目标对象是接口时,Jdk dynamic proxy可以完成代理,但目标对象是没有实现接口的类时(尽量少一些,面向接口编程是好习惯),是需要采用cglib proxy来完成代理的,当然你也可以强制接口也采用cglib来代理;另外需要注入或引用具体类型时,如果引用的东西恰恰是代理过的对象,此时也需要采用cglib的方式。
功能设计和实现上来可以分为两大块
aop基础设施的创建,可以看作是aopProxy的生成
aopProxy对象的调用时的处理拦截,即处理对目标对象的拦截器
生成代理对象的核心类, ProxyFactoryBean getObjecct()
下图是生成代理时,是用Jdk还是cglib的选择逻辑:
找到了生成代理的具体执行者,那么这个操作是在什么时候被调用的呢,了解过Spring bean生命周期的都应该知道,bean在创建的时候,有一系列的回调接口供用户插入自定义的行为,来左右bean的一些特性,其中 BeanPostProcessor 是接口中的一种。以往的文章有介绍过( 玩转Spring bean的终极利器 )。而Spring AOP正是利用这个契机,在创建bean的过程中插了一手,如果正在创建的bean是我们aop的target,则创建代理,并最终把代理对象返回给Ioc。
AbstractAutoProxyCreator 这个类是一个 BeanPostProcessor 的实现,用来创建代理,来看这个处理器的后处理方法,最终是返回了 createProxy() 方法返回的代理
可以理解成对目标对象上所有拦截器链的调用
由于Spring AOP的代理具体实现有两种,JDK dynamic proxy和cglib,所以执行拦截器的方式有所不同,具体可以阅读源码 JdkDynamicAopProxy 类的invoke方法
对目标方法的调用最终是依靠 ReflectiveMethodInvocation .
ReflectiveMethodInvocation中的 proceed 处理是采用递归的方式处理拦截器链
CglibAopProxy 的 intercept 方法
CglibMethodInvoation 是继承了 ReflectiveMethodInvocation ,处理拦截器链也是用的上边的proceed()方法。
在使用Spring AOP时需要注意的两点细节:
在类内部的方法调用时(self-invoke),Spring AOP不起作用,原因是内部调用没通过代理对象,直接使用的目标对象。解决方法有:
重构代码,避免内部调用
AopContext.currentProxy()
或者干脆使用AspectJ语言吧...
在注入bean时,如果想注入bean的具体的类型而不是接口,那么采用cglib吧
Spring AOP的功能很强大,设计巧妙,这里梳理了主要脉络,细节不再一一讨论。
后续的文章会逐步介绍Spring其它模块,重点聊聊设计和源码