Scopes in Spring Framework are essential for managing bean lifecycles and ensuring the proper functioning of your applications. This comprehensive guide will cover the various scopes provided by Spring using annotations and demonstrate how to create a custom scope.

Introduction to Spring Scopes

Spring Framework’s core functionality relies on the concept of dependency injection (DI). It enables the creation, management, and wiring of beans (Java objects) within an application. Scopes define the lifecycle and visibility of these beans within the application context. Spring offers several scopes to cater to different use cases and requirements.

Understanding the Default Scopes

Singleton

The singleton scope is the default scope in Spring. A bean defined with this scope will have only one instance per application context. Singleton beans are created eagerly by default, meaning that they are instantiated when the application context is initialized. To define a singleton bean using annotations, simply use the @Component annotation or any of its specialized stereotypes, such as @Service, @Repository, or @Controller.

@Component
public class MyBean {
}

Prototype

With the prototype scope, a new instance of the bean is created every time it is requested from the application context. This scope is useful when you need independent instances of a bean for each use. Use the @Scope annotation with the prototype value to define a prototype-scoped bean.

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MyBean {
}

Exploring Web-aware Scopes

Spring also provides web-aware scopes that are designed specifically for web applications. To use web-aware scopes, make sure to include the spring-web dependency in your project.

Request

The request scope creates a new bean instance for each HTTP request. This scope is ideal for storing data specific to a single request. Use the @Scope annotation with the request value to define a request-scoped bean.

@Component
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class MyBean {
}

Session

The session scope creates a new bean instance for each user session. This scope is useful for maintaining user-specific data across multiple requests. Use the @Scope annotation with the session value to define a session-scoped bean.

@Component
@Scope(WebApplicationContext.SCOPE_SESSION)
public class MyBean {
}

Application

The application scope creates a single bean instance per web application. This scope is similar to the singleton scope, but the bean is shared across the entire web application. Use the @Scope annotation with the application value to define an application-scoped bean.

@Component
@Scope(WebApplicationContext.SCOPE_APPLICATION)
public class MyBean {
}

Custom Scopes

Spring Framework allows you to define custom scopes to address specific requirements. Implement the org.springframework.beans.factory.config.Scope interface and register your custom scope with the application context.

Thread Scope (Custom Scope Example)

Thread scope is not available by default in Spring, but you can implement it as a custom scope. Thread-scoped beans are tied to the lifecycle of a Java thread and are useful for multi-threaded applications.

To implement the thread scope, follow these steps:

  1. Create a custom scope class implementing the Scope interface.
  2. Register the custom scope class in the application context.
  3. Use the custom scope in your bean definition.

Step 1: Create a custom scope class implementing Scope interface

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

import java.util.HashMap;
import java.util.Map;

public class ThreadScope implements Scope {
    private final ThreadLocal<Map<String, Object>> threadLocalScope = new ThreadLocal<Map<String, Object>>() {
        @Override
        protected Map<String, Object> initialValue() {
            return new HashMap<>();
        }
    };

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Map<String, Object> scope = threadLocalScope.get();
        Object object = scope.get(name);
        if (object == null) {
            object = objectFactory.getObject();
            scope.put(name, object);
        }
        return object;
    }

    @Override
    public Object remove(String name) {
        return threadLocalScope.get().remove(name);
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        // Not supported
    }

    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }

    @Override
    public String getConversationId() {
        return String.valueOf(Thread.currentThread().getId());
    }
}

Step 2: Register the custom scope class in the application context

To register the custom scope, create a @Configuration class and use the @Bean annotation to register the custom scope.

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CustomScopeConfig {
    public static final String THREAD_SCOPE = "thread";

    @Bean
    public ThreadScope threadScope() {
        return new ThreadScope();
    }

    @Bean
    public CustomScopeConfigurer customScopeConfigurer() {
        CustomScopeConfigurer configurer = new CustomScopeConfigurer();
        Map<String, Object> scopes = new HashMap<>();
        scopes.put(THREAD_SCOPE, threadScope());
        configurer.setScopes(scopes);
        return configurer;
    }
}

Step 3: Use the custom scope in your bean definition

Now that the custom scope is registered, you can use it in your bean definition by referencing the scope name you defined in the CustomScopeConfig class.

@Component
@Scope(CustomScopeConfig.THREAD_SCOPE)
public class MyBean {
}

Conclusion

Understanding Spring’s scope mechanisms is crucial for designing and developing efficient applications. With this comprehensive guide, you should be well-equipped to harness the power of Spring scopes using annotations and create custom scopes to maximize the potential of your applications.