众所周知,Spring容器的三大核心功能是IOC、DI和AOP,前面我们已经对IOC和DI的理论做了浅析,这篇文章将从AOP的概念、设计思路、应用场景、Spring AOP的源码分析四方面入手,理解下AOP究竟是个神马东东。
1、概念
AOP的英文全称是Aspect Oriented Programming,即面向切面编程。借鉴一下维基百科中对AOP相关概念的描述:
Aspect是一种新的模块化机制,从关注点中分离出横切关注点是面向切面编程的核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过切面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好地管理起来。
简单点来说,程序中总会出现一些重复的代码,而且不太方便使用继承的方式来实现重用和管理,这些功能重复并且需要用在不同的地方,这时候AOP就可以派上用场了。
AOP中的几个重要名词:
Aspect:Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice,Spring常用注解@Aspect来标识这是一个切面。
Advice:通知,定义在连接点做什么,为切面增强提供织入接口,在Spring中,它主要描述Spring AOP围绕方法调用而注入的切面行为,提供了具体的通知类型,例如BeforeAdvice、AfterAdvice、ThrowsAdvice。
JoinPoint:连接点,定义一个通知在程序中执行的位置,可以是方法前、方法后、抛出异常时等。
PointCut:切点,确定Advice通知应该作用在哪个连接点上,通俗来讲就是通过PointCut来定义需要增强的方法的集合,这些方法集合的选取可以由正则表达式进行标识,或根据某个方法名进行匹配。
2、Spring中的设计
AOP是Spring的核心模块,虽然AspectJ是一个完整的AOP框架,但在Spring中提供了另外一种实现,这种实现并不是要和AspectJ做竞争,相反,Spring AOP还将AspectJ集成进来,为IOC容器和Spring应用的开发提供了一个一致性的AOP解决方案。
Spring AOP是以动态代理为基础,设计出来一系列的AOP横切实现,例如前置通知、后置通知、环绕通知、异常通知等。同时Spring还提供了Pointcut来匹配切入点,可以直接使用来设计横切面。
在Spring中是用AOP,只需要配置相关的Bean定义即可,但分析Spring AOP的源码可以看到,为了实现AOP的完整功能,Spring实现了一个很长的调用链来完成这个过程。
3、Spring AOP的应用场景
Spring AOP的应用场景有很多,我们在实际项目开发中经常用到的场景如下:
(1)日志记录
(2)事务
(3)权限
(4)缓存
4、Spring AOP源码分析
大家都知道,Spring AOP是以动态代理(JDK或CGLIB)为基础的,根据以往看源码的经验,我们继续从Spring AOP的源码中抽取一条具有代表性的主线进行分析,以JDK动态代理为例。
时序图:
在Spring抽取出的一条AOP的实现线路后,可以发现这个过程主要分为三步:创建代理类、代码织入、通知回调
(1)创建代理类
回顾Spring的依赖注入过程,在AbstractAutowireCapableBeanFactory类的doCreateBean方法中调用populateBean完成Bean的属性赋值,在populateBean方法后继续调用initializeBean方法对Bean进行初始化,实际上是在Bean的初始化前后添加了BeanPostProcessor后置处理器。源码如下:
生成代理类是在bean初始化之后,即调用applyBeanPostProcessorsAfterInitialization方法,再调用postProcessAfterInitialization方法,查看源码如下:
进入wrapIfNecessary方法:
在 wrapIfNecessary方法中可以看到两个重要的步骤,即getAdvicesAndAdvisorsForBean和createProxy,getAdvicesAndAdvisorsForBean是获取bean的所有advice,createProxy是创建代理类的入口方法,源码截图中已给了注释,可自行翻阅源码。
点进createProxy方法中可以看到getProxy方法的两个实现类,Jdk动态代理和Cglib动态代理。如果bean实现的是接口则使用jdk动态代理,否则使用Cglib动态代理生成代理类。
(2)代码织入和通知回调
在JdkDynamicAopProxy类中,invoke方法是被代理对象方法调用的入口,getInterceptorsAndDynamicInterceptionAdvice方法返回一个连接器列表,如果这个列表chain不为空,就会创建一个ReflectiveMethodInvocation对象invocation,接下来调用invocation的proceed方法。
在 proceed方法中,会动态匹配joinPoint,如何匹配满足条件,调用拦截器的invoke方法,invoke方法有几个实现类,分别为AspectJMethodBeforeAdvice、AspectJAfterAdvice、AspectJAfterReturningAdvice、AspectJAroundAdvice、AspectJAfterThrowingAdvice,对应着AOP中的前置通知、后置通知、环绕通知、异常通知等。
动态代理生成代理类,通过自定义的匹配规则匹配到方法对应的拦截器,这样对方法进行增强,完成了AOP方法的切入。
欢迎关注公众号进行技术交流,新开通的公众号,以后会分享java技术、源码、架构和面试题。