3.4 Scope of Bean s
What is scope? In object-oriented programming, "scope" generally refers to the visible range between objects or variables. In Spring containers, it refers to the visibility of the Bean objects it creates relative to the requests of other Bean objects.
Spring provides two basic scopes of "singleton" and "prototype", and three web scopes of "request", "session" and "global session". Spring also allows users to customize their scopes.
3.4.1 Basic Scope
Singleton: Beans with a "singleton" scope only have one instance in each Spring IoC container, and their full life cycle is completely managed by the Spring container. The Spring container will return only the same Bean for all operations that acquire the Bean.
GoF singleton design pattern refers to guaranteeing that a class has only one instance and providing a global access point to it. Two implementations are introduced: maintaining the instance by defining static attributes on the class and registry. |
1) Maintaining the instance by defining static attributes on the class: generally speaking, a class loaded by a Java virtual machine ClassLoader has only one instance, and it is generally maintained by static attributes of the class, which results in that classes requiring singletons need to be coded according to the singleton design pattern; Spring does not use this method because it belongs to intrusive design; code samples, for example, are as follows:
- package cn.javass.spring.chapter3.bean;
- public class Singleton {
- //1. Privatization constructor
- private Singleton() {}
- //2. Singleton cache, lazy initialization, initialization when first used
- private static class InstanceHolder {
- private static final Singleton INSTANCE = new Singleton();
- }
- //3. Providing global access points
- public static Singleton getInstance() {
- return InstanceHolder.INSTANCE;
- }
- //4. Provide a counter to validate a ClassLoader instance
- private int counter=0;
- }
The above definition of a singleton class first privatizes the class constructor; secondly, it uses InstanceHolder static inner class to hold singleton objects so as to gain the benefit of lazy initialization; finally, it provides the global access point getInstance so that objects requiring the singleton instance can be obtained; and here we also provide a counter counter to verify an instance of ClassLoader. Specifically, a ClassLoader has a single instance test. Refer to the "test Singleton" test method in the code "cn. javass. spring. chapter3. Singleton Test", which demonstrates in detail that a ClassLoader has a single instance.
1) Registry: First, the instance of the singleton will be registered in the registry through the unique key, and then the singleton will be obtained by the key. Let's look at the implementation directly. Note that this registry implements the Spring interface "Singleton Bean Registry", which defines the singleton object for operation sharing, and the Spring container implements this interface. "Register Singleton" method registration, through "get Singleton" method acquisition, eliminating programming singletons, note that concurrency is not considered in the implementation:
- package cn.javass.spring.chapter3;
- import java.util.HashMap;
- import java.util.Map;
- import org.springframework.beans.factory.config.SingletonBeanRegistry;
- public class SingletonBeanRegister implements SingletonBeanRegistry {
- //Single Bean Cache Pool, where concurrency is not considered
- 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 + "] Already exist");
- }
- BEANS.put(beanName, bean);
- }
- }
Spring is the implementation of registration form design pattern, eliminating programmable singletons and non-intrusive to code.
Next let's see how to configure a singleton in Spring. If the scope is not specified in Spring container, the default is "singleton". The configuration is configurated through the scope attribute. The configuration is as follows:
- <bean class="cn.javass.spring.chapter3.bean.Printer" scope="singleton"/>
Spring manages the storage of singleton objects in Spring containers as shown in Figure 3-5. Spring not only caches singleton objects, but also Bean definitions. For lazily initialized objects, they are created and stored in the singleton cache pool according to Bean definitions for the first time.
Figure 3-5 Singleton Processing
prototype: prototype means that every request to the Spring container for a Bean returns a brand-new Bean. Compared with "singleton", it does not cache a Bean, and each time it is a brand-new Bean created according to the definition of the Bean.
GoF prototype design pattern refers to specifying the types of objects created with prototype instances and creating new objects by copying these prototypes.
Prototypes in Spring and GoF have different meanings:
GoF specifies the type of object created by prototype instances, while the Spring container specifies the type of object created by Bean definitions.
GoF creates new objects by copying these prototypes, while the Spring container creates new objects based on Bean definitions.
The same thing is to create something new based on something, and the GoF prototype must show cloning operations, which belongs to intrusive, while the Spring container only needs configuration, which belongs to non-intrusive.
Now let's see how Spring implements prototypes.
1) Let's first define the Bean "prototype": Bean definition, all objects will be created according to the Bean definition; here we are just a simple example, not involving complex implementations such as dependency injection: BeanDefinition class defines attribute "class" for prototype class, id for unique identifier, scope for scope, as follows:
- package cn.javass.spring.chapter3;
- public class BeanDefinition {
- //Single case
- public static final int SCOPE_SINGLETON = 0;
- //prototype
- public static final int SCOPE_PROTOTYPE = 1;
- //Unique identification
- private String id;
- //class full qualified name
- private String clazz;
- //Scope of action
- private int scope = SCOPE_SINGLETON;
- //In view of space, the setter and getter methods are omitted.
- }
2) Let's look at the Bean definition registry, similar to the singleton registry:
- package cn.javass.spring.chapter3;
- import java.util.HashMap;
- import java.util.Map;
- public class BeanDifinitionRegister {
- //Beans define caches, without considering concurrency here
- private final Map<String, BeanDefinition> DEFINITIONS =
- new HashMap<String, BeanDefinition>();
- public void registerBeanDefinition(String beanName, BeanDefinition bd) {
- //1. Bean definitions are not allowed to be overwritten in this implementation
- if(DEFINITIONS.containsKey(bd.getId())) {
- throw new RuntimeException("Already exist Bean Definition, this implementation does not allow coverage");
- }
- //2. Put Bean Definitions into Bean Definition Cache Pool
- DEFINITIONS.put(bd.getId(), bd);
- }
- public BeanDefinition getBeanDefinition(String beanName) {
- return DEFINITIONS.get(beanName);
- }
- public boolean containsBeanDefinition(String beanName) {
- return DEFINITIONS.containsKey(beanName);
- }
- }
3) It's time to define BeanFactory:
- package cn.javass.spring.chapter3;
- import org.springframework.beans.factory.config.SingletonBeanRegistry;
- public class DefaultBeanFactory {
- //Bean Definition Registry
- private BeanDifinitionRegister DEFINITIONS = new BeanDifinitionRegister();
- //Singleton Registry
- private final SingletonBeanRegistry SINGLETONS = new SingletonBeanRegister();
- public Object getBean(String beanName) {
- //1. Verify the existence of Bean definitions
- if(!DEFINITIONS.containsBeanDefinition(beanName)) {
- throw new RuntimeException("Non-existent[" + beanName + "]Bean Definition");
- }
- //2. Getting Bean Definitions
- BeanDefinition bd = DEFINITIONS.getBeanDefinition(beanName);
- //3. Whether the Bean definition is a singleton scope
- if(bd.getScope() == BeanDefinition.SCOPE_SINGLETON) {
- //3.1 If the singleton registry contains a Bean, return the Bean directly
- if(SINGLETONS.containsSingleton(beanName)) {
- return SINGLETONS.getSingleton(beanName);
- }
- //3.2 The Bean is not included in the singleton registry.
- //Create and register a singleton registry to cache
- SINGLETONS.registerSingleton(beanName, createBean(bd));
- return SINGLETONS.getSingleton(beanName);
- }
- //4. If it is a prototype Bean definition, it directly returns the new Bean created according to the Bean definition.
- //Every time it's new, no cache
- if(bd.getScope() == BeanDefinition.SCOPE_PROTOTYPE) {
- return createBean(bd);
- }
- //5. Bean Definition Wrong in Other Cases
- throw new RuntimeException("FALSE Bean Definition");
- }
- public void registerBeanDefinition(BeanDefinition bd) {
- DEFINITIONS.registerBeanDefinition(bd.getId(), bd);
- }
- private Object createBean(BeanDefinition bd) {
- //Create Beans Based on Bean Definitions
- try {
- Class clazz = Class.forName(bd.getClazz());
- //Creating Bean s by Reflecting Using a Parametric-Free Constructor
- return clazz.getConstructor().newInstance();
- } catch (ClassNotFoundException e) {
- throw new RuntimeException("Can't find Bean[" + bd.getId() + "]class");
- } catch (Exception e) {
- throw new RuntimeException("Establish Bean[" + bd.getId() + "]fail");
- }
- }
The method getBean is used to get the object created according to the Bean definition of the beanName. There are two types of beans: singleton and prototype; registerBean Definition is used to register the Bean definition; and the private method createBean is used to create beans according to the type information in the Bean definition.
3) Test it. Here we only test prototype scope beans, which are a brand new object for each bean retrieved from the bean factory. The Bean FatoryTest is as follows:
- @Test
- public void testPrototype () throws Exception {
- //1. Create Bean Factory
- DefaultBeanFactory bf = new DefaultBeanFactory();
- //2. Creating Prototype Bean Definition
- BeanDefinition bd = new BeanDefinition();
- bd.setId("bean");
- bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
- bd.setClazz(HelloImpl2.class.getName());
- bf.registerBeanDefinition(bd);
- //For prototype beans, you should return a brand new Bean at a time
- System.out.println(bf.getBean("bean") != bf.getBean("bean"));
- }
Finally, let's see how to configure it in Spring by specifying the < bean > tag attribute "scope" attribute as "prototype":
- <bean class="cn.javass.spring.chapter3.bean.Printer" />
Spring manages prototype objects stored in Spring containers as shown in Figure 3-6. Spring does not cache prototype objects, but returns a brand new bean per request according to the Bean definition:
Figure 3-6 Prototype Processing
Now that we've covered the singleton and prototype scopes, let's learn some scopes for Web applications:
3.4.2 Scope in Web Applications
In Web applications, we may need to store data into request s, session s, and global Session. So Spring provides three Web scopes: request, session, and global Session.
Request scope: Indicates that each request requires a container to create a brand new Bean. For example, the data submitting a form must be a new bean for each request to maintain the form data, and the request ends to release the data.
Session scope: Indicates that each session requires a container to create a brand new Bean. For example, for each user, there is usually a session, and the user information of that user needs to be stored in the session. At this time, the bean can be configured as a web scope.
globalSession: Similar to session scope, it is only a web application for portlet environment. If it is in a non-portlet environment, it will be considered session scope.
Configuration is the same as the basic scopes, but you have to have web environment support, and configure the corresponding container listener or interceptor to apply these scopes. We will explain how to use them when integrating the web. You just need to know these scopes.
3.4.4 Custom Scope
In daily program development, almost no custom scope is needed, unless it is necessary to do so.
Let's first look at the Scope interface:
- package org.springframework.beans.factory.config;
- import org.springframework.beans.factory.ObjectFactory;
- public interface Scope {
- Object get(String name, ObjectFactory<?> objectFactory);
- Object remove(String name);
- void registerDestructionCallback(String name, Runnable callback);
- Object resolveContextualObject(String key);
- String getConversationId();
- }
1) Object get (String name, ObjectFactory <?> objectFactory): Used to get beans from scope, where the parameter objectFactory is used to create a new Bean when the appropriate Bean is not found in the current scope;
2) Void register Destruction Callback (String name, Runnable callback): Used to register destroy callback, if you want to destroy the corresponding object, the Spring container registers the corresponding destroy callback, and the custom scope chooses whether to destroy the corresponding object;
3) Object resolveContextualObject(String key): Used to parse the corresponding context data, such as the request scope returns the attributes in the request.
4) String getConversation Id (): Session identifiers for scopes, such as session scopes, will be session Id.
- package cn.javass.spring.chapter3;
- import java.util.HashMap;
- import java.util.Map;
- import org.springframework.beans.factory.ObjectFactory;
- import org.springframework.beans.factory.config.Scope;
- public class ThreadScope implements Scope {
- private final ThreadLocal<Map<String, Object>> THREAD_SCOPE =
- new ThreadLocal<Map<String, Object>>() {
- protected Map<String, Object> initialValue() {
- //Used to store thread-related beans
- return new HashMap<String, Object>();
- }
- };
Let's implement a simple thread scope in which objects created are bound to ThreadLocal.
- @Override
- public Object get(String name, ObjectFactory<?> objectFactory) {
- //If the current thread has bound the corresponding Bean, return directly
- if(THREAD_SCOPE.get().containsKey(name)) {
- return THREAD_SCOPE.get().get(name);
- }
- //Create beans using objectFactory and bind them to the current thread
- THREAD_SCOPE.get().put(name, objectFactory.getObject());
- return THREAD_SCOPE.get().get(name);
- }
- @Override
- public String getConversationId() {
- return null;
- }
- @Override
- public void registerDestructionCallback(String name, Runnable callback) {
- //If it's not implemented here, it's proytotype-like. When the container is returned to the user, it doesn't matter.
- }
- @Override
- public Object remove(String name) {
- return THREAD_SCOPE.get().remove(name);
- }
- @Override
- public Object resolveContextualObject(String key) {
- return null;
- }
- }
Scope has been implemented, so let's register it with the Spring container to make it work:
- <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
- <property name="scopes">
- <map><entry>
- <!-- Appoint scope Keyword --><key><value>thread</value></key>
- <!-- scope Realization --> <bean class="cn.javass.spring.chapter3.ThreadScope"/>
- </entry></map>
- </property>
- </bean>
CustomScopeConfigurer registers a custom scope implementation through the scope attribute of CustomScopeConfigurer, where you need to specify the keyword "thread" that uses the scope and specify the custom scope implementation. Let's define a "thread" scoped Bean with the following configuration (chapter3/threadScope.xml):
- <bean id="helloApi"
- class="cn.javass.spring.chapter2.helloworld.HelloImpl"
- scope="thread"/>
Finally, let's test (cn.javass.spring.chapter3.ThreadScopeTest). First, test in one thread, and get the same Bean in the same thread. Then let's open two threads, and then the beans created by the two threads are different:
The implementation of custom scopes is actually very simple, but the complexity is how to correctly destroy beans if you need to destroy beans.
Please indicate the origin of the original content reprinted[ http://sishuok.com/forum/blogPost/list/2454.html]