aop 简介
aop,全称:aspect-oriented programming,即是面向切面编程。
aop和Ioc并称为spring的两大特性,但aop有什么用呢?
场景一:
对功能重复的代码,并被不同地方使用,我们都会把它简单的封装成util,以供调用。但是,我们并不能很好的灵活控制这个util的被调用。
假如我们需要在每次数据库操作之前或之后使用某个util记录日志,使用直接调用的话,要是有100个操作,你就需要在这100个操作前后调用log util。
又有可能,后来你不需要这个util了,你又需要在100个地方取消它。是不是很痛苦呢~~
当然,可以通过面向对象和设计模式的方法来解决一部分问题,但仍显得不够优雅和敏捷。
当然应用场景还不仅这些,还有如session管理,验证,事务,权限等等方面都有类似的问题。
为了解决这些痛点,于是便有了AOP。那AOP是如何解决这个问题的呢?首先了解基本概念。
基本概念
以上两张图,很好的展示了AOP的工作方式,以及为什么叫切面编程。
这里主要涉及到几个概念:
Advice(通知):
如图,主要描述要增强的行为,如场景一中的log行为,但不仅如此,Advice还分BeforeAdvice、AfterAdvice、ThrowsAdvice等类型。
Pointcut(切点):
这个描述了Advice或切面要嵌入的位置,比如场景一中的数据库操作,其实就是一个Pointcut。
现在,有了Advice和Pointcut,总感觉差点什么。没错,需要一个配置器(通知器),把两者关联起来。因此定义为:
Advisor(通知器):
通知器建立了Advice和Pointcut的关联,因此我们终于可以知道在某个切点要用哪个通知了。
因此,场景一的AOP解决方案,以Spring Aspect(注解版)为例:
1, 使用spring注解,如下代码, 定义Advice,下面我们使用@Before
注解,被标注的方法相当与一个Advice。
2, @Before
注解中有个参数,这个参数定义了切点的描述,这里可以使用一些通配符来简化配置。
@Aspect |
使用注解,好处就是省去了Advisor的配置,偷个懒~~ 当然在applicationContext.xml
中要启用<aop:aspectj-autoproxy/>
和<context:component-scan
才行。
XML 声明文档传送门
看完这个,你一定对spring aop的实现很好奇,接下来就简单看一下spring aop的工作flow吧。
实现过程
也许你已经猜到,要实现AOP,必须使用反射和动态代理。没错,Java的这个特性可以实现AOP。
spring环境下,再结合Ioc的统一对象管理,功能无比强大啊。
接下来我们就一起结合源码看一下aop是怎么工作的。
生成 aop proxy
这里Ioc部分不介绍了(请参阅另一博文spring Ioc
),直接以aop proxy生成为入口。
以下是主要的类,接下来让我们慢慢体会这样设计的原因:
具体的aop代理对象的生成,分别是由AspectJProxyFactory、ProxyFactoryBean和ProxyFactory来完成的。
以ProxyFactoryBean.getObject()为例:
public Object getObject() throws BeansException { |
initializeAdvisorChain()代码比较长,主要流程是 : 根据内置成员interceptorNames : String[]
生成bean,然后namedBeanToAdvisor()转换成Advisor,
再调用父类AdvisedSupport.addAdvisor(Advisor)增加到通知器链LinkedList<Advisor>
中,后面也会对这部分进行更详细介绍。
结合上面的class hierarchy图,可知AdvisedSupport主要封装了对通知和通知器的相关操作(从命名也可以知道),这些操作对不同的代理对象应该是一致的,
而代理对象的生成,是交给子类去实现的,ProxyCreatorSupport
则协助子类进行proxy的生成。
而生成的proxy, 可以是singleton或者prototype,这个功能都是由AopProxy完成的。
而这个AopProxy是由AopProxyFactory创建,这部分代码在ProxyCreatorSupport中:
// ProxyCreatorSupport.java |
其中getAopProxyFactory(),即this.aopProxyFactory,是spring封装的DefaultAopProxyFactory:
public ProxyCreatorSupport() { |
DefaultAopProxyFactory createAopProxy()会根据target class是否接口来选择不同的生成方式(下图)。
如果是接口,则使用jdk动态代理;否则使用cglib,如下所示:
所以,上面提到的AopProxy是一个接口,它有两个实现:CglibAopProxy和JdkDynamicAopProxy。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { |
jdk和cglib的生成方式这里略过,感兴趣的话查看new JdkDynamicAopProxy(AdvisedSupport)
和CglibProxyFactory.createCglibProxy(config)
即可。
拦截器调用
生成代理完成时,相关的拦截器已经配置完成了,现在只需在代理对象调用时回调这些拦截器即可,Jdk和cglib的代理和回调有些不同。
这里以大家熟悉的jdk dynamic proxy为例,如下图,JdkDynamicAopProxy实现了InvocationHandler:
invoke()的源码如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
如上可知,chain
是通过AdvisedSupport
(this.advised)的getInterceptorsAndDynamicInterceptionAdvice()获取的。
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) { |
其中,this.advisorChainFactory是一个叫DefaultAdvisorChainFactory的工厂,主要实现了拦截器链的获取。
而且,会注册到AdvisorAdapterRegistry
接口的实现DefaultAdvisorAdapterRegistry
中。
DefaultAdvisorAdapterRegistry的构造器如下所示,可以看到,这里注册了一些常见的AdviceAdapter:
public DefaultAdvisorAdapterRegistry() { |
上面这些adapters,主要有两个作用,以AfterReturningAdviceAdapter
为例:
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable { |
作用之一是判断某个Advice属于什么类型;还有就是返回相对应的AdviceInterceptor。
advice的实现其实也是拦截器(实现了MethodInterceptor),如下所示:
和上面保持一致,以AfterReturingInterceptor为例:
public Object invoke(MethodInvocation mi) throws Throwable { |
回到JdkDynamicAopProxy.invoke()
,其中的ReflectiveMethodInvocation.proceed()就是链调用主要入口, 如下:
|
上面主要是遍历每个 interceptorOrInterceptionAdvice,并调用相应的拦截器,这样串起来,整个流程就清晰了。
其中InterceptorAndDynamicMethodMatcher组合MethodInterceptor和MethodMatcher,使其作为通知链的一个element使用,如下:
class InterceptorAndDynamicMethodMatcher { |
以上就是ProxyFactoryBean调用的主要流程,只是一个大概的流程,看源码的话,spring会有许多细节验证和处理。不过阅读时,一定要抓住重要的主线,还有熟悉常见的设计模式,这会让你有个全景的认识和理解。
DIY 属于自己的AOP
// TODO still in drafts 。