Spring AOP是咱们平常开发中常用的工具,常被用来作统一的日志、异常处理、监控等功能,使用方法在此很少赘述,有兴趣的读者能够自行去网上查阅资料进行学习,咱们以注解的使用方式为例,分析其相关源码,其余方式大同小异。html
开启Spring AOP注解方式首先要配置<aop:aspectj-autoproxy/>标签,咱们就以这个标签的解析做为入口来分析,这里须要读者对Spring自定义标签解析的过程有必定的了解,笔者后续也会出相关的文章。锁定AopNamespaceHandler:api
这里提到了proxy-target-class和expose-proxy两个属性,简单介绍一下,Spring提供了JDK动态代理和CGLIB代理两种方式为目标类建立代理,默认状况下,若是目标类实现了一个以上的用户自定义的接口或者目标类自己就是接口,就会使用JDK动态代理,若是目标类自己不是接口而且没有实现任何接口,就会使用CGLIB代理,若是想强制使用CGLIB代理,则能够将proxy-target-class设置true,这两种代理方式在使用的时候有一些须要注意的事项,JDK动态代理是基于实现目标类的接口来建立代理类的,因此只有接口方法会被代理,其余方法不会被代理,而CGLIB代理是基于继承目标类实现的,因此不能被继承的方法(例如final修饰的方法、private修饰的方法等)是不能被代理的,建议尽可能使用JDK动态代理的方式建立代理类。expose-proxy用来解决对象内部this调用没法被切面加强的问题,例如咱们在A类的对象内部x方法中调用另一个内部方法y时,y方法不会被切面加强,这时能够配置expose-proxy为true并将this.y()改成((A)AopContext.currentProxy()).y(),便可让y方法被切面加强。下面让咱们来看本篇文章的主角AnnotationAwareAspectJAutoProxyCreator的注册过程:缓存
咱们发现优先级的判断就是根据类在APC_PRIORITY_LIST中的索引值来判断的,索引值越小的优先级越高,咱们看一下APC_PRIORITY_LIST的内容:eclipse
咱们发现它是一个ArrayList,而且在静态块中为其add了三个类,也就是这三个类的优先级依次下降。注册完AnnotationAwareAspectJAutoProxyCreator以后,要怎么使用这个bean呢,咱们看一下它的层次结构:ide
咱们发现这个类间接实现了BeanPostProcessor接口,咱们知道,Spring会保证全部bean在实例化的时候都会调用其postProcessAfterInitialization方法,咱们可使用这个方法包装和改变bean,而真正实现这个方法是在其父类AbstractAutoProxyCreator类中:工具
上面这个方法相信你们已经看出了它的目的,先找出全部对应Advisor的类的beanName,再经过beanFactory.getBean方法获取这些bean并返回,这里就是经过父类获取其余aop配置信息。下面咱们来看注解aop配置信息的获取:post
方法很长,不过逻辑很清晰,首先获取全部bean,而后过滤掉不知足子标签配置过滤条件的bean,接着判断bean是否有@Aspect注解,最后解析注解的配置内容并放入缓存中,咱们分部来看:学习
这里的includePatterns就是文章开始解析<aop:aspectj-autoproxy/>子标签<aop:include/>的配置时织入的,有兴趣的读者能够了解一下具体用法,这里很少赘述。优化
这里提到了aspect的初始化模式,目前一共有6种,对应PerClauseKind这个枚举,这里不作详细说明,你们能够到aspect官方文档进行了解,这里给出地址:this
https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/index.html
这里咱们看到aop相关的一些注解的提取,下面就是初始化过程了:
到这里整个aop注解方式的初始化工做就完成了,不知道你们是否还记得咱们是怎么一步一步的走到这里的,我获取到了全部的候选加强器,下面要匹配适用于当前bean的加强器:
上面的方法中提到引介加强的概念,在此作简要说明,引介加强是一种比较特殊的加强类型,它不是在目标方法周围织入加强,而是为目标类建立新的方法和属性,因此引介加强的链接点是类级别的,而非方法级别的。经过引介加强,咱们能够为目标类添加一个接口的实现,即原来目标类未实现某个接口,经过引介加强能够为目标类建立实现该接口的代理,使用方法能够参考文末的引用连接。另外这个方法用两个重载的canApply方法为目标类寻找匹配的加强器,其中第一个canApply方法会调用第二个canApply方法并将第三个参数传为false:
咱们来看一下前面初始化的InstantiationModelAwarePointcutAdvisorImpl的层次结构:
咱们看到它实现了PointcutAdvisor接口,因此会调用红框中的canApply方法进行判断,第一个参数pca.getPointcut()也就是调用InstantiationModelAwarePointcutAdvisorImpl的getPointcut方法,这个方法的返回值就是咱们看到的在InstantiationModelAwarePointcutAdvisorImpl初始化时传入的AspectJExpressionPointcut,咱们以AspectJExpressionPointcut做为第一个参数继续跟踪canApply方法:
咱们跟踪pc.getMethodMatcher()方法也就是AspectJExpressionPointcut的getMethodMatcher方法:
发现方法直接返回this,也就是下面methodMatcher.matches方法就是调用AspectJExpressionPointcut的matches方法:
getShadowMatch方法里面就是调用aspect提供的api来判断当前类是否知足execution表达式的规则,有兴趣的读者能够查阅aspect的相关资料进行学习。在获取了全部bean匹配的加强器以后,就能够建立代理了:
这里咱们看到了Spring若是选择使用JDK动态代理仍是CGLIB代理,optimize用来控制经过CGLIB建立的代理是否使用激进的优化策略,这个配置对JDK动态代理无效,不推荐使用,proxy-target-class文章开始已经介绍过,逻辑就是在这里实现的,hasNoUserSuppliedProxyInterfaces判断目标类是否没有用户自定义的代理接口。咱们先看JDK动态代理的方式:
这里咱们看到为即将建立的代理类添加了3个接口,后面会用到。JDK动态代理还有一个关键的角色就是InvocationHandler,这里传入的this,因此咱们判定,JdkDynamicAopProxy必定实现了InvocationHandler接口:
分析其invoke方法:
在DefaultAdvisorAdapterRegistry初始化时初始化了3个适配器,这里咱们以MethodBeforeAdviceAdapter为例,也就是对应@Before注解建立的advice的适配器:
下面咱们来看拦截器链的调用:
这里咱们以刚刚适配的MethodBeforeAdviceInterceptor为例:
这里首先执行拦截器的before方法,而后再次执行上面的proceed方法进行下一个拦截器方法的调用,这里的advice也就是获取候选加强器时生成的AspectJMethodBeforeAdvice:
这里的aspectJAdviceMethod也就是咱们应用程序中@Before注解的方法了。咱们再来看一个@After注解对应的advice是若是执行的,锁定AspectJAfterAdvice:
咱们发现是在finally块中执行了拦截器方法,也就是@After注解的方法会在目标方法执行以后执行。下面咱们来看一下CGLIB代理的方式,这里须要读者去了解一下CGLIB以及其建立代理的方式:
这里将拦截器链封装到了DynamicAdvisedInterceptor中,并加入了Callback,DynamicAdvisedInterceptor实现了CGLIB的MethodInterceptor,因此其核心逻辑在intercept方法中:
这里咱们看到了与JDK动态代理一样的获取拦截器链的过程,而且CglibMethodInvokcation继承了咱们在JDK动态代理看到的ReflectiveMethodInvocation,可是并无重写其proceed方法,只是重写了执行目标方法的逻辑,因此总体上是大同小异的。
到这里,整个Spring 动态AOP的源码就分析完了,Spring还支持静态AOP,这里就不过多赘述了,有兴趣的读者能够查阅相关资料来学习。