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