天天看點

《Spring從0到1》:配置建立、注入bean原理

作者:java小悠

手把手帶你看spring的配置建立、注入以及擷取bean。

看源碼最好的方式就是定義一個最最基本的流程,從開始debug到你認為懂了為止。

bean的配置

最基本的spring配置是将bean配置在spring的配置檔案中,該配置檔案還可以配置spring的配置屬性,雖然标簽不是<bean>,但是spring會将其注入到指定的類中,是以不論标簽如何,spring都會将配置檔案中的屬性轉化成類屬性,最終建立對應的對象。有的朋友可能已經注意到了:配置都是放在<beans>标簽中的。

《Spring從0到1》:配置建立、注入bean原理

bean的生成與注入

啟動spring的入口之一就是建立 ClassPathXmlApplicationContext 對象:

java複制代碼//建立Spring容器的對象:ApplicationContext的實作類 ----> ClassPathXmlApplicationContext
//ApplicationContext就是表示Spring容器,我們可以通過容器擷取對象
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
           

向下看之前不妨看一下該類的UML關系:

《Spring從0到1》:配置建立、注入bean原理

建立ClassPathXmlApplicationContext對象時就能看到整個spring建立對象的流程:

《Spring從0到1》:配置建立、注入bean原理

真正調用的是這裡:

《Spring從0到1》:配置建立、注入bean原理

這裡的setConfigLocations方法隻是将傳入的配置檔案名注入到本類的configLocations字元串數組變量中。

真正的流程是在refresh方法中,該方法的實作其實是在父類AbstractApplicationContext中:

《Spring從0到1》:配置建立、注入bean原理

将一些與主流程相關較小的代碼剔除後如下:

scss複制代碼public void refresh() throws BeansException, IllegalStateException {
   //在方法内對别的對象加鎖,使得鎖最小化。
   synchronized (this.startupShutdownMonitor) {
      //第一步就是建立bean的容器
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      prepareBeanFactory(beanFactory);

      try {
         
         postProcessBeanFactory(beanFactory);
         
         invokeBeanFactoryPostProcessors(beanFactory);

         registerBeanPostProcessors(beanFactory);

         finishBeanFactoryInitialization(beanFactory);
         
      } catch (BeansException ex) {...}

      finally {
         resetCommonCaches();
         contextRefresh.end();
      }
   }
}
           

逐漸解析:

  1. obtainFreshBeanFactory():
  2. 其中:refreshBeanFactory() 才是建立beanFactory的關鍵:其在 AbstractApplicationContext 類中是抽象方法,需要子類實作,通常調用的是 AbstractRefreshableApplicationContext 類。
  3. 其中 loadBeanDefinitions(beanFactory) 是最主要的,在子類 AbstractXmlApplicationContext 中 loadBeanDefinitions(beanFactory) 建立了 XmlBeanDefinitionReader 對象。該類封裝了讀配置檔案的邏輯。随後就是調用 XmlBeanDefinitionReader的方法 loadBeanDefinitions : 這個方法在其父類 AbstractBeanDefinitionReader 中實作: AbstractBeanDefinitionReader 将字元串類型的配置檔案名轉化為 Resource 對象,Resource是個接口,繼承了InputStreamSource,Spring中對于檔案的讀取類都需實作該接口。 而通過Resource加載類的方法是在子類 XmlBeanDefinitionReader 實作: 接着就是建立但是真正讀配置檔案的抽象類:BeanDefinitionDocumentReader。 registerBeanDefinitions 在子類 DefaultBeanDefinitionDocumentReader 中實作: 然後挨個解析每一個标簽 這裡的 BeanDefinitionHolder 要注意,這個就是解析bean标簽的生成對象,可以看出,bean 标簽裡設定的屬性都在該對象中,沒有配置的屬性會采用預設值(這也解釋了spring中建立的對象預設是懶加載的)。 随後将對象名和類定義放入【DefaultListableBeanFactory】的 beanDefinitionMap 中。
  4. 而事實上,後續的流程都是針對 DefaultListableBeanFactory類的。 就此,spring中一個完整的配置檔案中bean的解析、注冊就結束了!!!
  5. prepareBeanFactory(beanFactory)
  6. 該方法就隻是為Spring的BeanFactory指派。比如後置處理器,添加一些Spring内部對象等:
  7. scss複制代碼
  8. /** * 配置BeanFactory的一些上下文屬性,比如上下文類加載器和後置處理器 * @param 需要指派的beanFactory */ protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { //使用調用類的類加載器 beanFactory.setBeanClassLoader(getClassLoader()); if (!shouldIgnoreSpel) { beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); } beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // Configure the bean factory with context callbacks. beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class); // BeanFactory interface not registered as resolvable type in a plain factory. // MessageSource registered (and found for autowiring) as a bean. beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // Register early post-processor for detecting inner beans as ApplicationListeners. beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // Detect a LoadTimeWeaver and prepare for weaving, if found. if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // 注冊預設的環境類對象 if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) { beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup()); } }
  9. invokeBeanFactoryPostProcessors(beanFactory):
  10. 該方法主要是加載和執行個體化 BeanFactory 後置處理器(所有實作了 BeanFactoryPostProcessor 接口的類)。後置處理器可以在 Bean 執行個體化、屬性填充、初始化和銷毀等不同的階段對 Bean 進行自定義的處理和增強。典型的後處理器就是org.mybatis.spring.mapper.MapperScannerConfigurer,該類将指定的mapper接口注冊到mybatis的Configuration類的MapperRegistry屬性中。
  11. registerBeanPostProcessors(beanFactory):
  12. 主要是将上一步執行個體化的後置處理器對象添加到 beanFactory 的 beanPostProcessors 變量中。
  13. finishBeanFactoryInitialization(beanFactory):
  14. 從方法名也能看出該方法是最終的一步:初始化所有的單例模式,所有的非懶加載的類。該方法内封裝的其實就是之前建構的 ConfigurableListableBeanFactory 接口實作類的方法,spring中該接口的實作類用的是 DefaultListableBeanFactory
《Spring從0到1》:配置建立、注入bean原理
《Spring從0到1》:配置建立、注入bean原理

可以看出,初始化類的時候是會根據類是否是工廠類而作出額外的初始化,最重要的還是 getBean方法。 該方法是 ClassPathXmlApplication 的父類 AbstractApplicationContext 實作的:

《Spring從0到1》:配置建立、注入bean原理

而 doGetBean 也是 AbstractBeanFactory 實作的:

scss複制代碼protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException {

   String beanName = transformedBeanName(name);
   Object beanInstance;

   // 看看有無該類對象的緩存,有則直接傳回
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      if (logger.isTraceEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }else {
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      // 如果有父類的BeanFactory對象則調用父類BeanFactory獨享的doGetBean方法。
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         // Not found -> check parent.
         String nameToLookup = originalBeanName(name);
         if (parentBeanFactory instanceof AbstractBeanFactory) {
            return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                  nameToLookup, requiredType, args, typeCheckOnly);
         }
         else if (args != null) {
            // Delegation to parent with explicit args.
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         }
         else if (requiredType != null) {
            // No args -> delegate to standard getBean method.
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         }
         else {
            return (T) parentBeanFactory.getBean(nameToLookup);
         }
      }

      if (!typeCheckOnly) {
         markBeanAsCreated(beanName);
      }

      StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
            .tag("beanName", name);
      try {
         if (requiredType != null) {
            beanCreation.tag("beanType", requiredType::toString);
         }
         RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         // 先初始化要初始化對象依賴的對象,其實就是遞歸調用getBean。
         String[] dependsOn = mbd.getDependsOn();
         if (dependsOn != null) {
            for (String dep : dependsOn) {
               if (isDependent(beanName, dep)) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
               }
               registerDependentBean(dep, beanName);
               try {
               
                  getBean(dep);
               }
               catch (NoSuchBeanDefinitionException ex) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
               }
            }
         }

         // 單例對象的初始化
         if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  destroySingleton(beanName);
                  throw ex;
               }
            });
            beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
         //原型模式對象的建立
         else if (mbd.isPrototype()) {
            // It's a prototype -> create a new instance.
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }

         else {
            //Spring2.0之後又引入了另外三種scope類型:request、session、global session類型
            //隻能在隻能在Web應用中使用,若隻是背景程序的話則不會走該分支。
            String scopeName = mbd.getScope();
            if (!StringUtils.hasLength(scopeName)) {
               throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
            }
            Scope scope = this.scopes.get(scopeName);
            if (scope == null) {
               throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
            }
            try {
               Object scopedInstance = scope.get(beanName, () -> {
                  beforePrototypeCreation(beanName);
                  try {
                     return createBean(beanName, mbd, args);
                  }
                  finally {
                     afterPrototypeCreation(beanName);
                  }
               });
               beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
            }
            catch (IllegalStateException ex) {
               throw new ScopeNotActiveException(beanName, scopeName, ex);
            }
         }
      }
      catch (BeansException ex) {
         beanCreation.tag("exception", ex.getClass().toString());
         beanCreation.tag("message", String.valueOf(ex.getMessage()));
         cleanupAfterBeanCreationFailure(beanName);
         throw ex;
      }
      finally {
         beanCreation.end();
      }
   }

   return adaptBeanInstance(name, beanInstance, requiredType);
}
           

doGetBean 方法是先看看該對象是否已經初始化,如果沒有在建立并初始化,值得注意的是,其是先建立并初始化目前要建立的對象的依賴(還是調用doGetbean),最後再根據對象類型分開建立,但是核心是調用 父類 AbstractAutowireCapableBeanFactory 的 createBean 方法。

《Spring從0到1》:配置建立、注入bean原理
《Spring從0到1》:配置建立、注入bean原理

createBeanInstance則很有意思,其是先判斷是否由指定工廠建立,再有無注冊的構造方法,最後才會通過反射擷取該類的構造方法構造對象。

《Spring從0到1》:配置建立、注入bean原理
《Spring從0到1》:配置建立、注入bean原理

instantiate方法在 SimpleInstantiationStrategy類中。

《Spring從0到1》:配置建立、注入bean原理

而 BeanUtil.instantiateClass方法則是封裝的的通過構造器擷取對象。

《Spring從0到1》:配置建立、注入bean原理

由此,一個對象就被建立結束,而後雖然會對該對象進行一系列封裝,但是歸根結底,spring的bean建立還是上述這些過程。

bean的擷取

其實通過 ClassPathXmlApplicationContext 對象擷取指定類的對象的 getBean 方法其實就是第5步中的方法,對于非懶加載類對象就可以直接擷取,而懶加載類的對象則在此刻建構。

最後再附上一張用到的UML圖:

《Spring從0到1》:配置建立、注入bean原理
原文連結:https://juejin.cn/post/7261566651605532727

繼續閱讀