Spring框架浅析 -- AOP
什么是AOP
AOP-Aspect Oriented Programming,通常翻译为面向切面编程。它的提出实际上是为了补充OOP-Object Oriented Programming的一些不足。
那么OOP有哪些不足呢?这里我谈谈自己的一些想法,我们知道OOP的思路通常是,将一个大型的功能划分成若干子功能,然后进一步抽象成为若干小模块或者叫小组件,每一个小组件封装了数据(属性)与操作(方法),我们叫这样的小组件为类,每一个类代表着抽象后的一种对外的服务能力。如下图所示:
通常情况下,OOP是能够将一个庞大而复杂的功能层层切分到一些抽象后的通用化的服务单元上,也就是类上。通过对不同的类进行拼装、组合,实现不同的业务需求,达到可复用的效果。但是在某些应用场景上,仅有OOP是不够的。比如对于一些非常通用的无业务逻辑的功能(打日志、监控、权限验证、数据库连接&关闭&事务处理等),沿着OOP的思路走下去就不是很方便了。当然你也可以针对这些通用功能分别创建工具类,然后在需要使用这种通用功能的时候,使用工具类完成功能。但是试想假设有成千上万的业务相关类都要进行比如打日志这种通用操作,在实际使用的时候,难道你要在每一个业务相关类需要打日志的地方调用工具类完成此功能吗?答案当然是No。而AOP,正是针对这种场景应运而生的。
关于AOP的原理,不可避免地要涉及一系列晦涩难懂的概念,比如什么是连接点-JointPoint,什么是切点-PointCut,什么是切面-Aspect等,想了解这些概念的可以自行google。
我们不去纠结这些概念,只谈谈我对此的一些理解。其实我认为AOP最为重要的就两件事:切入增强逻辑的地方和增强逻辑本身。
顾名思义,切入增强逻辑的地方即在哪里引入通用操作,如Before,After,Around,AfterReturning,AfterThrowing。
增强逻辑本身就要看具体要实现的通用功能了,比如打日志、监控、权限验证、数据库连接&关闭&事务处理等。
详情见下图:
值得注意的是,AOP并不是用来取代OOP的,而是和OOP一起针对不同的使用场景,应对不同需求的。如果是OOP看世界的方式是横向切分的,那么AOP则是换了个角度看世界,更倾向于纵向的层级性切分,估计将AOP翻译为"面向切面编程"也受到了这种思路的影响。因此,AOP适用场合通常为面向纵向切分出的一些通用功能。
Spring支持AOP的原理
在此,我们采用一个权限校验的场景为例,结合Spring源码,来介绍一下Spring是如何支持AOP的。
需求
为了简单描述,我们仅考虑这样一个权限校验的场景,即进入方法时,进行权限校验,当具备权限的时候,正常进入方法执行;不具备则抛异常。
对于打日志等场景,无非是多了几个切入增强逻辑的地方,比如进入方法,方法返回,方法抛出异常。
配置
<bean id="xxxAspect" class="com.xxx.xxxAspect"/>
<aop:aspectj-autoproxy proxy-target-class="true">
<aop:include name="xxxAspect"/>
</aop:aspectj-autoproxy>
Spring对配置的识别过程
在Spring框架浅析 -- IoC容器与Bean的生命周期一文中,大致有过介绍,对于aop这样的自定义namespace,其对应的namespaceHanlder为AopNamespaceHandler。对于aspectj-autoproxy属性,其对应的BeanDefinitionParser为AspectJAutoProxyBeanDefinitionParser,关注其parse方法。
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 重点在这里,注册BeanPostProcessor
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
从registerAspectJAnnotationAutoProxyCreatorIfNecessary进入
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 注册AnnotationAwareAspectJAutoProxyCreator
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
继续向下
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
// 注意注入的是AnnotationAwareAspectJAutoProxyCreator
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
然后回到顶层的parse方法,注意第二步
private void extendBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinition beanDef =
parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
// 将include的Aspect注册进来
if (element.hasChildNodes()) {
addIncludePatterns(element, parserContext, beanDef);
}
}
private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
ManagedList<TypedStringValue> includePatterns = new ManagedList<TypedStringValue>();
NodeList childNodes = element.getChildNodes();
// 结合上文中的xml配置文件,此处遍历<aop:aspectj-autoproxy>的子节点
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node instanceof Element) {
Element includeElement = (Element) node;
// 提取需要include的Aspect
TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
valueHolder.setSource(parserContext.extractSource(includeElement));
includePatterns.add(valueHolder);
}
}
if (!includePatterns.isEmpty()) {
includePatterns.setSource(parserContext.extractSource(element));
// 将Aspect加到includePatterns属性里
beanDef.getPropertyValues().add("includePatterns", includePatterns);
}
}
至此,xml文件中的配置解析完毕,需要注册的组件(AnnotationAwareAspectJAutoProxyCreator)也注册完毕。由于AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor,因此它会介入到Bean初始化的过程中。
首先看一下其postProcessAfterInitialization(实际上其父类AbstractAutoProxyCreator中),如果读者看过Spring框架浅析 -- 数据库事务处理,就会发现其实Spring对声明式事务的支持,也用到了这个方法,只不过Spring声明式事务使用的是BeanFactoryTransactionAttributeSourceAdvisor。
前边部分都差不多,在获取Advisor候选集的时候,开始不一样了。
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
List<Advisor> advisors = super.findCandidateAdvisors();
// 按照xml中的配置,除调用父类的获取候选集的方法,还需要往Advisor候选集中追加一些Advisor
// Build Advisors for all AspectJ aspects in the bean factory.
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
顺着调用链继续向下,关注以下方法。
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
final String aspectName = maaif.getAspectMetadata().getAspectName();
validate(aspectClass);
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(maaif);
final List<Advisor> advisors = new LinkedList<Advisor>();
for (Method method : getAdvisorMethods(aspectClass)) {
// 关注这个方法
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
参照注释,看到getAdvisor方法,如下所示,其中关键点在于创建了InstantiationModelAwarePointcutAdvisor。这个Advisor后续会用到。
@Override
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif,
int declarationOrderInAspect, String aspectName) {
validate(aif.getAspectMetadata().getAspectClass());
AspectJExpressionPointcut ajexp =
getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
if (ajexp == null) {
return null;
}
return new InstantiationModelAwarePointcutAdvisorImpl(
this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
}
经过这个调用链,获取到了配置中所配置xxxAspect的元信息(有哪些增强逻辑,都在何处切入),并将其元信息包装到InstantiationModelAwarePointcutAdvisor中去,然后得到了Advisor的候选集。
之后的逻辑与Spring对声明式事务处理的支持差不多,循环判断当前BeanFactory中注册的Bean,基于候选集判断可作用在该bean上的Advisor,注意InstantiationModelAwarePointcutAdvisor实现的是PointcutAdvisor。
再之后就是创建proxy,这之后也与Spring框架浅析 -- 数据库事务处理中基本一致,这里不再赘述。简单说就是分为JDK或CGLib方式创建动态代理proxy,在运行时,外部对被代理类的方法调用的时候,其实是在调用proxy的代理方法,因此也就具备了切入增强逻辑的能力。
上一篇: Spring之Bean依赖注入