Spring Scope 自定义使用

Spring Scope 自定义使用

1.实现Scope接口

1.1.Scope接口

需要实现Spring的org.springframework.beans.factory.config.Scope接口。
下面是Scope接口的描述:

package com.example.demo.common.scope;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.lang.Nullable;

/**
 * @author yushun
 * @date 2022/5/6 - 15:28
 **/
public interface Scope {

    /**
     * 返回当前作用域中name对应的bean对象
     * @param name          需要检索的bean的名称
     * @param objectFactory 如果name对应的bean在当前作用域中没有找到,那么可以调用这个ObjectFactory来创建这个对象
     * @return              bean对象
     */
    Object get(String name, ObjectFactory<?> objectFactory);

    /**
     * 将name对应的bean从当前作用域中移除
     * @param name          需要移除的bean的名称
     * @return              移除的bean对象
     */
    @Nullable
    Object remove(String name);

    /**
     * 用于注册销毁回调,如果想要销毁相应的对象,则由Spring容器注册相应的销毁回调,而由自定义作用域选择是不是要销毁相应的对象
     * @param name          需要销毁的bean名称
     * @param callBack      回调函数
     */
    void registerDestructionCallback(String name, Runnable callBack);

    /**
     * 用于解析相应的上下文数据,比如request作用域将返回request中的属性
     * @param key
     * @return
     */
    @Nullable
    Object resolveContextualObject(String key);

    /**
     * 作用域的会话标识,比如session作用域将是sessionId
     * @return              会话标志
     */
    @Nullable
    String getConversationId();
}

1.2.创建Scope接口实现类

实现一个不同线程使用不同实例,一个线程共用一个实例的效果。
其实和Spring自己实现的org.springframework.context.support.SimpleThreadScope类是一致的。

package com.example.demo.common.scope;

import java.util.Map;
import java.util.Objects;

import com.google.common.collect.Maps;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

/**
 * @author yushun
 * @date 2022/5/6 - 15:44
 **/
public class ThreadScope implements Scope {

    /**
     * 线程作用域标志
     */
    public static final String THREAD_SCOPE = "thread";

    private ThreadLocal<Map<String, Object>> beanMap = ThreadLocal.withInitial(Maps::newHashMap);

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Object bean = beanMap.get().get(name);
        if (Objects.isNull(bean)) {
            bean = objectFactory.getObject();
            beanMap.get().put(name, bean);
        }
        return bean;
    }

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

    @Override
    public void registerDestructionCallback(String name, Runnable callBack) {
        System.out.println(name);
    }

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

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

2.注册Scope接口到Spring容器

我们已经定义好自己的Scope类了,接下来就需要将它注册到Spring容器中去。才可以在创建bean时,通过scope设置作相应的处理,去创建bean。

package com.example.demo.common.scope;

import com.example.demo.dto.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.CustomScopeConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/**
 * @author yushun
 * @date 2022/5/6 - 15:51
 **/
@Slf4j
@Configuration
public class ScopeConfig {

    // 创建测试Bean
    @Scope("thread")
    @Bean
    public Person person() {
        return new Person(Thread.currentThread().getName(), 10);
    }

    @Bean
    public CustomScopeConfigurer configurer() {
      	// 关键代码,将自己的Scope实现类注册到容器中
        CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer();
        customScopeConfigurer.addScope(ThreadScope.THREAD_SCOPE, new ThreadScope());
        return customScopeConfigurer;
    }
}

3.测试

我使用的是SpringBoot项目,通过Test类测试一下,打出对象的hashCode对比一下看看。

package com.example.demo;

import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import com.example.demo.dto.Person;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
@Component
@Slf4j
class DemoApplicationTests {

    @Resource
    private BeanFactory beanFactory;

    @Test
    void testFunctionFacade() {
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                Person person1 = beanFactory.getBean("person", Person.class);
                log.info("person1: {}, hashCode: {}", JSON.toJSONString(person1), person1.hashCode());
                Person person2 = beanFactory.getBean("person", Person.class);
                log.info("person2: {}, hashCode: {}", JSON.toJSONString(person2), person2.hashCode());
            }).start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

通过下面输出可以看出来,不同线程获取的是不同的实例,同一个线程获取到的是两个不同的实例。

2022-05-06 19:34:03.419  INFO 930 --- [       Thread-2] com.example.demo.DemoApplicationTests    : person1: {"age":10,"name":"Thread-2"}, hashCode: 1538272566
2022-05-06 19:34:03.421  INFO 930 --- [       Thread-2] com.example.demo.DemoApplicationTests    : person2: {"age":10,"name":"Thread-2"}, hashCode: 1538272566
2022-05-06 19:34:04.338  INFO 930 --- [       Thread-3] com.example.demo.DemoApplicationTests    : person1: {"age":10,"name":"Thread-3"}, hashCode: 1538272567
2022-05-06 19:34:04.339  INFO 930 --- [       Thread-3] com.example.demo.DemoApplicationTests    : person2: {"age":10,"name":"Thread-3"}, hashCode: 1538272567
posted @ 2022-05-06 19:37  Thoughtful_z  阅读(150)  评论(0)    收藏  举报