Friday, April 18, 2014

Spring BeanDefinition

In this article, we will discuss the basic flow involved in loading the bean definitions.

A bean factory can be created from multiple locations. Each location pattern can result in more than one actual resource of bean definitions.We will only touch the outer flow and not the actual parsing of bean definitions.

Basic flow

A bean factory can be created from context files residing in more than one location path. In the below test, we pass in two location paths, the first location is of pattern "context*.xml" and other is the actual file name "aliasContext.xml". The first location results in three context files context1.xml, context2.xml and context3.xml. The third context file, context3.xml, has import element which imports another context file testImport.xml.


    public void testBasicFlow() {
        System.setProperty("spring.profiles.active", "dev");
        DefaultListableBeanFactory testNameFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(testNameFactory, events);
        xmlBeanDefinitionReader.loadBeanDefinitions(
"classpath*:org/springframework/beans/factory/xml/context*.xml", 
"classpath*:org/springframework/beans/factory/xml/aliasContext.xml");
...
    }

The context files

The contents of the context files have been chosen so as to understand the main flow. It has "bean", nested "beans", "import" and alias  elements. Each element will be discussed in detail in my coming posts.

context1.xml

Other than the "bean" elements, it has two other nested "beans" element for different profiles.

<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.1.xsd"
       profile="dev">
    <bean id="bean1" class="org.springframework.tests.sample.beans.TestBean"/>
    <bean id="bean2" class="org.springframework.tests.sample.beans.TestBean"/>
    <beans profile="dev">
        <bean id="devBean1" class="org.springframework.tests.sample.beans.TestBean"/>
    </beans>
    <beans profile="test">
        <bean id="testBean1" class="org.springframework.tests.sample.beans.TestBean"/>
    </beans>
</beans>

context2.xml 

It just has one "bean" element.

<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.1.xsd">
    <bean id="bean3" class="org.springframework.tests.sample.beans.TestBean"/>
</beans>

context3.xml 

It has an "import" element which imports resources from the location path in the resource attribute.

<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.1.xsd">
    <import resource="testImport.xml"/>
</beans>

aliasContext.xml

It has an "alias" element to register aliases.

<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.1.xsd">
    <alias name="bean1" alias="bean1Alias"/>
</beans>

testImport.xml

This is imported from context3.xml. It just has one "bean" element.

<?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.1.xsd">
    <bean id="importedBean" class="org.springframework.tests.sample.beans.TestBean"/>
</beans>

Unit test

We will now fill in assert statements to our unit test to confirm that the beans (beans from import and the nested beans) and alias are registered.


public void testBasicFlow() {
        System.setProperty("spring.profiles.active", "dev");
        DefaultListableBeanFactory testNameFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(testNameFactory, events);
        xmlBeanDefinitionReader.loadBeanDefinitions(
"classpath*:org/springframework/beans/factory/xml/context*.xml", 
"classpath*:org/springframework/beans/factory/xml/aliasContext.xml");

        assertTrue(testNameFactory.containsBeanDefinition("bean1"));
        assertTrue(testNameFactory.containsBeanDefinition("bean2"));
        assertTrue(testNameFactory.containsBeanDefinition("devBean1"));
        assertFalse(testNameFactory.containsBeanDefinition("testBean1"));
        assertTrue(testNameFactory.containsBeanDefinition("bean3"));
        assertTrue(testNameFactory.containsBeanDefinition("importedBean"));
        assertTrue(testNameFactory.isAlias("bean1Alias"));
    }


Flow diagram

The location patterns are converted to actual resources. Each resource is loaded into a document which is parsed to register the beans. The profile is checked to make sure the beans belong to one of the active profiles. If not, the beans element will be ignored  First the beans default attributes are parsed and then each of the elements within the beans element are parsed. Each context file contains one or more bean element. Other than bean elements, we may also have one or more of the below elements. Once the bean element is parsed, a bean definition will be created and registered in the factory.

  1. import
  2. alias
  3. beans


Basic flow of loading bean definitions -1
Basic flow of loading bean definitions -1


Home 

No comments:

Post a Comment