Spring Beans initialization

Posted by fazbob on Sat, 04 Dec 2021 03:10:06 +0100

In the Spring Framework, all objects are managed as beans, which are initialized, stored and destroyed by the IoC container.

Inside the IoC container, such beans are specifically represented by BeanDefinition, which defines the following information:

  • Full path class name
  • Behavior characteristics of Bean (such as scope scope, life cycle callback, etc.)
  • Dependency information of Bean
  • Other configuration information

1. Definition of bean

In the Spring Framework, each Bean has one or more unique identifiers. Beans can be configured in the form of traditional XML or Java Config.

In the meta information configuration of XML, you can use id or name to identify beans. Where, name can set multiple values, using, or; Or separated by spaces. When no id or name is specified, Spring generates a default identity.

The unique identifier of the Bean is automatically generated. Follow the Java standard rules, that is, use the java.beans.Introspector#decapitalize method. If the first two letters are uppercase, they will not be transformed. Otherwise, the first letter will be lowercase

2. Initialize Bean

Bean initialization mainly includes the following three methods:

  • Construction method
  • Static factory method
  • Factory method

First, let's look at how the constructor is initialized
Create a new maven project and introduce Spring Framework related dependencies, as follows:

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <springFramework.version>5.2.0.RELEASE</springFramework.version>
    </properties>

     <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${springFramework.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
     </dependencies>

Introducing the spring context module actually introduces several modules of the Spring Framework, such as spring core, spring beans, spring AOP, spring expression, etc.

2.1 constructor mode

First, let's look at the form of XML and create a common PoJo object, as follows:

package com.vielat.springframework.bean.service;

/**
 * TODO
 *
 * @author Michael
 * @since 2021/12/3
 */
public class ConstructorClientService {

}

Create a new META-INF/applicationContext.xml file in src/main/resources directory, with the following contents:

<?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
       https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="service" class="com.vielat.springframework.bean.service.ConstructorClientService" />

</beans>

Write Junit test class in src/test/java directory, as follows:

public class XmlApplicationDemo {

    @Test
    public void testConstructorInitializing(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/applicationContext.xml");
        System.out.println(ac.getBean("service"));
    }
}

After running Junit test class, the execution results are as follows:

com.vielat.springframework.bean.service.ConstructorClientService@77ec78b9

Process finished with exit code 0

If a constructor with parameters is added to the PoJo, it is as follows:

public class ConstructorClientService {

    public ConstructorClientService(String name){

    }
}

If the Junit test class is executed again, the following exception will be thrown:

December 03, 2021 7:57:00 morning org.springframework.context.support.AbstractApplicationContext refresh
 warning: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service' defined in class path resource [META-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.vielat.springframework.bean.service.ConstructorClientService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service' defined in class path resource [META-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.vielat.springframework.bean.service.ConstructorClientService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()

	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1320)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1214)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
	at com.vielat.springframework.bean.XmlApplicationDemo.testConstructorInitializing(XmlApplicationDemo.java:27)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.vielat.springframework.bean.service.ConstructorClientService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:83)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1312)
	... 38 more
Caused by: java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78)
	... 39 more

As can be seen from the above logs, if only the id and class attributes are configured for the bean tag in the XML (the id attribute can be omitted), it will be initialized directly with the parameterless constructor of the object corresponding to the class. If the object does not provide a parameterless constructor, an exception will be thrown. If you want to use a constructor with parameters for initialization, you need to configure it in the XML configuration, as follows:

...
  <bean id="service" class="com.vielat.springframework.bean.service.ConstructorClientService">
        <constructor-arg name="name" value="Michael" />
  </bean>

In addition to the name field attribute, the above configuration can also be specified by index subscript.
In addition, after Spring Framework 3.1, a more simplified form of namespace is provided, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="service" class="com.vielat.springframework.bean.service.ConstructorClientService">
        <constructor-arg name="name" value="Michael" />
    </bean>

    <bean id="service2" class="com.vielat.springframework.bean.service.ConstructorClientService" c:name="Michael">
    </bean>

</beans>

You need to add xmlns:c to the beans signature=“ http://www.springframework.org/schema/c ", when specifying construction parameters, it can be directly configured in the form of c: attribute name.

2.2 static factory mode

Write a static factory class as follows:

public class ClientService {

    private static final ClientService service = new ClientService();

    public static ClientService getInstance() {
        return service;
    }

    public ClientService getService(){
        return service;
    }
}

The XML configuration information is as follows:

 <bean name="clientService" class="com.vielat.springframework.bean.service.ClientService" factory-method="getInstance" />

The test classes are as follows:

    @Test
    public void testStaticFactoryInitializing(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/application.xml");
        System.out.println(ac.getBean("clientService"));
    }

2.3 factory method

Similar to the static factory method, the object is initialized through FactoryBean, but the initialization method is not decorated with static.
The factory method is defined as follows:

public class DefaultServiceLocator {
    public ClientService createClientService() {
        return new ClientService();
    }
}

XML is defined as follows:

  <bean id="defaultServiceLocator" class="com.vielat.springframework.bean.service.DefaultServiceLocator"/>
  <bean id="serviceLocator" factory-bean="defaultServiceLocator" factory-method="createClientService" />

The test classes are as follows:

    @Test
    public void testFactoryInitializing() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/applicationContext.xml");
        System.out.println(ac.getBean("serviceLocator"));
    }

Whether it is a static factory or an ordinary factory class, you can initialize multiple beans. You only need to define multiple methods in the factory class. The code is omitted. Such factories are called factorybeans