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

1.背景

最近在使用Spring MVC過程中遇到了一些問題,網(wǎng)上搜索不少帖子后雖然找到了答案和解決方法,但這些答案大部分都只是給了結論,并沒有說明具體原因,感覺總是有點不太滿意。

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

要成為一名優(yōu)秀的碼農(nóng),不僅能熟練的復制粘貼,更要有打破砂鍋問到底的精神,達到知其然也知其所以然的境界。

那作為程序員怎么能知其所以然呢?

答案就是閱讀源代碼!

此處請大家內(nèi)心默讀三遍。

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

2. 實驗環(huán)境

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

IDEA 是一個優(yōu)秀的開發(fā)工具,如果還在用Eclipse的建議切換到此工具進行。

IDEA有很多的快捷鍵,在分析過程中建議大家多用Ctrl+Alt+B快捷鍵,可以快速定位到實現(xiàn)函數(shù)。

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參數(shù)設置了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.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/context/ContextLoaderListener.html

翻譯過來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初始化時候調(diào)用
    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext();
    }
    //servletContext銷毀時候調(diào)用
    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
    }
}

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

繼續(xù)看initWebApplicationContext函數(shù)。

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//初始化Spring容器時如果發(fā)現(xiàn)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.創(chuàng)建webApplicationContext實例
        this.context = createWebApplicationContext(servletContext);
   }
   if (this.context instanceof ConfigurableWebApplicationContext) {
        ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
     //2.配置WebApplicationContext
        configureAndRefreshWebApplicationContext(cwac, servletContext);
    }
    //把生成的webApplicationContext 設置為root webApplicationContext。
    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
    return this.context; 

}

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

  • (1)創(chuàng)建WebApplicationContext實例。
  • (2)配置生成WebApplicationContext實例。

3.1.1 創(chuàng)建WebApplicationContext實例

進入CreateWebAPPlicationContext函數(shù)

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

進入determineContextClass函數(shù)。

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函數(shù)。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
  //webapplicationContext設置servletContext.
   wac.setServletContext(sc);
   // 此處CONFIG_LOCATION_PARAM = "contextConfigLocation",即讀即取web.xm中配設置的contextConfigLocation參數(shù)值,獲得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有很多實現(xiàn)類。
但從上面determineContextClass得知此處wac實際上是XmlWebApplicationContext類,因此進入XmlWebApplication類查看其繼承的refresh()方法。

沿方法調(diào)用棧一層層看下去。

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 {
   //創(chuàng)建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) {
         //調(diào)用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;
   }
}

繼續(xù)查看loadBeanDefinitons函數(shù)調(diào)用棧,進入到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,繼續(xù)跟蹤函數(shù)。

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);
}

循環(huán)解析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的命名空間的可以網(wǎng)上查一下,其實類似于package,用來區(qū)分變量來源,防止變量重名。

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元素為例繼續(xù)展開。

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中。接下繼續(xù)跟蹤registerBeanDefinition函數(shù)。

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函數(shù),此函數(shù)將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的時機有兩個。

一個是容器啟動時候,另一個是真正調(diào)用的時候。

如果bean聲明為scope=singleton且lazy-init=false,則容器啟動時候就實例化該bean(Spring 默認就是此行為)。否則在調(diào)用時候再進行實例化。

相信用過Spring的同學們都知道以上概念,但是為什么呢?

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

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      //生成beanFactory,
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // 實例化所有聲明為非懶加載的單例bean 
      finishBeanFactoryInitialization(beanFactory);
 }

可以看到獲得beanFactory后調(diào)用了 finishBeanFactoryInitialization()方法,繼續(xù)跟蹤此方法。

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中處理的邏輯很多,為了減少干擾,下面只顯示了創(chuàng)建bean的函數(shù)調(diào)用棧。

protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {
    //創(chuàng)建bean
       createBean(beanName, mbd, args);
}

創(chuàng)建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) {
      //調(diào)用實例化策略進行實例化
      beanInstance = getInstantiationStrategy().instantiate(mbd, beanName,
}

判斷哪種動態(tài)代理方式實例化bean。

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
   //使用JDK動態(tài)代理
   if (bd.getMethodOverrides().isEmpty()) {
      return BeanUtils.instantiateClass(constructorToUse);
   }
   else {
     //使用CGLIB動態(tài)代理
     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

我們繼續(xù)回到doGetBean函數(shù),分析獲取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是否為單例且容器中是否已經(jīng)存在例化的單例類
    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,則調(diào)用父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執(zhí)行此邏輯
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                   @Override
                   public Object getObject() throws BeansException {
            //調(diào)用創(chuàng)建bean方法
                         return createBean(beanName, mbd, args);
                      }
                    
                   }
                });
             }
             //如果scope為Prototype執(zhí)行此邏輯,每次獲取時候都實例化一個bean
             else if (mbd.isPrototype()) {
                Object prototypeInstance = null;
                   prototypeInstance = createBean(beanName, mbd, args);
             }
             //如果scope為Request,Session,GolbalSession執(zhí)行此邏輯
             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;
}

上面方法中首先調(diào)用getSingleton(beanName)方法來獲取單例bean,如果獲取到則直接返回該bean。方法調(diào)用棧如下:

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()函數(shù)繼續(xù)分析。如果singletonObjects沒有該bean的對象,進入到創(chuàng)建bean的邏輯。處理邏輯如下:

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

下面是判斷容器中有沒有注冊bean的邏輯,此處beanDefinitionMap相信大家都不陌生,在注冊bean的流程里已經(jīng)說過所有的bean信息都會保存到該變量中。

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

如果該容器中已經(jīng)注冊過bean,繼續(xù)往下走。先獲取該bean的依賴bean,如果镩子依賴bean,則先遞歸獲取相應的依賴bean。

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

依賴bean創(chuàng)建完成后,接下來就是創(chuàng)建自身bean實例了。

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

3.6.1 Singleton

如果bean是單例模式,執(zhí)行此邏輯。

if (mbd.isSingleton()) {
       sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
          @Override
          public Object getObject() throws BeansException {
            //創(chuàng)建bean回調(diào)
            return createBean(beanName, mbd, args); 
       });
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

獲取單例bean,如果已經(jīng)有該bean的對象直接返回。如果沒有則創(chuàng)建單例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) {
        //回調(diào)參數(shù)傳進來的ObjectFactory的getObject方法,即調(diào)用createBean方法創(chuàng)建bean實例
            singletonObject = singletonFactory.getObject();
        //置新創(chuàng)建單例bean標志位為true。
           newSingleton = true;
         if (newSingleton) {
        //如果是新創(chuàng)建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,因此邏輯比較簡單,直接創(chuàng)建一個bean后返回。

else if (mbd.isPrototype()) {
    Object prototypeInstance = null;
    //創(chuàng)建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實例,如果沒有則新建,如果已經(jīng)有直接返回
   Object scopedObject = attributes.getAttribute(name, getScope());
   if (scopedObject == null) {
      //回調(diào)函數(shù)調(diào)用createBean創(chuàng)建實例
      scopedObject = objectFactory.getObject();
      //創(chuàng)建實例后保存到相應scope中
      attributes.setAttribute(name, scopedObject, getScope());
      }
   }
   return scopedObject;
}

判斷scope,獲取實例函數(shù)邏輯。

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中設置實例函數(shù)邏輯。

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的創(chuàng)建、保存、獲取。其實也就是我們平時所說的IOC容器,只不過名字表述不同而已。

5. 尾聲

本文主要是講解了XML配置文件中bean的解析、注冊、實例化。對于其它命名空間的解析還沒有講到,后續(xù)的文章中會一一介紹。

希望通過本文讓大家在以后使用Spring的過程中有“一切盡在掌控之中”的感覺,而不僅僅是稀里糊涂的使用。

如果想獲得更多,歡迎關注公眾號:七分熟pizza

公眾號里我會分享更多技術以及職場方面的經(jīng)驗,大家有什么問題也可以直接在公眾號向我提問交流。

qrcode.jpg
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容