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 { private Singleton() {}
private static class InstanceHolder { private static final Singleton INSTANCE = new Singleton(); } 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 { public static final int SCOPE_SINGLETON = 0; public static final int SCOPE_PROTOTYPE = 1; private String id;
private String clazz; 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 { private BeanDifinitionRegister DEFINITIONS = new BeanDifinitionRegister();
private final SingletonBeanRegistry SINGLETONS = new SingletonBeanRegister();
public Object getBean(String beanName) { if (!DEFINITIONS.containsBeanDefinition(beanName)) { throw new RuntimeException("Not exist[" + beanName + "]Bean difinition"); } BeanDefinition bd = DEFINITIONS.getBeanDefinition(beanName); if (bd.getScope() == BeanDefinition.SCOPE_SINGLETON) { if (SINGLETONS.containsSingleton(beanName)) { return SINGLETONS.getSingleton(beanName); } SINGLETONS.registerSingleton(beanName, createBean(bd)); return SINGLETONS.getSingleton(beanName); } 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) { 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 { DefaultBeanFactory bf = new DefaultBeanFactory(); 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.