Skip to content

Spring Bean Scopes And Lifecycle XML#

Example Singleton Bean Scope XML#

Dependencies#

pom.xml
1
2
3
4
5
6
7
8
9
....  

<dependency>  
    <groupId>org.springframework</groupId>  
    <artifactId>spring-context</artifactId>  
    <version>5.3.24</version>  
</dependency>

....

Configure The Spring Bean#

  • Let's create an interface and implementation class as below.
Coach.java
1
2
3
4
5
6
7
package com.spring.core.spring.bean.scopes.and.lifecycle;

public interface Coach {

    public String getDailyHomeWork();

}
EnglishCoach.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package com.spring.core.spring.bean.scopes.and.lifecycle;

public class EnglishCoach implements Coach {

    @Override
    public String getDailyHomeWork() {
        return "Spend 1 hour to practise Speaking Skill!";
    }

}
  • Then in the resources let's create an applicationContext.xml for configuring Spring beans.
applicationContext.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="englishCoach" class="com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach">

    </bean>

</beans>

Testing#

  • Now, let's create the SpringApplication class for getting and using bean englishCoach as below.
SpringApplication.java
 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
package com.spring.core.spring.bean.scopes.and.lifecycle;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {

        //Load Spring Configuration File
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //retrieve bean from the spring container
        Coach englishCoach = context.getBean("englishCoach", Coach.class);
        Coach englishCoach2 = context.getBean("englishCoach", Coach.class);

        boolean result = (englishCoach == englishCoach2);

        //Check Bean scope result
        System.out.println("Pointing to the same object: " + result);
        System.out.println("Memory location of englishCoach: " + englishCoach);
        System.out.println("Memory location of englishCoach2: " + englishCoach2);


        //close the context
        context.close();

    }
}
  • As you can see, we will try to get bean englishCoach from the Spring Container 2 times and then we will check them together to make sure they are equal and they are loaded from the same memory area or not. So let's start the application and check the log as below.
Pointing to the same object: true
Memory location of englishCoach: com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach@59494225
Memory location of englishCoach2: com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach@59494225

Process finished with exit code 0
  • As you can see the bean englishCoach that we get from the Spring Container 2 times are the same bean and stored in the same memory address 59494225. So it means each time we get the bean englishCoach from the Spring Container, it will check this bean existed or not, if exist Spring Container will return to us that bean and in case if doesn't exist then Spring Container will create it only one time and return to us.

Example Prototype Bean Scope XML#

Configure The Spring Bean#

  • Like we did in the example for Singleton scope, so let's create a new implementation class HistoryCoach as below.
HistoryCoach.java
1
2
3
4
5
6
7
8
9
package com.spring.core.spring.bean.scopes.and.lifecycle;

public class HistoryCoach implements Coach {

    @Override
    public String getDailyHomeWork() {
        return "Spend 20 minutes to read history books";
    }
}
  • Then in the resources let's update applicationContext.xml with configuring prototype bean historyCoach.
applicationContext.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="englishCoach"
          class="com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach">

    </bean>

    <bean id="historyCoach"
          class="com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach"
          scope="prototype">
    </bean>

</beans>

Testing#

  • Now, let's update the SpringApplication class for getting and using prototype bean historyCoach as below.
SpringApplication.java
 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
package com.spring.core.spring.bean.scopes.and.lifecycle;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {

        //Load Spring Configuration File
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //retrieve bean from the spring container
        Coach englishCoach = context.getBean("englishCoach", Coach.class);
        Coach englishCoach2 = context.getBean("englishCoach", Coach.class);

        Coach historyCoach = context.getBean("historyCoach", Coach.class);
        Coach historyCoach2 = context.getBean("historyCoach", Coach.class);


        boolean result = (englishCoach == englishCoach2);
        boolean result2 = (historyCoach == historyCoach2);

        //Check Bean scope result
        System.out.println("Pointing to the same object: " + result);
        System.out.println("Memory location of englishCoach: " + englishCoach);
        System.out.println("Memory location of englishCoach2: " + englishCoach2);

        System.out.println("Pointing to the same object: " + result2);
        System.out.println("Memory location of historyCoach: " + historyCoach);
        System.out.println("Memory location of historyCoach2: " + historyCoach2);

        //close the context
        context.close();

    }
}
  • As you can see, we will try to get bean historyCoach from the Spring Container 2 times and then we will check them together to see they are equal and loaded from the same memory area or not. So let's start the application and check the log as below.
Pointing to the same object: true
Memory location of englishCoach: com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach@5cb9f472
Memory location of englishCoach2: com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach@5cb9f472

Pointing to the same object: false
Memory location of historyCoach: com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach@cb644e
Memory location of historyCoach2: com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach@13805618

Process finished with exit code 0
  • As you can see, the bean historyCoach that we get from the Spring Container 2 times are not the same bean and they are stored in different memory address cb644e and 13805618. So it means each time we get the historyCoach from the Spring Container, It will create a new bean and return to us for using.

Example Bean Lifecycle Hooks XML#

Prepare#

  • To avoid duplicated steps, so in this example we will continue with the example that we did in the Example Prototype Bean Scope XML.

Define our methods for init and destroy#

  • So, let's go to the EnglishCoach and create 2 methods with any name. In this example they will be named initAdHocMethod and destroyAdHocMethod which are used for bean initialization and bean destruction respectively.

```java linenums="1" titles="EnglishCoach.java"

package com.spring.core.spring.bean.scopes.and.lifecycle;

public class EnglishCoach implements Coach {

@Override
public String getDailyHomeWork() {
    return "Spend 1 hour to practise Speaking Skill!";
}

public void initAdHocMethod() {
    System.out.println("EnglishCoach: The initAdHocMethod() is called!");
}

public void destroyAdHocMethod() {
    System.out.println("EnglishCoach: The destroyAdHocMethod() is called!");
}

}

### Configure the method names in Spring config file
- Now, let's open the `applicationContext.xml` file and adding 2 properties `init-method` and `destroy-method` for the bean `englishCoach` as below.

```xml linenums="1" title="applicationContext.xml"

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="englishCoach"
          class="com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach"
          init-method="initAdHocMethod"
          destroy-method="destroyAdHocMethod">

    </bean>

    <bean id="historyCoach"
          class="com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach"
          scope="prototype">
    </bean>

</beans>
  • In which, the property init-method is used for hooking our custom method into the bean initialization of englishCoach and the value of this property is the method's name in it.
  • The property destroy-method is used for hooking our custom method into the bean destruction of englishCoach and the value of this property is the method's name in it.

Testing#

  • Finally, let's run our application and then check the console output, you should see the method initAdHocMethod() is executed before the bean englishCoach is used and the method destroyAdHocMethod() is executed before the application is actually closed.
EnglishCoach: The initAdHocMethod() is called!
Pointing to the same object: true
Memory location of englishCoach: com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach@56ef9176
Memory location of englishCoach2: com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach@56ef9176
Pointing to the same object: false
Memory location of historyCoach: com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach@4566e5bd
Memory location of historyCoach2: com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach@1ed4004b
EnglishCoach: The destroyAdHocMethod() is called!

Process finished with exit code 0

Example With Destroy Method For Prototype Bean XML#

  • As we know in the Bean Lifecycle Hooks section, for the bean with prototype scope, Spring does not call the destroy method although we apply destroy-method property for XML configuration or use @PostDestroy for annotation based configuration.
  • For this case, if we want to call the destroy method on prototype scope beans, we have to add some custom codes and follow the process as below:
    • Create a custom bean processor: This bean processor will keep track of prototype scoped beans. During shutdown it will call the destroy() method on the prototype scoped beans. The custom processor is configured in the spring config file.
    • Implement DisposableBean For Prototype Beans: the prototype scoped beans MUST implement the DisposableBean interface. This interface defines a "destroy()" method.
    • Configure Spring configuration file: The Spring configuration does not require use the destroy-method attribute. We can safely remove it.

Prepare#

  • In this example we will continue with the example that we did in the Bean Lifecycle Hooks XML.

Create A Custom Bean Processor#

  • Firstly, let's create the CustomBeanProcessor java class which will implement BeanPostProcessor, BeanFactoryAware and DisposableBean and override methods as below.
CustomBeanProcessor.java
 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
package com.spring.core.spring.bean.scopes.and.lifecycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class CustomBeanProcessor implements BeanPostProcessor, BeanFactoryAware, DisposableBean {

    private BeanFactory beanFactory;

    private final List<Object> prototypeBeans = Collections.synchronizedList(new LinkedList<>());

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void destroy() throws Exception {
        // loop through the prototype beans and call the destroy() method on each one
        System.out.println("CustomBeanProcessor: the destroy() method is called!");
        for (Object bean : prototypeBeans) {
            if (bean instanceof DisposableBean) {
                DisposableBean disposable = (DisposableBean) bean;
                try {
                    disposable.destroy();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        prototypeBeans.clear();
        System.out.println("CustomBeanProcessor: the destroy() method is finished!");
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // after start up, keep track of the prototype scoped beans.
        // we will need to know who they are for later destruction
        if (beanFactory.isPrototype(beanName)) {
            prototypeBeans.add(bean);
        }
        return bean;
    }
}
  • As you can see, in the CustomBeanProcessor class. Firstly we will inject the bean beanFactory into the CustomBeanProcessor by overriding the method setBeanFactory.
  • Then for the method postProcessAfterInitialization, we will check every bean after initialized if the bean is the prototype bean then we will add it into a synchronized linked list.
    • Why synchronized linked list? Because we want to maintain the insertion order for calling destroy method of every bean. For example, we have two prototype beans, the first bean is initialized then we add it into the synchronized linked list. Likewise, the second prototype bean is also initialized then we also add it into the synchronized linked list. Then when the customBeanProcessor bean is going to be destroyed, then the destroy() method of it is executed, then we will loop through the prototype beans and the first bean initialized will be executed destroy method first and the second bean will be executed destroy method later.

Implement DisposableBean For Prototype Beans#

  • Now, let's use the class HistoryCoach to implement DisposableBean interface and override the method destroy() as below.
HistoryCoach.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.spring.core.spring.bean.scopes.and.lifecycle;

import org.springframework.beans.factory.DisposableBean;

public class HistoryCoach implements Coach, DisposableBean {

    @Override
    public String getDailyHomeWork() {
        return "Spend 20 minutes to read history books";
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("HistoryCoach: The destroy() method is called!: " + this);
    }
}
  • This destroy() method will be called from the destroy() method in the customBeanProcessor bean.

Configure Spring Configuration File#

  • So, let's update the applicationContext.xml for configuring customBeanProcessor bean as below.
applicationContext.xml
 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
<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="englishCoach"
          class="com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach"
          init-method="initAdHocMethod"
          destroy-method="destroyAdHocMethod">

    </bean>

    <bean id="historyCoach"
          class="com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach"
          scope="prototype">
    </bean>


    <bean id="customBeanProcessor"
          class="com.spring.core.spring.bean.scopes.and.lifecycle.CustomBeanProcessor">

    </bean>

</beans>

Testing#

  • Finally, let's run our application and then check the console output, you should see the destroy methods in prototype bean historyCoach() are executed when the destroy method of customBeanProcessor is executed and they are executed with the same order as they were created.
EnglishCoach: The initAdHocMethod() is called!
Pointing to the same object: true
Memory location of englishCoach: com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach@ff5b51f
Memory location of englishCoach2: com.spring.core.spring.bean.scopes.and.lifecycle.EnglishCoach@ff5b51f
Pointing to the same object: false
Memory location of historyCoach: com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach@25bbe1b6
Memory location of historyCoach2: com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach@5702b3b1
EnglishCoach: The destroyAdHocMethod() is called!
CustomBeanProcessor: the destroy() method is called!
HistoryCoach: The destroy() method is called!: com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach@25bbe1b6
HistoryCoach: The destroy() method is called!: com.spring.core.spring.bean.scopes.and.lifecycle.HistoryCoach@5702b3b1
CustomBeanProcessor: the destroy() method is finished!

Process finished with exit code 0

See Also#

References#