3.2 Bean Scopes

Bean scopes: singleton and prototype

1. singleton

Only one instance in Spring IoC container. Only return the same Bean.

One instance of the bean is created for the entire application.

Two ways to implement, we could learn how Spring work through the code bellow:

  • Define static attribute to hold the instance:
1
2
3
4
5
6
7
8
9
10
11
12
13
package cn.javass.spring.chapter3.bean;  
public class Singleton {
//1. Private constructor
private Singleton() {}

private static class InstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
//3.Global accession
public static Singleton getInstance() {
return InstanceHolder.INSTANCE;
}
}
  • Via SingletonBeanRegistry:
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
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.config.SingletonBeanRegistry;

public class SingletonBeanRegister implements SingletonBeanRegistry {
private final Map<String, Object> BEANS = new HashMap<String, Object>();

public boolean containsSingleton(String beanName) {
return BEANS.containsKey(beanName);
}

public Object getSingleton(String beanName) {
return BEANS.get(beanName);
}
@Override
public int getSingletonCount() {
return BEANS.size();
}
@Override
public String[] getSingletonNames() {
return BEANS.keySet().toArray(new String[0]);
}
@Override
public void registerSingleton(String beanName, Object bean) {
if (BEANS.containsKey(beanName)) {
throw new RuntimeException("[" + beanName + "] is exist");
}
BEANS.put(beanName, bean);
}
}

JUnit Test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.junit.Test;

public class SingletonTest {
@Test
public void testRegister() {
SingletonBeanRegister register = new SingletonBeanRegister();
register.registerSingleton("bean1", new HelloImpl2());
HelloImpl2 bean1 = (HelloImpl2) register.getSingleton("bean1");
bean1.sayHello();

try {
register.registerSingleton("bean1", new HelloImpl2());
}
catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
  • We could define singleton in xml:

In most time, singleton is default in Spring.

1
<bean  class="cn.javass.spring.chapter3.bean.Printer" scope="singleton"/>

2. prototype

Return new Bean when container request a Bean. Contrast with singleton, prototype would not cache Bean.

One instance of the bean is created every time the bean is injected
into or retrieved from the Spring application context.

  • Here is the implement of prototype in Spring:

Bean definition:

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
public class BeanDefinition {
//singleton
public static final int SCOPE_SINGLETON = 0;
//prototype
public static final int SCOPE_PROTOTYPE = 1;
//identity
private String id;

private String clazz;
//Scope
private int scope = SCOPE_SINGLETON;

public void setId(String id) {
this.id = id;
}
public String getId() {
return this.id;
}

public void setClazz(String clazz) {
this.clazz = clazz;
}
public String getClazz() {
return this.clazz;
}

public void setScope(int scope) {
this.scope = scope;
}
public int getScope() {
return this.scope;
}
}

Bean registry, also use Map:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BeanDifinitionRegister {
private final Map<String, BeanDefinition> DEFINITIONS = new HashMap<String, BeanDefinition>();
public void registerBeanDefinition(String name, BeanDefinition bd) {
if (DEFINITIONS.containsKey(bd.getId())) {
throw new RuntimeException("Bean definition is exist!");
}
DEFINITIONS.put(bd.getId(), bd);
}
public BeanDefinition getBeanDefinition(String beanName) {
return DEFINITIONS.get(beanName);
}
public boolean containsBeanDefinition(String beanName) {
return DEFINITIONS.containsKey(beanName);
}
}

Bean factory checks in the registry so that the Bean is new every time:

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
public class DefaultBeanFactory {
//Bean registry
private BeanDifinitionRegister DEFINITIONS = new BeanDifinitionRegister();

//singleton registry
private final SingletonBeanRegistry SINGLETONS = new SingletonBeanRegister();

public Object getBean(String beanName) {
//is Bean definition exist?
if (!DEFINITIONS.containsBeanDefinition(beanName)) {
throw new RuntimeException("Not exist[" + beanName + "]Bean difinition");
}
//get Bean definition
BeanDefinition bd = DEFINITIONS.getBeanDefinition(beanName);
//is it singleton?
if (bd.getScope() == BeanDefinition.SCOPE_SINGLETON) {
//yes
if (SINGLETONS.containsSingleton(beanName)) {
return SINGLETONS.getSingleton(beanName);
}
//no
SINGLETONS.registerSingleton(beanName, createBean(bd));
return SINGLETONS.getSingleton(beanName);
}
//if it is prototype, construct new one every time.
if (bd.getScope() == BeanDefinition.SCOPE_PROTOTYPE) {
return createBean(bd);
}
throw new RuntimeException("UNKNOWN Definition!");
}

public void registerBeanDefinition(BeanDefinition bd) {
DEFINITIONS.registerBeanDefinition(bd.getId(), bd);
}

private Object createBean(BeanDefinition bd) {
//new Bean
try {
Class clazz = Class.forName(bd.getClazz());
return clazz.getConstructor().newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("Not found Bean[" + bd.getId());
} catch (Exception e) {
throw new RuntimeException("New Bean[" + bd.getId() + "] failed");
}
}
}

Junit test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BeanFatoryTest {
@Test
public void testPrototype() throws Exception {
//new Bean factory
DefaultBeanFactory bf = new DefaultBeanFactory();
//new Bean definition
BeanDefinition bd = new BeanDefinition();
bd.setId("bean");
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bd.setClazz(HelloImpl2.class.getName());
bf.registerBeanDefinition(bd);
System.out.println(bf.getBean("bean") != bf.getBean("bean"));

}
}
  • We could define prototype in xml:
1
<bean  class="cn.javass.spring.chapter3.bean.Printer" scope="prototypes"/>

Bean Scopes in Web application

1. request

In a web application, one instance of the bean is created for each
request.

2. session

In a web application, one instance of the bean is created for each session.