Spring核心源码深度解析(五) invokeBeanFactoryPostProcessors(下)

​invokeBeanFactoryPostProcessors(下)

在上一小节我们分析完了invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);这个方法干了那些事情,现在我们继续对后文做分析,如图:
image

上图Spring进行循环,判断是否有遗漏掉的后置处理器没有解析,一般情况下,currentRegistryProcessors会是0,我们运行代码都会直接跳过,继续往下走:


image

这里是Spring最后一次对BeanDefinitionRegistryPostProcessor后置处理器进行解析,主要是针对一些父类层级关系,通常也为0,所以依旧不会有什么作用。好了,接下来到了关键代码了


image

我们对invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);进行跟踪->postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) ,我们将代码停在下图:


image

注意AppConfig.class的变化


image

这里它还是一个普通的类,我们调试下一步


image

可以发现,Spring将其增强成为了cglib增强类,那么AppConfig为什么会增强呢?我们下面进行详细分析

@Configuration

当我们将 @Configuration取消时,


image

我们调试停在上面的代码


image

可以看到这里的配置类并没有被增强,所以可以得出结论,我们的@Configuration可以将我们的普通类进行增强,不知读者是否还记得我们在上一章所讲full吗,如果是 @Configuration类,Spring会认为这是一个配置类就会将其标识full,而这个full就是代表配置类是否需要增强,后面我们会在源码中进行证明,好了,说了这么多,相信读者应该对 @Configuration
有一定的了解,现在我们来探讨为什么Spring要增强 配置类,这里我们对加上@Configuration和取消@Configuration进行测试,我们看下面代码:


image

第一种情况-》取消@Configuration


image

第二种情况-》加上@Configuration


image

我们可以看到有了@Configuration,我们的user1()和user2()方法返回的对象是同一个对象,当然这里还有第三种情况加上static,读者可以自行测试看看又是什么情况,结果会发现代理功能会失效,至于为什么会失效我们在后面的源码会详细说明。好了我们大概知道了@Configuration的作用,那么Spring是如何对@Configuration进行增强的呢,我们回到源码中:


image

进入enhanceConfigurationClasses

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
​
    //找到config注解类,这里就可以看到full和lite了,如果full会变成增强类,目的是为了实现单例
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
      BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
      if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
        if (!(beanDef instanceof AbstractBeanDefinition)) {
          throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
              beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
        }
        else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
          logger.info("Cannot enhance @Configuration bean definition '" + beanName +
              "' since its singleton instance has been created too early. The typical cause " +
              "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
              "return type: Consider declaring such methods as 'static'.");
        }
        configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
      }
    }
    if (configBeanDefs.isEmpty()) {
      // nothing to enhance -> return immediately
      return;
    }
​
    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
      AbstractBeanDefinition beanDef = entry.getValue();
      // If a @Configuration class gets proxied, always proxy the target class
      beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
      try {
        // Set enhanced subclass of the user-specified bean class
        Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
        if (configClass != null) {
          Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
          if (configClass != enhancedClass) {
            if (logger.isTraceEnabled()) {
              logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
                  "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
            }
            beanDef.setBeanClass(enhancedClass);
          }
        }
      }
      catch (Throwable ex) {
        throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
      }
    }
  }

这里(ConfigurationClassUtils.isFullConfigurationClass(beanDef))会判断有没有标识了FULL的配置类,如果没有下面configBeanDefs
会为空直接返回,如果不为空,对配置类增强,我们锁定这段代码Class<?> enhancedClass = enhancer.enhance(configClass,
this.beanClassLoader);-》Class<?> enhancedClass createClass(newEnhancer(configClass,classLoader));-》
newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader)最后来到了这里:


image

在对@Configuration做详细分析时,笔者可以先自行思考,Spring对配置类进行增强之后如何做到采用不同的方式去创建对象,为什么在Spring的容器里都只会存放一份Bean,如果要实现这种功能,是不是需要改变创建对象的方式呢?那么既然我们的工厂我们的@Bean.class是创建对象交给Spring管理,那么如果我们创建对象的方式先通过工厂里面获取,如果获取不到我们再创建对象交给Spring管理是不是就能够实现呢?好了,我想读者应该有些思路了,我们再看一张图


image

图上大致讲解了配置类转变为增强类后大概是什么样子的,从图上我们可以看到增强代理类有个私有变量,这个变量存放着我们的工厂,那么为什么需要这个工厂我想读者应该有所顿悟。好了我们再回到源码:


image

这里Spring是采用CGLIB增强,如果读者没有了解过CGLIB的话,最好在网上了解下CGLIB的使用,这里不再赘述。首先将目标类交给enhancer,其次添加了一个接口,EnhanceConfiguration.class,这个类是CGLIB实现增强的核心,我们点进这个接口会发现它实现了BeanFactoryAwae接口,然后我们继续往下走,看到setStrategy(newBeanFactoryAwareGeneratorStrategy(classLoader)),我们点进去
image
image

如图我们可以大概才到,Spring在这里添加了一个策略,这个策略是声明一个变量名为"$$beanFactory"的公共属性,再回想我们之前添加的接口是不是可以得到工厂,那么我么你现在还差一步,如何将前面的原料加工就是我们代理需要去做的事情:


image

这个Callback笔者就不分析了,因为涉及到CGLIB的知识,如果读者有情趣可以自行研究。好了我们生成代理后还差一件事情,那就是如何将我们拿到的工厂赋值给代理类得变量中,看下面代码


image

我们点进去


image

这里读者是不是豁然开朗,是不是觉得Spring的设计如神一般的存在啊,好了当我们了解到这里其实
invokeBeanFactoryPostProcessors这个方法也差不多结束了,当然还有一些更细节的地方笔者会作为单独的章节进行描述,哎,总算是到了一半了,什么才一半?,毕竟我们真正的对象实例还没开始呢,好的我们在下一小节会对Spring剩余部分进行深度分析。

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