美文网首页
Spring核心源码深度解析(五) invokeBeanFact

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

作者: 七天0 | 来源:发表于2020-07-08 14:02 被阅读0次

    ​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剩余部分进行深度分析。

    相关文章

      网友评论

          本文标题:Spring核心源码深度解析(五) invokeBeanFact

          本文链接:https://www.haomeiwen.com/subject/txpqcktx.html