Spring深度源码解析(八)Spring AOP源码分析

Spring AOP

在上一章节,笔者对Spring-Mybatis的核心思想做了详细介绍,目的是让读者能够更加清晰的认知Spring的全局生命周期,以及Spring是如何设计对外扩展的开放,本章将对Spring AOP的原理及源码进行详细说明。

Spring AOP应用

首先还是对Spring AOP的基本功能简单介绍。切面配置类


image

被增强的类


image

配置类

image

新建一个测试类


image

输出结果:


image

通过执行结果我们可以看到我们不需要修改任何原有的代码就可以改变代码的逻辑,其实在上一篇文章中笔者就已经对动态代理有所介绍,比如在Mybatis中的Mapper接口就是通过JDK动态代理,只不过我们的Mybatis代理需求相对复杂,而Spring AOP相对单一,因为Spring AOP是对自己内部的对象代理,也就是说Spring AOP是先装入Spring容器,在由其他的其他组件进行代理,而Mybatis和其恰好消防,Mybatis是先代理在装入Spring,所以我们在上一篇文章中在将代理对象整合到Spring中遇到了很多问题,如果读者对此没有印象建议回顾《Spring深度源码解析(七) Spring-Mybatis核心思想》。

Spring AOP源码解析

在对Spring应用做了简单介绍后,我们先思考一下如果我们自己实现一个SpringAOP,如何去实现呢,如何动态的去改变类的创建方式呢?在《Spring源码深度分析(六)》笔者提到,在对Spring实例化前后、初始化前后,Spring都做了循环后置处理器的处理提供对外扩展,再思考如果我们是对类进行代理,那么会采用CGLIB的方式进行代理,为什么SpringAOP实现了切面代理,我们关注这个注解

image

进入这个注解

image

可以看到这里又有这个@Import注解,@Import什么用我就不介绍了,笔者在Spring-Mybatis做了大量文章。

image

这里我们又看到了ImportBeanDefinitionRegistrar,这里我在啰嗦几句Spring提供了很多种对外的拓展方式,ImportBeanDefinitionRegistrar就是其中一种,如果自己定的类实现了它就可以获取工厂注册器。


@Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

    AnnotationAttributes enableAspectJAutoProxy =
        AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    if (enableAspectJAutoProxy != null) {
      if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
      }
      if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
        AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
      }
    }

注意这段代码:

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
->registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
->registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
->registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);

其实就是向我们的工厂注册了这个一个类AnnotationAwareAspectJAutoProxyCreator,我们点开这么类

image

可以看到这个类实现了BeanPostProcessor,说明这个类同样是后置处理器,既然这是后置处理器,就可以改变我们工厂内部的对象,既然能够改变我们的对象是不是就能将我们的某些对象进行代理?答案是肯定的,那么这就解惑了我在上文提出的Spring通过什么手段完成的代理,读者是不是觉得Spring的后置处理器很神奇啊,到哪都有它,好了我们对AnnotationAwareAspectJAutoProxyCreator做了简要分析,接下来将对Spring AOP做详细介绍。Spring AOP源码

首先我们将代码停留在doCreateBean(),断点采用的是被AOP增强的person类

image

然后这里的instanceWrapper为空,我们下一步,

image

看到我们的person对象已经创建成功,注意我们发现这里的person对象并没有被代理,而仅仅是被创建出来,为什么Spring没有在创建对象的时候进行拦截代理呢?道理很简单,我们在IOC的文章提出了Spring在创建对象后还需要执行属性注入,生命周期等步骤,如果我们提前将对象进行代理,我们能否对对象实施属性注入、生命周期这都不能确定。那么Spring是在哪里进行代理的呢?我们将代码停在属性注入的下面

image

发现并没有被代理,我们执行下一步

image

发现该对象已经被我们的CGLIB进行代理,那么在这段代码里面就进做了什么操作呢?我们跟踪下一这段代码:exposedObject = initializeBean(beanName, exposedObject, mbd);我们一路跟踪停在这里

image

然后点击去

image

循环我们的后置处理器获取我们所需要的AspectJAwareAdvisorAutoProxyCreator->postProcessAfterInitialization

image

然后我们再度将代码停在这里

image

看名字我们大概能猜到这里就是代理的关键代码,我们跟进下去

image

然后获取一些adviors

image

我们其实可以猜到,这里就是获取我们的AspectTest的信息

image

好了,既然我们获取了我们的代理配置,那么就要开始对我们所符合标准的代理对象进行代理了

image

进createProxy方法在-》 proxyFactory.getProxy(getProxyClassLoader());

image

很明显这里就是真正创建代理的角色了,继续跟踪代码

image

这里就是根据什么样的需求创建什么样的代理方式,注意这里仅仅只是方式,并不是真正创建代理对象的逻辑,比如我们这里代理的是类,那么会选择CGLIB的方式

image

然后我们再回到前面的代码

image

可以看到createAopProxy()获取的就是我们创建代理的方式,至于我们的getProxy(),笔者就不多写了,内部就是如何使用cglib增强的代码,笔者在@Configuration中对如何通过cglib代理对象做了大量分析,这里就略过,那么我们已经知道了Spring AOP的代理流程,那么其实还缺少一样东西,不知读者是否发现我们的adviors并不知道什么时候就存在了呢?接下来我们就去解答这个问题,首先我们将代码停在doCreateBean():的上面,doCreateBean()我们在IOC创建Bean进行了详细介绍,就是开始创建Bean,那么对adviors的操作明显就是在创建Bean之前就完成了,为了验证我们将代码跟进:

image

进入代码

image

注意这个getBeanPostProcessors(),一共有九个,这里会循环获取

image

找到我们AOP注册的后置处理器,然后点进去

image

注意postProcessBeforeInstantiation,其实就是实例化之前的操作,Spring的命名十分规范,我们在上文介绍了这是在doCreateBean之前操作的,所以称为实例化之前,我们停留在这里

image

点击去shouldSkip,继续跟踪跟踪

image

然后进入

image

然后进入会发现这个buildAspectJAdvisors方法很长,我们直接找到关键代码

image

可以看到我们就是用过this.advisorFactory.getAdvisors(factory);这句代码获取我们切面的方法,我们在进入getAdvisors(factory)


@Override
  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;
  }

其实这里面就是通过反射的方式获取我们的方法,返回后在存起来交给我们后面对被代理类进行增强,至此Spring AOP源码分析到此为止,Spring 事物其实和我所提到Spring源码第七章和本章思想大致一致,笔者就不在对事物进行单独叙述了,下一章将会更新SpringMVC。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345