# 自定义Scope-每个线程获取到的Bean实例是同一个
一、流程:
- 实现Scope接口
- 将步骤1的Scope对象通过BeanFactoryPostProcessor注册到容器中
- 设置Bean的Scope为自定义的Scope
- 使用时需要从ApplicationContext中获取,直接注入似乎无效
二、详细流程:
1.实现Scope接口
2.将步骤1的Scope对象通过BeanFactoryPostProcessor注册到容器中
/** * 自定义Bean的Scope * target: 每个线程获取到的Bean实例都是同一个 * * @author ft * @date 2021/1/28 */ @Component(ThreadScope.SCOPE) public class ThreadScope implements Scope, BeanFactoryPostProcessor { /** * 线程本地变量持有当前线程获取到的Bean实例 */ private static final ThreadLocal<Object> THREAD_LOCAL_BEAN = new ThreadLocal<>(); /** * 自定义的Scope的名称 */ public static final String SCOPE = "threadLocal"; @Override public Object get(String name, ObjectFactory<?> objectFactory) { Object o = THREAD_LOCAL_BEAN.get(); if (o == null) { THREAD_LOCAL_BEAN.set(objectFactory.getObject()); } return THREAD_LOCAL_BEAN.get(); } @Override public Object remove(String name) { Object o = THREAD_LOCAL_BEAN.get(); THREAD_LOCAL_BEAN.remove(); return o; } @Override public void registerDestructionCallback(String name, Runnable callback) { } @Override public Object resolveContextualObject(String key) { return null; } @Override public String getConversationId() { return null; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 将自定义的Scope注册到容器中 beanFactory.registerScope(ThreadScope.SCOPE, this); } }
3.设置Bean的Scope为自定义的Scope
/** * Scope为每个线程持有一个对象 * * @author ft * @date 2021/1/28 */ @Scope(ThreadScope.SCOPE) @Component @Slf4j public class TestThreadBean { /** * 构造方法,当进行了实例构建可以看到日志 */ public TestThreadBean() { log.info("."); } public void sayThing() { log.info("-"); } }
4.使用时需要从ApplicationContext中获取,直接注入似乎无效
测试获取bean
TestThreadBean
,验证是否是每个线程所持有的对象都不一样,但是同一个线程重复获取的都是同一个对象。
/** * @author ft * @date 2021/1/28 */ @Slf4j @Component public class ThreadScopeTest implements ApplicationRunner { @Autowired private ApplicationContext applicationContext; @Override public void run(ApplicationArguments args) throws Exception { // 开启10个线程 for (int i = 0; i < 10; i++) { new Thread(() -> { // 从容器中获取TestThreadBean实例 TestThreadBean bean1 = applicationContext.getBean(TestThreadBean.class); log.info("第一次获取:{}", bean1); // 同一个线程第二次从容器中获取TestThreadBean实例 TestThreadBean bean2 = applicationContext.getBean(TestThreadBean.class); log.info("第二次获取:{}", bean2); }, "scope-test-" + i).start(); } } }
5. 测试结果
- 实例化了10次,且每次都是不同的线程进行的实例化
每个线程获取到的对象都不一样,但是同一个线程多次获取的都是同一个对象
# Next
- 其他Scope的实现,如
session
- 必须通过application获取bean才能实现这样的效果?