Spring源碼分析:配置文件讀取流程

前言

Spring配置文件讀取流程原本是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一塊兒的,這兩天在看Spring自定義標籤的時候,感受對Spring配置文件讀取流程仍是研究得不夠,所以將Spring配置文件讀取流程部分從以前的文章拆出來單獨成爲一文。html

爲了看一下Spring配置文件加載流程,先定義一個bean.xml:node

1spring

2c#

3緩存

4數據結構

5dom

6ide

7函數

8源碼分析

9

10

11

12

13

14

15

<?xml version="1.0" encoding="UTF-8"?>

 <beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

 

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

 

    <bean id="beanPostProcessorBean" class="org.xrq.action.BeanPostProcessorBean" />

 

    <bean id="beanFactoryPostProcessorBean" class="org.xrq.action.BeanFactoryPostProcessorBean" />

 

    <bean id="multiFunctionBean" class="org.xrq.action.MultiFunctionBean" init-method="initMethod">

        <property name="propertyA" value="abc" />

    </bean>

 </beans>

至於Bean是什麼並不重要,有配置文件就夠了。

Bean定義加載流程—-從Refresh到Bean定義加載前

首先看一下Bean加載前整個代碼流程走向。Spring上下文刷新始於AbstractApplicationContext的refresh()方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

public void refresh() throws BeansException, IllegalStateException {

     synchronized (this.startupShutdownMonitor) {

     // Prepare this context for refreshing.

     prepareRefresh();

 

     // Tell the subclass to refresh the internal bean factory.

     ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

 

     // Prepare the bean factory for use in this context.

     prepareBeanFactory(beanFactory);

 

         ...

 }

代碼不全帖了,第7行的obtainFreshBeanFactory()方法進去:

1

2

3

4

5

6

7

8

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

refreshBeanFactory();

ConfigurableListableBeanFactory beanFactory = getBeanFactory();

if (logger.isDebugEnabled()) {

logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);

}

return beanFactory;

}

第2行的refreshBeanFactory()方法進去,它是AbstractApplicationContext的子類AbstractRefreshableApplicationContext中的方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

@Override

protected final void refreshBeanFactory() throws BeansException {

if (hasBeanFactory()) {

   destroyBeans();

   closeBeanFactory();

 }

 try{

   DefaultListableBeanFactory beanFactory = createBeanFactory();

   beanFactory.setSerializationId(getId());

   customizeBeanFactory(beanFactory);

   loadBeanDefinitions(beanFactory);

   synchronized (this.beanFactoryMonitor) {

   this.beanFactory = beanFactory;

   }

 }

 catch (IOException ex) {

 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);

 }

}

首先第8行獲取DefaultListableBeanFactory,而後執行第11行的方法,傳入當前獲取的BeanFactory,準備加載Bean定義。BeanFactory中有存儲了些什麼數據在【Spring源碼分析】Bean加載流程概覽一文中有畫表格詳細說明,看過表格的朋友應該知道爲何第8行要獲取的是DefaultListableBeanFactory而不是它的接口BeanFactory,由於Bean定義存儲在Map<String, BeanDefinition>中,這個Map的位置就是在DefaultListableBeanFactory裏,所以這裏直接獲取DefaultListableBeanFactory並做爲參數層層向後傳,加載完Bean定義後直接向Map<String, BeanDefinition>裏put鍵值對。

看下loadBeanDefinitions方法,它是AbstractRefreshableApplicationContext子類AbstractXmlApplicationContext中的一個方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {

    // Create a new XmlBeanDefinitionReader for the given BeanFactory.

    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

 

    // Configure the bean definition reader with this context's

    // resource loading environment.

    beanDefinitionReader.setResourceLoader(this);

    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

 

    // Allow a subclass to provide custom initialization of the reader,

    // then proceed with actually loading the bean definitions.

    initBeanDefinitionReader(beanDefinitionReader);

    loadBeanDefinitions(beanDefinitionReader);

}

第3行的XmlBeanDefinitionReader是Bean加載的核心類,先構建出來,後面代碼沒什麼值得看的,直接看第13行代碼,傳入XmlBeanDefinitionReader:

1

2

3

4

5

6

7

8

9

10

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {

    Resource[] configResources = getConfigResources();

    if (configResources != null) {

        reader.loadBeanDefinitions(configResources);

    }

    String[] configLocations = getConfigLocations();

    if (configLocations != null) {

        reader.loadBeanDefinitions(configLocations);

    }

}

由第8行的代碼進去,這個就不跟了,直接走到XmlBeanDefinitionReader的父類AbstractBeanDefinitionReader的loadBeanDefinitions方法中:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {

    ResourceLoader resourceLoader = getResourceLoader();

    if (resourceLoader == null) {

        throw new BeanDefinitionStoreException(

                "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");

    }

 

    if (resourceLoader instanceof ResourcePatternResolver) {

        // Resource pattern matching available.

        try {

            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);

            int loadCount = loadBeanDefinitions(resources);

            if (actualResources != null) {

                for (Resource resource : resources) {

                    actualResources.add(resource);

                }

            }

            if (logger.isDebugEnabled()) {

                logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");

            }

            return loadCount;

        }

        catch (IOException ex) {

            throw new BeanDefinitionStoreException(

                    "Could not resolve bean definition resource pattern [" + location + "]", ex);

        }

    }

    else {

        // Can only load single resources by absolute URL.

        Resource resource = resourceLoader.getResource(location);

        int loadCount = loadBeanDefinitions(resource);

        if (actualResources != null) {

            actualResources.add(resource);

        }

        if (logger.isDebugEnabled()) {

            logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");

        }

        return loadCount;

    }

}

咱們研究Spring加載流程使用的ClassPathXmlApplicationContext是ResourcePatternResolver的實現類,進入第8行的判斷,走第12行的方法,這裏也不跟了,很簡單,最終代碼走到了XmlBeanDefinitionReader的loadBeanDefinitions方法中,也就是Bean定義加載的開始。

Bean定義加載流程—-Bena定義的存儲

上面說到了Bean定義是存儲在DefaultListableBeanFactory中的,咱們來看一下具體代碼:

1

2

3

4

5

/** Map of bean definition objects, keyed by bean name */

 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

 

 /** List of bean definition names, in registration order */

 private final List<String> beanDefinitionNames = new ArrayList<String>();

最終DefaultListableBeanFactory會先遍歷beanDefinitionNames,從beanDefinitionMap中拿到對應的BeanDefinition,最終轉爲具體的Bean對象。BeanDefinition自己是一個接口,AbstractBeanDefinition這個抽象類存儲了Bean的屬性,看一下AbstractBeanDefinition這個抽象類的定義:

這個類的屬性與方法不少,這裏就列舉了一些最主要的方法和屬性,能夠看到包含了bean標籤中的全部屬性,以後就是根據AbstractBeanDefinition中的屬性值構造出對應的Bean對象。

Bean定義加載流程—-開始加載Bean定義

上面一部分的結尾說道,Bean定義加載的開始始於XmlBeanDefinitionReader的loadBeanDefinitions方法,看下loadBeanDefinitions方法定義:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {

    Assert.notNull(encodedResource, "EncodedResource must not be null");

    if (logger.isInfoEnabled()) {

        logger.info("Loading XML bean definitions from " + encodedResource.getResource());

    }

 

    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

    if (currentResources == null) {

        currentResources = new HashSet<EncodedResource>(4);

        this.resourcesCurrentlyBeingLoaded.set(currentResources);

    }

    if (!currentResources.add(encodedResource)) {

        throw new BeanDefinitionStoreException(

                "Detected cyclic loading of " + encodedResource + " - check your import definitions!");

    }

    try {

        InputStream inputStream = encodedResource.getResource().getInputStream();

        try {

            InputSource inputSource = new InputSource(inputStream);

            if (encodedResource.getEncoding() != null) {

                inputSource.setEncoding(encodedResource.getEncoding());

            }

            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());

        }

        finally {

            inputStream.close();

        }

    }

    catch (IOException ex) {

        throw new BeanDefinitionStoreException(

                "IOException parsing XML document from " + encodedResource.getResource(), ex);

    }

    finally {

        currentResources.remove(encodedResource);

        if (currentResources.isEmpty()) {

            this.resourcesCurrentlyBeingLoaded.remove();

        }

    }

}

第17行根據XML文件獲取輸入字節流,接着流程走到23行doLoadBeanDefinitions方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)

        throws BeanDefinitionStoreException {

    try {

        int validationMode = getValidationModeForResource(resource);

        Document doc = this.documentLoader.loadDocument(

                inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());

        return registerBeanDefinitions(doc, resource);

    }

    catch (BeanDefinitionStoreException ex) {

        throw ex;

    }

    catch (SAXParseException ex) {

        throw new XmlBeanDefinitionStoreException(resource.getDescription(),

                "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);

    }

    catch (SAXException ex) {

        throw new XmlBeanDefinitionStoreException(resource.getDescription(),

                "XML document from " + resource + " is invalid", ex);

    }

    catch (ParserConfigurationException ex) {

        throw new BeanDefinitionStoreException(resource.getDescription(),

                "Parser configuration exception parsing XML from " + resource, ex);

    }

    catch (IOException ex) {

        throw new BeanDefinitionStoreException(resource.getDescription(),

                "IOException parsing XML document from " + resource, ex);

    }

    catch (Throwable ex) {

        throw new BeanDefinitionStoreException(resource.getDescription(),

                "Unexpected exception parsing XML document from " + resource, ex);

    }

}

首先是第4行,獲取驗證模式,代碼不跟了,最終出來的是DetectMode,DetectMode的意思是XML文件的驗證模式由XML文件自己決定,若是是DTD那就使用DTD驗證,若是是XSD就使用XSD驗證

接着是第5行~第6行,這兩行的做用是經過DOM獲得org.w3c.dom.Document對象,Document將XML文件當作一棵樹,Dociument即對這顆樹數據結構的一個描述。

最近進入第7行,繼續加載Bean定義的流程,跟一下registerBeanDefinitions方法:

1

2

3

4

5

6

7

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {

    // Read document based on new BeanDefinitionDocumentReader SPI.

    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

    int countBefore = getRegistry().getBeanDefinitionCount();

    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

    return getRegistry().getBeanDefinitionCount() - countBefore;

}

由於是每一個XML文件執行一次registerBeanDefinitions方法註冊Bean定義,所以這整個方法的返回值表示的是當前XML裏面一共註冊了多少個Bean。直接進入第5行的代碼,使用BeanDefintionDocumentReader的registerBeanDefinitions方法來註冊Bean定義:

1

2

3

4

5

6

7

8

9

10

11

12

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {

    this.readerContext = readerContext;

 

    logger.debug("Loading bean definitions");

    Element root = doc.getDocumentElement();

 

    BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);

 

    preProcessXml(root);

    parseBeanDefinitions(root, delegate);

    postProcessXml(root);

}

這裏面的方法,第9行的方法preProcessXml是個空方法,留給子類擴展用;第11行的方法postProcessXml是個空方法,留給子類擴展用。

第5行的方法獲得根節點,也就是<beans …></beans>。

剩下的就是第7行的createHelper方法與第10行的parseBeanDefintions方法了,前者構造出一個Bean定義解析器的委託類,後者使用委託類解析Bean定義,下面分兩部分分別來看。

Bean定義加載流程—-createHelper

先看createHelper,即根據根節點建立一個Bean定義解析器的委託類,看一下代碼實現:

1

2

3

4

5

6

7

8

9

10

11

12

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {

    this.readerContext = readerContext;

 

    logger.debug("Loading bean definitions");

    Element root = doc.getDocumentElement();

 

    BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);

 

    preProcessXml(root);

    parseBeanDefinitions(root, delegate);

    postProcessXml(root);

}

第2行沒有什麼特別的,new一個BeanDefinitionParserDelegate出來,第3行的代碼跟一下,用於設置默認屬性:

1

2

3

4

public void initDefaults(Element root) {

populateDefaults(this.defaults, root);

this.readerContext.fireDefaultsRegistered(this.defaults);

}

跟一下第2行的代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

protected void populateDefaults(DocumentDefaultsDefinition defaults, Element root) {

    defaults.setLazyInit(root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE));

    defaults.setMerge(root.getAttribute(DEFAULT_MERGE_ATTRIBUTE));

    defaults.setAutowire(root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE));

    defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE));

    if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {

        defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));

    }

    if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {

        defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));

    }

    if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {

        defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));

    }

    defaults.setSource(this.readerContext.extractSource(root));

}

看到就是這個地方將<beans>標籤下的default-lazy-init、default_merge、default_autowire、default-dependency-check、default-autowire-candidates、default-init-method、default-destroy-method這幾個屬性取出來,設置到DocumentDefaultsDefinition即defaults中。

Bean定義加載流程—-parseBeanDefintions

到了parseBeanDefintions方法了,這個方法開始真正遍歷XML文件中的各個標籤並轉換爲對應的Bean定義,看一下方法定義:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {

    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;

                if (delegate.isDefaultNamespace(ele)) {

                    parseDefaultElement(ele, delegate);

                }

                else {

                    delegate.parseCustomElement(ele);

                }

            }

        }

    }

    else {

        delegate.parseCustomElement(root);

    }

}

首先說一下,這裏的Namespace都是默認的Namespace,至於Namespace的問題,和自定義Spring標籤相關,我想放到自定義Spring標籤部分說,這裏只要知道代碼會進入第2行與第9行的判斷便可。

第2行的判斷進去,都在遍歷Element下的節點不看了,直接跟第9行的代碼parseDefaultElement:

1

2

3

4

5

6

7

8

9

10

11

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {

    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {

        importBeanDefinitionResource(ele);

    }

    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {

        processAliasRegistration(ele);

    }

    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {

        processBeanDefinition(ele, delegate);

    }

}

這邊就是判斷節點名稱是import仍是alias仍是bean,是其中任意一個就進入相應的執行邏輯,import和alias不看了,這裏就看Bean加載流程部分,也就是第9行的processBeanDefinition方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

    if (bdHolder != null) {

        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

        try {

            // Register the final decorated instance.

            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

        }

        catch (BeanDefinitionStoreException ex) {

            getReaderContext().error("Failed to register bean definition with name '" +

                    bdHolder.getBeanName() + "'", ele, ex);

        }

        // Send registration event.

        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

    }

}

先看第4行,第4行的意思是在須要的時候裝飾Bean定義,好比AOP的場景會使用到,這個留在AOP的時候看這段代碼。

再看第7行,第7行的意思是註冊Bean定義,這在下一部分說,屬於Bean定義加載流程的最後一步。

如今看來第2行的代碼,顧名思義即解析Bean定義元素,跟一下代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {

    String id = ele.getAttribute(ID_ATTRIBUTE);

    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

 

    List<String> aliases = new ArrayList<String>();

    if (StringUtils.hasLength(nameAttr)) {

        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);

        aliases.addAll(Arrays.asList(nameArr));

    }

 

    String beanName = id;

    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {

        beanName = aliases.remove(0);

        if (logger.isDebugEnabled()) {

            logger.debug("No XML 'id' specified - using '" + beanName +

                    "' as bean name and " + aliases + " as aliases");

        }

    }

 

    if (containingBean == null) {

        checkNameUniqueness(beanName, aliases, ele);

    }

 

    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

    if (beanDefinition != null) {

        if (!StringUtils.hasText(beanName)) {

            try {

                if (containingBean != null) {

                    beanName = BeanDefinitionReaderUtils.generateBeanName(

                            beanDefinition, this.readerContext.getRegistry(), true);

                }

                else {

                    beanName = this.readerContext.generateBeanName(beanDefinition);

                    // Register an alias for the plain bean class name, if still possible,

                    // if the generator returned the class name plus a suffix.

                    // This is expected for Spring 1.2/2.0 backwards compatibility.

                    String beanClassName = beanDefinition.getBeanClassName();

                    if (beanClassName != null &&

                            beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&

                            !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {

                        aliases.add(beanClassName);

                    }

                }

                if (logger.isDebugEnabled()) {

                    logger.debug("Neither XML 'id' nor 'name' specified - " +

                            "using generated bean name [" + beanName + "]");

                }

            }

            catch (Exception ex) {

                error(ex.getMessage(), ele);

                return null;

            }

        }

        String[] aliasesArray = StringUtils.toStringArray(aliases);

        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);

    }

 

    return null;

}

第2行~第18行的代碼都是用於獲取BeanName,從這段邏輯中咱們能夠總結出BeanName定義的規則:

  • 默認的BeanName就是<bean>標籤重定義的id
  • <bean>標籤中能夠定義name屬性,一個bean能夠有多個別名(alias),都定義在name屬性中,不一樣的別名以」,;」分割,假如beanId未定義,那麼就以name屬性中的第一個別名做爲beanName

第20行~第22行的代碼主要用於確保BeanName的惟一性,跟一下第21行的方法就知道,BeanName與Bean別名都會放在Set<String>中,而後每次加載Bean定義的時候都會去這個Set<String>中檢查當前BeanName和Bean別名是否存在,若是存在就報錯。

接着進入第24行的代碼,開始解析Bean定義元素:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

public AbstractBeanDefinition parseBeanDefinitionElement(

        Element ele, String beanName, BeanDefinition containingBean) {

 

    this.parseState.push(new BeanEntry(beanName));

 

    String className = null;

    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {

        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();

    }

 

    try {

        String parent = null;

        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {

            parent = ele.getAttribute(PARENT_ATTRIBUTE);

        }

        AbstractBeanDefinition bd = createBeanDefinition(className, parent);

 

        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

 

        parseMetaElements(ele, bd);

        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

 

        parseConstructorArgElements(ele, bd);

        parsePropertyElements(ele, bd);

        parseQualifierElements(ele, bd);

 

        bd.setResource(this.readerContext.getResource());

        bd.setSource(extractSource(ele));

 

        return bd;

    }

    catch (ClassNotFoundException ex) {

        error("Bean class [" + className + "] not found", ele, ex);

    }

    catch (NoClassDefFoundError err) {

        error("Class that bean class [" + className + "] depends on not found", ele, err);

    }

    catch (Throwable ex) {

        error("Unexpected failure during bean definition parsing", ele, ex);

    }

    finally {

        this.parseState.pop();

    }

 

    return null;

}

對這個方法逐行總結一下:

  • 第4行,ParseState是一個基於棧的簡單結構,在解析的流程中用於追蹤邏輯上的位置,裏面存放的是Entry接口。Entry接口有各類實現類好比AdviceEntry、AdvisorEntry、BeanEntry、PropertyEntry等,每一步操做開始將一個Entry推送至棧頂,每一步操做結束將Entry從棧頂彈出,這裏將一個BeanEntry推送至棧頂,標識解析Bean定義開始
  • 第6行~第9行的代碼,用於獲取Bean對應的類路徑
  • 第12行~第15行的代碼,用於解析<bean>標籤中的parent屬性
  • 第16行的代碼,根據Bean對應的類路徑以及parent,生成一個AbstractBeanDefinition,AbstractBeanDefinition是一個抽象類,生成出來的具體實例爲GenericBeanDefinition
  • 第18行的代碼,就不跟了進去一看就很好理解,是用於解析Bean定義的屬性的,包括scope、sington、abstract、lazy-init、autowire、dependency-check、depends-on、autowire-candidate、primary、init-method、destory-method、factory-method、factory-bean,createHelper部分說了這個方法會建立一個名爲defaults的DocumentDefaultsDefinition,像lazy-init、autowire-candidate、init-method、destory-method未定義時都會嘗試從DocumentDefaultsDefinition中獲取
  • 第19行用於設置Bean描述
  • 第21行的方法parseMetaElements用於解析META元素
  • 第22行的方法parseLookupOverrideSubElements用於解析<bean>標籤下的<lookup-method>標籤
  • 第23行的方法parseReplacedMethodSubElements用於解析<bean>標籤下的<replaced-method>標籤,不過這個和<lookup-method>標籤好像不太經常使用
  • 第25行的方法parseConstructorArgElements用於解析<bean>標籤下的<constructor-arg>標籤,<constructor-arg>標籤用於實現構造函數注入Bean屬性
  • 第26行的方法parsePropertyElements用於解析<bean>標籤下的<property>標籤,<property>標籤是最多見的Bean屬性注入的方式
  • 第27行的方法parseQualifierElements用於解析<bean>標籤下的<qualifier>標籤,使用<qualifier>標籤也是Spring屬性注入的一種方式,不過不太經常使用

這樣,就把整個Bean定義加載的流程跟完了,最後一步,就是將AbstractBeanDefinition寫回到DefaultListableBeanFactory中了。

Bean定義加載流程—-Bean定義寫回DefaultListableBeanFactory

最後一步,將Bean定義寫回DefaultListableBeanFactory中。代碼要追溯回DefaultBeanDefinitionDocumentReader的processBeanDefinition方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

    if (bdHolder != null) {

        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

        try {

            // Register the final decorated instance.

            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

        }

        catch (BeanDefinitionStoreException ex) {

            getReaderContext().error("Failed to register bean definition with name '" +

                    bdHolder.getBeanName() + "'", ele, ex);

        }

        // Send registration event.

        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

    }

}

Bean定義加載完畢後構造爲一個BeanDefinitionHolder,第4行的代碼以前說過的,用於在必要的狀況下裝飾Bean定義先無論。

第7行的代碼用於註冊Bean定義,跟一下代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public static void registerBeanDefinition(

        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)

        throws BeanDefinitionStoreException {

 

    // Register bean definition under primary name.

    String beanName = definitionHolder.getBeanName();

    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

 

    // Register aliases for bean name, if any.

    String[] aliases = definitionHolder.getAliases();

    if (aliases != null) {

        for (String aliase : aliases) {

            registry.registerAlias(beanName, aliase);

        }

    }

}

跟一下第7行的方法,調用DefaultListableBeanFactory的registerBeanDefinition方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

        throws BeanDefinitionStoreException {

 

    Assert.hasText(beanName, "Bean name must not be empty");

    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

 

    if (beanDefinition instanceof AbstractBeanDefinition) {

        try {

            ((AbstractBeanDefinition) beanDefinition).validate();

        }

        catch (BeanDefinitionValidationException ex) {

            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,

                    "Validation of bean definition failed", ex);

        }

    }

 

    synchronized (this.beanDefinitionMap) {

        Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);

        if (oldBeanDefinition != null) {

            if (!this.allowBeanDefinitionOverriding) {

                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,

                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +

                        "': There is already [" + oldBeanDefinition + "] bound.");

            }

            else {

                if (this.logger.isInfoEnabled()) {

                    this.logger.info("Overriding bean definition for bean '" + beanName +

                            "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");

                }

            }

        }

        else {

            this.beanDefinitionNames.add(beanName);

            this.frozenBeanDefinitionNames = null;

        }

        this.beanDefinitionMap.put(beanName, beanDefinition);

 

        resetBeanDefinition(beanName);

    }

}

簡單說這個方法作了幾件事情:

  • beanDefinitionNames添加BeanName
  • beanDefinitionMap添加一對映射,Key爲BeanName,Value爲Bean定義
  • 第38行的方法用於重置一下全部本地緩存了的Bean定義

<bean>中不定義id及id重複場景Spring的處理方式

這兩天又想到了兩個細節問題,<bean>中不定義id或者id重複,這兩種場景Spring是如何處理的。首先看一下不定義id的場景,代碼在BeanDefinitionParserDelegate類第398行的這個判斷這裏:

1

2

3

4

5

6

7

8

9

10

11

if (beanDefinition != null) {

    if (!StringUtils.hasText(beanName)) {

        try {

            if (containingBean != null) {

                beanName = BeanDefinitionReaderUtils.generateBeanName(

                        beanDefinition, this.readerContext.getRegistry(), true);

            }

            else {

                beanName = this.readerContext.generateBeanName(beanDefinition);

...

}

當bean的id未定義時,即beanName爲空,進入第2行的if判斷。containingBean能夠看一下,這裏是由方法傳入的,是一個null值,所以進入第9行的判斷,即beanName由第9行的方法生成,看一下生成方式,代碼最終要追蹤到BeanDefinitionReaderUtils的generateBeanName方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

public static String generateBeanName(

        BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)

        throws BeanDefinitionStoreException {

 

    String generatedBeanName = definition.getBeanClassName();

    if (generatedBeanName == null) {

        if (definition.getParentName() != null) {

            generatedBeanName = definition.getParentName() + "$child";

        }

        else if (definition.getFactoryBeanName() != null) {

            generatedBeanName = definition.getFactoryBeanName() + "$created";

        }

    }

    if (!StringUtils.hasText(generatedBeanName)) {

        throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +

                "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");

    }

 

    String id = generatedBeanName;

    if (isInnerBean) {

        // Inner bean: generate identity hashcode suffix.

        id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);

    }

    else {

        // Top-level bean: use plain class name.

        // Increase counter until the id is unique.

        int counter = -1;

        while (counter == -1 || registry.containsBeanDefinition(id)) {

            counter++;

            id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;

        }

    }

    return id;

}

這段代碼的邏輯很容易看懂,即:

  • 假如是innerBean(好比Spring AOP產生的Bean),使用【類全路徑+#+對象HashCode的16進制】的格式來命名Bean
  • 假如不是innerBean,使用【類全路徑+#+數字】的格式來命名Bean,其中數字指的是,同一個Bean出現1次,只要該Bean沒有id,就從0開始依次向上累加,好比a.b.c#0、a.b.c#一、a.b.c#2

接着看一下id重複的場景Spring的處理方式,重複id是這樣的,Spring使用XmlBeanDefinitionReader讀取xml文件,在這個類的doLoadBeanDefinitions的方法中:

1

2

3

4

5

6

7

8

9

10

11

12

13

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)

    throws BeanDefinitionStoreException {

    try {

        int validationMode = getValidationModeForResource(resource);

        Document doc = this.documentLoader.loadDocument(

                inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());

        return registerBeanDefinitions(doc, resource);

    }

    catch (BeanDefinitionStoreException ex) {

        throw ex;

    }

    ...

}

第5行的代碼將xml解析成Document,這裏的解析使用的是JDK自帶的DocumentBuilder,DocumentBuilder處理xml文件輸入流,發現兩個<bean>中定義的id重複即會拋出XNIException異常,最終將致使Spring容器啓動失敗。

所以,結論就是:Spring不容許兩個<bean>定義相同的id。

相關文章
相關標籤/搜索