Custom Bean Scope in Spring (original) (raw)

Last Updated : 4 Apr, 2026

In the Spring Framework, bean scopes define the lifecycle and visibility of beans within the application context. Spring provides several built-in scopes such as singleton, prototype, request, session, and application. However, some applications require more control over bean lifecycle, which can be achieved using custom bean scopes.

When to Use Custom Scopes

Steps to Creating a Custom Thread-Local Scope

Step 1: Create a Maven Project

1. Open IntelliJ / Eclipse / STS.
2. Select File -> New -> Maven Project.
3. Choose Create a simple project (skip archetype selection).
4. Enter the following details:

Click Finish to create the project.

Step 2: Add Spring Dependency

Open the pom.xml file and add the Spring dependency.

Java `

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.1.6</version>
</dependency>

`

Then update the Maven project so the dependencies are downloaded.

Step 3: Create Project Package Structure

Create the following package structure inside src/main/java.

ou

Project-Structure

Step 4: Implement the Custom Scope

Spring provides the interface:

org.springframework.beans.factory.config.Scope

We need to implement this interface to define the behavior of our custom scope.

Create the class ThreadLocalScope.java.

Java `

package com.example.scope;

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

import java.util.Map; import java.util.concurrent.ConcurrentHashMap;

public class ThreadLocalScope implements Scope {

private final ThreadLocal<Map<String, Object>> threadLocal =
        ThreadLocal.withInitial(ConcurrentHashMap::new);

@Override
public Object get(String name, ObjectFactory<?> objectFactory) {

    Map<String, Object> scopedObjects = threadLocal.get();

    return scopedObjects.computeIfAbsent(name,
            k -> objectFactory.getObject());
}

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

@Override
public void registerDestructionCallback(String name, Runnable callback) {
    // Optional destruction logic
}

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

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

}

`

**Explanation:

Step 5: Register the Custom Scope

Now we must register the custom scope in the Spring configuration.

**Create AppConfig.java.

Java `

package com.example.config;

import com.example.scope.ThreadLocalScope; import com.example.service.MyBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.beans.factory.config.CustomScopeConfigurer;

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

@Configuration public class AppConfig {

@Bean
public static CustomScopeConfigurer customScopeConfigurer() {

    CustomScopeConfigurer configurer =
            new CustomScopeConfigurer();

    Map<String, Object> scopes = new HashMap<>();

    scopes.put("thread-local", new ThreadLocalScope());

    configurer.setScopes(scopes);

    return configurer;
}

@Bean
@org.springframework.context.annotation.Scope("thread-local")
public MyBean myBean() {
    return new MyBean();
}

}

`

Here we define a new scope named thread-local and register it with Spring.

Step 6: Create the Bean Class

**MyBean.java.

Java `

package com.example.service;

public class MyBean {

public MyBean() {
    System.out.println("MyBean instance created");
}

}

`

Step 7: Create Main Class to Test Custom Scope

**CustomScopeDemo.java.

Java `

package com.example;

import com.example.config.AppConfig; import com.example.service.MyBean; import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class CustomScopeDemo {

public static void main(String[] args) {

    AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(AppConfig.class);

    Runnable task = () -> {

        MyBean bean1 = context.getBean(MyBean.class);
        MyBean bean2 = context.getBean(MyBean.class);

        System.out.println(Thread.currentThread().getName()
                + " : " + (bean1 == bean2));
    };

    Thread t1 = new Thread(task, "Thread-1");
    Thread t2 = new Thread(task, "Thread-2");

    t1.start();
    t2.start();
}

}

`

Step 8: Run the Application

ou

Output

Advantages of Custom Bean Scopes