Как создать собственный Scope для бинов в Spring?

«Как создать собственный Scope для бинов в Spring?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, можно создать собственный Scope, реализовав интерфейс org.springframework.beans.factory.config.Scope и зарегистрировав его в контексте.

Почему это нужно: Для управления жизненным циклом бинов нестандартным образом, например, для 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 CustomScope implements Scope {
    private final Map<String, Object> scopedObjects = new ConcurrentHashMap<>();
    private final Map<String, Runnable> destructionCallbacks = new ConcurrentHashMap<>();

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        return scopedObjects.computeIfAbsent(name, key -> objectFactory.getObject());
    }

    @Override
    public Object remove(String name) {
        destructionCallbacks.remove(name);
        return scopedObjects.remove(name);
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        destructionCallbacks.put(name, callback);
    }

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

Регистрация Scope в контексте:

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

@Configuration
public class ScopeConfig {
    @Bean
    public static BeanFactoryPostProcessor beanFactoryPostProcessor() {
        return factory -> factory.registerScope("custom", new CustomScope());
    }
}

Использование:

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("custom")
public class MyScopedBean {
    // Бин будет управляться вашим CustomScope
}

Ключевые моменты:

  1. Реализуйте все методы интерфейса Scope, особенно get, remove и registerDestructionCallback.
  2. Зарегистрируйте scope до создания бинов, используя BeanFactoryPostProcessor.
  3. Управляйте хранением и очисткой объектов внутри вашей реализации (например, в Map).