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:
- Create a custom scope class implementing the
Scope
interface. - Register the custom scope class in the application context.
- 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.