Spring Boot 番外 (01) - 自定义Scope
2019-09-09
Coding
Spring Boot
Spring
Java
👋 ‍️‍️阅读
❤️ 喜欢
💬 评论

Spring Boot 番外 (01) - 自定义Scope

ConfigurableBeanFactory提供了接口來进行自定义Scope的注册

/**
 * Register the given scope, backed by the given Scope implementation.
 * @param scopeName the scope identifier
 * @param scope the backing Scope implementation
 */
void registerScope(String scopeName, Scope scope);

我们只需要实现Scope接口,注意这里的Scope不是注解@Scope

Scope接口有如下定义(下文中所有上下文均指代作用域的上下文)

// 调用者请求从上下文中获取Bean,
// Scope实例可以根据上下文和BeanName查找Bean,若不存在可以调用objectFactory.getBean构造
Object get(String name, ObjectFactory<?> objectFactory);

// 从上下文中移除Bean
Object remove(String name);

// 为Bean注册销毁回调
void registerDestructionCallback(String name, Runnable callback);

// 直接从上下文中通过Key获取对象,此方法相当于在容器内注册了BeanName -> Bean
// 例如sessionScope.resolveContextualObject("session")可以获取http session对象
Object resolveContextualObject(String key);

// 获取当前上下文的Id,一般上下文都会有标识符,例如sessionScope可以返回session-id
String getConversationId();

Spring附赠了一个简单实现的SimpleThreadScope可以作为参考。

在这里我们一起来创建一个新的WorldScope(平行世界),通过一个全局的世界标识符来决定当前上下文。

public class WorldScope implements Scope {

    public static String WORLD_ID = "The World"; // 世界标识符

    // Bean的存储,<world-id, <bean-name, bean>>
    private final Map<String, Map<String, Object>> beans = new HashMap<>(); 

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        return beans.computeIfAbsent(WORLD_ID, k -> new HashMap<>())
                .computeIfAbsent(name, k -> objectFactory.getObject());
    }

    @Override
    public Object remove(String name) {
        return beans.computeIfAbsent(WORLD_ID, k -> new HashMap<>()).remove(name);
    }

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

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

    @Override
    public String getConversationId() {
        return WORLD_ID;
    }
}

定义好了Scope,接着我们把它注册到容器中

@Inject
public void config(ConfigurableBeanFactory beanFactory) {
    beanFactory.registerScope("world", new WorldScope());
}

然后我们定义我们的Bean并测试我们的多世界Scope

@Bean
@Scope("world")
public static BeanA beanA() {
    return new BeanA();
}

public static void main(String[] args) {
    ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);

    System.out.println(ctx.getBean(BeanA.class));
    WorldScope.WORLD_ID = "Another World";
    System.out.println(ctx.getBean(BeanA.class));
    WorldScope.WORLD_ID = "The World";
    System.out.println(ctx.getBean(BeanA.class));
}

得到输出

BeanA{world=&#39;The World&#39;}
BeanA{world=&#39;Another World&#39;}
BeanA{world=&#39;The World&#39;}

Copyright © 2020-2022 Dean Xu. All Rights reserved.