Spring系列(一):Spring MVC bean 解析、註冊、實例化流程源碼剖析

1.背景

最近在使用Spring MVC過程當中遇到了一些問題,網上搜索很多帖子後雖然找到了答案和解決方法,但這些答案大部分都只是給告終論,並無說明具體緣由,感受老是有點不太滿意。html

更重要的是這些所謂的結論大可能是抄來抄去,基本源自一家,真實性也有待考證。java

要成爲一名優秀的碼農,不只能熟練的複製粘貼,更要有打破砂鍋問到底的精神,達到知其然也知其因此然的境界。node

那做爲程序員怎麼能知其因此然呢?git

答案就是閱讀源代碼!程序員

此處請你們心裏默讀三遍。github

用過Spring 的人都知道其核心就是IOC和AOP,所以要想了解Spring機制就得先從這兩點入手,本文主要經過對IOC部分的機制進行介紹。web

2. 實驗環境

在開始閱讀以前,先準備好如下實驗材料。spring

IDEA 是一個優秀的開發工具,若是還在用Eclipse的建議切換到此工具進行。

IDEA有不少的快捷鍵,在分析過程當中建議你們多用Ctrl+Alt+B快捷鍵,能夠快速定位到實現函數。

3. Spring Bean 解析註冊

Spring bean的加載主要分爲如下6步:

  • (1)讀取XML配置文件
  • (2)XML文件解析爲document文檔
  • (3)解析bean
  • (4)註冊bean
  • (5)實例化bean
  • (6)獲取bean

3.1 讀取XML配置文件

查看源碼第一步是找到程序入口,再以入口爲突破口,一步步進行源碼跟蹤。

Java Web應用中的入口就是web.xml。

在web.xml找到ContextLoaderListener ,此Listener負責初始化Spring IOC。

contextConfigLocation參數設置了bean定義文件地址。

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:spring.xml</param-value>
</context-param>
複製代碼

下面是ContextLoaderListener的官方定義:

public class ContextLoaderListener extends ContextLoader implements ServletContextListener

Bootstrap listener to start up and shut down Spring's root WebApplicationContext. Simply delegates to ContextLoader as well as to ContextCleanupListener.

docs.spring.io/spring-fram…

翻譯過來ContextLoaderListener做用就是負責啓動和關閉Spring root WebApplicationContext。

具體WebApplicationContext是什麼?開始看源碼。

package org.springframework.web.context;
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }
    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }
    //servletContext初始化時候調用
    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext();
    }
    //servletContext銷燬時候調用
    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
    }
}
複製代碼

從源碼看出此Listener主要有兩個函數,一個負責初始化WebApplicationContext,一個負責銷燬。

繼續看initWebApplicationContext函數。

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//初始化Spring容器時若是發現servlet 容器中已存在根Spring容根器則拋出異常,證實rootWebApplicationContext只能有一個。
   if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
      throw new IllegalStateException(
            "Cannot initialize context because there is already a root application context present - " +
            "check whether you have multiple ContextLoader* definitions in your web.xml!");
   }
   if (this.context == null) {
	//1.建立webApplicationContext實例
        this.context = createWebApplicationContext(servletContext);
   }
   if (this.context instanceof ConfigurableWebApplicationContext) {
        ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
	 //2.配置WebApplicationContext
        configureAndRefreshWebApplicationContext(cwac, servletContext);
    }
    //3.把生成的webApplicationContext 設置爲root webApplicationContext。
    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
    return this.context; 

}
複製代碼

在上面的代碼中主要有兩個功能:

  • (1)建立WebApplicationContext實例。
  • (2)配置生成WebApplicationContext實例。
  • (3)把生成的webApplicationContext 設置爲root webApplicationContext。

3.1.1 建立WebApplicationContext實例

進入CreateWebAPPlicationContext函數

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
   //獲得ContextClass類,默認實例化的是XmlWebApplicationContext類
   Class<?> contextClass = determineContextClass(sc);
   //實例化Context類
   return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
複製代碼

進入determineContextClass函數。

protected Class<?> determineContextClass(ServletContext servletContext) {
   // 此處CONTEXT_CLASS_PARAM = "contextClass"String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
   if (contextClassName != null) {
         //若設置了contextClass則使用定義好的ContextClass。
         return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
      }
   else {
      //此處獲取的是在Spring源碼中ContextLoader.properties中配置的org.springframework.web.context.support.XmlWebApplicationContext類。
      contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
      return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
複製代碼

3.1.2 配置Web ApplicationContext

進入configureAndReFreshWebApplicaitonContext函數。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
  //webapplicationContext設置servletContext.
   wac.setServletContext(sc);
   // 此處CONFIG_LOCATION_PARAM = "contextConfigLocation",即讀即取web.xm中配設置的contextConfigLocation參數值,得到spring bean的配置文件.
   String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
   if (configLocationParam != null) {
      //webApplicationContext設置配置文件路徑設。
      wac.setConfigLocation(configLocationParam);
   }
   //開始處理bean
   wac.refresh();
}
複製代碼

3.2 解析XML文件

上面wac變量聲明爲ConfigurableWebApplicationContext類型,ConfigurableWebApplicationContext又繼承了WebApplicationContext。

WebApplication Context有不少實現類。 但從上面determineContextClass得知此處wac其實是XmlWebApplicationContext類,所以進入XmlWebApplication類查看其繼承的refresh()方法。

沿方法調用棧一層層看下去。

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      //獲取beanFactory
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
     // 實例化全部聲明爲非懶加載的單例bean 
      finishBeanFactoryInitialization(beanFactory);
    }
}
複製代碼

獲取beanFactory。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
     //初始化beanFactory
     refreshBeanFactory();
     return beanFactory;
}
複製代碼

beanFactory初始化。

@Override
protected final void refreshBeanFactory() throws BeansException {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      //加載bean定義
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
      this.beanFactory = beanFactory;
      }
}
複製代碼

加載bean。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   //建立XmlBeanDefinitionReader實例來解析XML配置文件
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
   initBeanDefinitionReader(beanDefinitionReader);
   //解析XML配置文件中的bean。
   loadBeanDefinitions(beanDefinitionReader);
}
複製代碼

讀取XML配置文件。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
//此處讀取的就是以前設置好的web.xml中配置文件地址
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      for (String configLocation : configLocations) {
         //調用XmlBeanDefinitionReader讀取XML配置文件
         reader.loadBeanDefinitions(configLocation);
      }
   }
}
複製代碼

XmlBeanDefinitionReader讀取XML文件中的bean定義。

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
   ResourceLoader resourceLoader = getResourceLoader();
      Resource resource = resourceLoader.getResource(location);
      //加載bean
      int loadCount = loadBeanDefinitions(resource);
      return loadCount;
   }
}
複製代碼

繼續查看loadBeanDefinitons函數調用棧,進入到XmlBeanDefinitioReader類的loadBeanDefinitions方法。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
      //獲取文件流
      InputStream inputStream = encodedResource.getResource().getInputStream();
      InputSource inputSource = new InputSource(inputStream);
     //從文件流中加載定義好的bean。
      return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
   }
}
複製代碼

最終將XML文件解析成Document文檔對象。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      //XML配置文件解析到Document實例中
      Document doc = doLoadDocument(inputSource, resource);
      //註冊bean
      return registerBeanDefinitions(doc, resource);
   }
複製代碼

3.3 解析bean

上一步完成了XML文件的解析工做,接下來將XML中定義的bean註冊到webApplicationContext,繼續跟蹤函數。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   //使用documentRedder實例讀取bean定義
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   }
複製代碼

用BeanDefinitionDocumentReader對象來註冊bean。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   //讀取document元素
   Element root = doc.getDocumentElement();
   //真正開始註冊bean
   doRegisterBeanDefinitions(root);
}
複製代碼

解析XML文檔。

protected void doRegisterBeanDefinitions(Element root) {
    //預處理XML 
   preProcessXml(root);
   //解析註冊bean
   parseBeanDefinitions(root, this.delegate);
   postProcessXml(root);
}
複製代碼

循環解析XML文檔中的每一個元素。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   //若是該元素屬於默認命名空間走此邏輯。Spring的默認namespace爲:http://www.springframework.org/schema/beans「
   if (delegate.isDefaultNamespace(root)) {
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
         Node node = nl.item(i);
         if (node instanceof Element) {
            Element ele = (Element) node;
            //對document中的每一個元素都判斷其所屬命名空間,而後走相應的解析邏輯
            if (delegate.isDefaultNamespace(ele)) {
               parseDefaultElement(ele, delegate);
            }
            else {
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      //若是該元素屬於自定義namespace走此邏輯 ,好比AOP,MVC等。
      delegate.parseCustomElement(root);
   }
}
複製代碼

下面是默認命名空間的解析邏輯。

不明白Spring的命名空間的能夠網上查一下,其實相似於package,用來區分變量來源,防止變量重名。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   //解析import元素
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   //解析alias元素
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   //解析bean元素
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   //解析beans元素
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}
複製代碼

這裏咱們就不一一跟蹤,以解析bean元素爲例繼續展開。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   //解析bean
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
         // 註冊bean
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
   }
}
複製代碼

解析bean元素,最後把每一個bean解析爲一個包含bean全部信息的BeanDefinitionHolder對象。

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
   String id = ele.getAttribute(ID_ATTRIBUTE);
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
複製代碼

3.4 註冊bean

接下來將解析到的bean註冊到webApplicationContext中。接下繼續跟蹤registerBeanDefinition函數。

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {
      // 獲取beanname
      String beanName = definitionHolder.getBeanName();
      //註冊bean
      registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
      // 註冊bean的別名
      String[] aliases = definitionHolder.getAliases();
      if (aliases != null) {
          for (String alias : aliases) {
             registry.registerAlias(beanName, alias);
     }
   }
}
複製代碼

跟蹤registerBeanDefinition函數,此函數將bean信息保存到到webApplicationContext的beanDefinitionMap變量中,該變量爲map類型,保存Spring 容器中全部的bean定義。

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {
	//把bean信息保存到beanDefinitionMap中
        this.beanDefinitionMap.put(beanName, beanDefinition);
	//把beanName 保存到List 類型的beanDefinitionNames屬性中
    this.beanDefinitionNames.add(beanName);
   }
複製代碼

3.5 實例化bean

Spring 實例化bean的時機有兩個。

一個是容器啓動時候,另外一個是真正調用的時候。

若是bean聲明爲scope=singleton且lazy-init=false,則容器啓動時候就實例化該bean(Spring 默認就是此行爲)。不然在調用時候再進行實例化。

相信用過Spring的同窗們都知道以上概念,可是爲何呢?

繼續從源碼角度進行分析,回到以前XmlWebApplication的refresh()方法。

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      //生成beanFactory,
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // 實例化全部聲明爲非懶加載的單例bean 
      finishBeanFactoryInitialization(beanFactory);
 }
複製代碼

能夠看到得到beanFactory後調用了 finishBeanFactoryInitialization()方法,繼續跟蹤此方法。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  // 初始化非懶加載的單例bean   
  beanFactory.preInstantiateSingletons();
}
複製代碼

預先實例化單例類邏輯。

public void preInstantiateSingletons() throws BeansException {
   // 獲取全部註冊的bean
   List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
   // 遍歷bean 
   for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      //若是bean是單例且非懶加載,則獲取實例
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            getBean(beanName);
      }
   }
}
複製代碼

獲取bean。

public Object getBean(String name) throws BeansException {
   return doGetBean(name, null, null, false);
}
複製代碼

doGetBean中處理的邏輯不少,爲了減小干擾,下面只顯示了建立bean的函數調用棧。

protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {
	//建立bean
       createBean(beanName, mbd, args);
}
複製代碼

建立bean。

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    return beanInstance;
}
複製代碼
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
      throws BeanCreationException {
      // 實例化bean   
      instanceWrapper = createBeanInstance(beanName, mbd, args);
}
複製代碼
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
   //實例化bean
   return instantiateBean(beanName, mbd);
}
複製代碼
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
      //調用實例化策略進行實例化
      beanInstance = getInstantiationStrategy().instantiate(mbd, beanName,
}
複製代碼

判斷哪一種動態代理方式實例化bean。

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
   //使用JDK動態代理
   if (bd.getMethodOverrides().isEmpty()) {
      return BeanUtils.instantiateClass(constructorToUse);
   }
   else {
     //使用CGLIB動態代理
     return instantiateWithMethodInjection(bd, beanName, owner);
   }
}
複製代碼

無論哪一種方式最終都是經過反射的形式完成了bean的實例化。

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) 
      ReflectionUtils.makeAccessible(ctor);
      return ctor.newInstance(args);
}
複製代碼

3.6 獲取bean

咱們繼續回到doGetBean函數,分析獲取bean的邏輯。

protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {
    //獲取beanName
     final String beanName = transformedBeanName(name);
     Object bean
    // 先檢查該bean是否爲單例且容器中是否已經存在例化的單例類
    Object sharedInstance = getSingleton(beanName);
    //若是已存在該bean的單例類
    if (sharedInstance != null && args == null) {
       bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    else{
          // 獲取父BeanFactory
          BeanFactory parentBeanFactory = getParentBeanFactory();
          //先判斷該容器中是否註冊了此bean,若是有則在該容器實例化bean,不然再到父容器實例化bean
          if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
               String nameToLookup = originalBeanName(name);
               // 若是父容器有該bean,則調用父beanFactory的方法得到該bean
               return (T) parentBeanFactory.getBean(nameToLookup, args);
           }
            //若是該bean有依賴bean,先實遞歸例化依賴bean。    
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                   registerDependentBean(dep, beanName);
                   getBean(dep);
                }
            }
    	    //若是scope爲Singleton執行此邏輯
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                   @Override
                   public Object getObject() throws BeansException {
    		//調用建立bean方法
                         return createBean(beanName, mbd, args);
                      }
                    
                   }
                });
             }
             //若是scope爲Prototype執行此邏輯,每次獲取時候都實例化一個bean
             else if (mbd.isPrototype()) {
                Object prototypeInstance = null;
                   prototypeInstance = createBean(beanName, mbd, args);
             }
             //若是scope爲Request,Session,GolbalSession執行此邏輯
             else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                   Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                      @Override
                      public Object getObject() throws BeansException {
                            return createBean(beanName, mbd, args);
                      }
                   });
             }
          }
}
      return (T) bean;
}
複製代碼

上面方法中首先調用getSingleton(beanName)方法來獲取單例bean,若是獲取到則直接返回該bean。方法調用棧以下:

public Object getSingleton(String beanName) {
   return getSingleton(beanName, true);
}
複製代碼
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//從singletonObjects中獲取bean。
   Object singletonObject = this.singletonObjects.get(beanName);
   return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
複製代碼

getSingleton方法先從singletonObjects屬性中獲取bean 對象,若是不爲空則返回該對象,不然返回null。

那 singletonObjects保存的是什麼?何時保存的呢?

回到doGetBean()函數繼續分析。若是singletonObjects沒有該bean的對象,進入到建立bean的邏輯。處理邏輯以下:

//獲取父beanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
//若是該容器中沒有註冊該bean,且父容器不爲空,則去父容器中獲取bean後返回
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
      return parentBeanFactory.getBean(nameToLookup, requiredType);
}

複製代碼

下面是判斷容器中有沒有註冊bean的邏輯,此處beanDefinitionMap相信你們都不陌生,在註冊bean的流程裏已經說過全部的bean信息都會保存到該變量中。

public boolean containsBeanDefinition(String beanName) {
   Assert.notNull(beanName, "Bean name must not be null");
   return this.beanDefinitionMap.containsKey(beanName);
}
複製代碼

若是該容器中已經註冊過bean,繼續往下走。先獲取該bean的依賴bean,若是鑹子依賴bean,則先遞歸獲取相應的依賴bean。

String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
    for (String dep : dependsOn) {
         registerDependentBean(dep, beanName);
         getBean(dep);
    }
}   
複製代碼

依賴bean建立完成後,接下來就是建立自身bean實例了。

獲取bean實例的處理邏輯有三種,即Singleton、Prototype、其它(request、session、global session),下面一一說明。

3.6.1 Singleton

若是bean是單例模式,執行此邏輯。

if (mbd.isSingleton()) {
       sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
          @Override
          public Object getObject() throws BeansException {
    	    //建立bean回調
            return createBean(beanName, mbd, args); 
       });
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
複製代碼

獲取單例bean,若是已經有該bean的對象直接返回。若是沒有則建立單例bean對象,並添加到容器的singletonObjects Map中,之後直接從singletonObjects直接獲取bean。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   synchronized (this.singletonObjects) {
      Object singletonObject = this.singletonObjects.get(beanName);
	  //若是singletonObjects中沒有該bean
      if (singletonObject == null) {
    	//回調參數傳進來的ObjectFactory的getObject方法,即調用createBean方法建立bean實例
            singletonObject = singletonFactory.getObject();
        //置新建立單例bean標誌位爲true。
           newSingleton = true;
         if (newSingleton) {
        //若是是新建立bean,註冊新生成bean對象
            addSingleton(beanName, singletonObject);
         }
      }
      //返回獲取的單例bean
      return (singletonObject != NULL_OBJECT ? singletonObject : null);
   }
}
複製代碼

把新生成的單例bean加入到類型爲MAP 的singletonObjects屬性中,這也就是前面singletonObjects()方法中獲取單例bean時今後Map中獲取的緣由。

protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
    //把新生成bean對象加入到singletonObjects屬性中。
      this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
 
      this.registeredSingletons.add(beanName);
   }
}
複製代碼

3.6.2 Prototype

Prototype是每次獲取該bean時候都新建一個bean,所以邏輯比較簡單,直接建立一個bean後返回。

else if (mbd.isPrototype()) {
    Object prototypeInstance = null;
    //建立bean
    prototypeInstance = createBean(beanName, mbd, args);
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
複製代碼

3.6.3 request、session、global session

else {
    //獲取該bean的scope   
    String scopeName = mbd.getScope();
    //獲取相應scope
    final Scope scope = this.scopes.get(scopeName);
    //獲取相應scope的實例化對象
    Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
             @Override
             public Object getObject() throws BeansException {
                   return createBean(beanName, mbd, args);
             }
    });
    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
   }
複製代碼

從相應scope獲取對象實例。

public Object get(String name, ObjectFactory<?> objectFactory) {
   RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
   //先從指定scope中獲取bean實例,若是沒有則新建,若是已經有直接返回
   Object scopedObject = attributes.getAttribute(name, getScope());
   if (scopedObject == null) {
      //回調函數調用createBean建立實例
      scopedObject = objectFactory.getObject();
      //建立實例後保存到相應scope中
      attributes.setAttribute(name, scopedObject, getScope());
      }
   }
   return scopedObject;
}
複製代碼

判斷scope,獲取實例函數邏輯。

public Object getAttribute(String name, int scope) {
   //scope是request時
   if (scope == SCOPE_REQUEST) {
      //從request中獲取實例
      return this.request.getAttribute(name);
   }
   else {
      PortletSession session = getSession(false);
      if (session != null) {
         //scope是globalSession時,從application中獲取實例
         if (scope == SCOPE_GLOBAL_SESSION) {
            //從globalSession中獲取實例
            Object value = session.getAttribute(name, PortletSession.APPLICATION_SCOPE);
            return value;
         }
         else {
            //從session中獲取實例
            Object value = session.getAttribute(name);
            return value;
         }
      }
      return null;
   }
}
複製代碼

在相應scope中設置實例函數邏輯。

public void setAttribute(String name, Object value, int scope) {
   if (scope == SCOPE_REQUEST) {
      this.request.setAttribute(name, value);
   }
   else {
      PortletSession session = getSession(true);
      if (scope == SCOPE_GLOBAL_SESSION) {
         session.setAttribute(name, value, PortletSession.APPLICATION_SCOPE);
      }
      else {
         session.setAttribute(name, value);
      }
   }
}
複製代碼

以上就是Spring bean從無到有的整個邏輯。

4. 小結

從源碼角度分析 bean的實例化流程到此基本接近尾聲了。

回到開頭的問題,ContextLoaderListener中初始化的WebApplicationContext究竟是什麼呢?

經過源碼的分析咱們知道WebApplicationContext負責了bean的建立、保存、獲取。其實也就是咱們平時所說的IOC容器,只不過名字表述不一樣而已。

5. 尾聲

本文主要是講解了XML配置文件中bean的解析、註冊、實例化。對於其它命名空間的解析尚未講到,後續的文章中會一一介紹。

但願經過本文讓你們在之後使用Spring的過程當中有「一切盡在掌控之中」的感受,而不只僅是稀裏糊塗的使用。

想要了解更多,關注公衆號:七分熟pizza

在這裏插入圖片描述
相關文章
相關標籤/搜索