【Spring】Spring 自定义scope

【Spring】Spring 自定义scope1 概述 有时候 spring 内置的几种 sope 都无法满足我们的需求的时候 我们可以自定义 bean 的作用域 1 1 自定义 Scope 3 步骤 1 1 1 第 1 步 实现 Scope 接口 我们来看一下这个接口定义 package org springframew beans factory config

大家好,我是讯享网,很高兴认识大家。

在这里插入图片描述
讯享网

1.概述

有时候,spring内置的几种sope都无法满足我们的需求的时候,我们可以自定义bean的作用域。

1.1 自定义Scope 3步骤

1.1.1 第1步:实现Scope接口

我们来看一下这个接口定义

package org.springframework.beans.factory.config; import org.springframework.beans.factory.ObjectFactory; import org.springframework.lang.Nullable; public interface Scope { 
    / * 返回当前作用域中name对应的bean对象 * name:需要检索的bean的名称 * objectFactory:如果name对应的bean在当前作用域中没有找到,那么可以调用这个ObjectFactory来创建这个对象 / Object get(String name, ObjectFactory<?> objectFactory); / * 将name对应的bean从当前作用域中移除 / @Nullable Object remove(String name); / * 用于注册销毁回调,如果想要销毁相应的对象,则由Spring容器注册相应的销毁回调,而由自定义作用域选择是不是要销毁相应的对象 */ void registerDestructionCallback(String name, Runnable callback); / * 用于解析相应的上下文数据,比如request作用域将返回request中的属性。 */ @Nullable Object resolveContextualObject(String key); / * 作用域的会话标识,比如session作用域将是sessionId */ @Nullable String getConversationId(); } 

讯享网

1.1.2 第2步:将自定义的scope注册到容器

需要调用org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope的方法,看一下这个方法的声明

讯享网/ * 向容器中注册自定义的Scope *scopeName:作用域名称 * scope:作用域对象 / void registerScope(String scopeName, Scope scope); 

1.1.3 第3步:使用自定义的作用域

定义bean的时候,指定bean的scope属性为自定义的作用域名称。

2. 案例

2.1 需求

下面我们来实现一个线程级别的bean作用域,同一个线程中同名的bean是同一个实例,不同的线程中的bean是不同的实例。

2.2 实现分析

需求中要求bean在线程中是贡献的,所以我们可以通过ThreadLocal来实现,ThreadLocal可以实现线程中数据的共享。

下面我们来上代码。

package com.scope.blog.self; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.Scope; import org.springframework.lang.Nullable; import java.util.HashMap; import java.util.Map; import java.util.Objects; / * @author: chuanchuan.lcc * @date: 2020-11-30 23:29 * @modifiedBy: chuanchuan.lcc * @version: 1.0 * @description: * 自定义本地线程级别的bean作用域,不同的线程中对应的bean实例是不同的,同一个线程中同名的bean是同一个实例 * * 自定义的scope 参考:https://mp.weixin..com/s?__biz=MzA5MTkxMDQ4MQ==&mid=&idx=1&sn=f4308f8955f87d75963c379c2a0241f4&chksm=88621e76bfd404c253fa6716d3ffce4de8df0fc1d0d5dd0cf00a81bc170a30829ee58f&token=&lang=zh_CN#rd * 参考: */ public class ThreadScope implements Scope { 
    public static final String THREAD_SCOPE = "thread";//@1 private ThreadLocal<Map<String, Object>> beanMap = new ThreadLocal() { 
    @Override protected Object initialValue() { 
    return new HashMap<>(); } }; @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; } @Nullable @Override public Object remove(String name) { 
    return this.beanMap.get().remove(name); } @Override public void registerDestructionCallback(String name, Runnable callback) { 
    //bean作用域范围结束的时候调用的方法,用于bean清理 System.out.println(name); } @Nullable @Override public Object resolveContextualObject(String key) { 
    return null; } @Nullable @Override public String getConversationId() { 
    return Thread.currentThread().getName(); } } 

@1:定义了作用域的名称为一个常量thread,可以在定义bean的时候给scope使用

2.3 BeanScopeModel

讯享网package com.scope.blog.self; / * @author: chuanchuan.lcc * @date: 2020-11-30 23:31 * @modifiedBy: chuanchuan.lcc * @version: 1.0 * @description: */ public class BeanScopeModel { 
    public BeanScopeModel(String beanScope) { 
    System.out.println(String.format("线程:%s,create BeanScopeModel,{sope=%s},{this=%s}", Thread.currentThread(), beanScope, this)); } } 

上面的构造方法中会输出当前线程的信息,到时候可以看到创建bean的线程。

2.4 bean配置文件

beans-thread.xml内容

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <!-- 自定义scope的bean --> <bean id="threadBean" class="com.scope.blog.self.BeanScopeModel" scope="thread"> <constructor-arg index="0" value="thread"/> </bean> </beans> 

注意上面的scope是我们自定义的,值为thread

2.5 测试用例

讯享网package com.scope.blog.self; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.concurrent.TimeUnit; import static org.junit.Assert.*; / * @author: chuanchuan.lcc * @date: 2020-11-30 23:34 * @modifiedBy: chuanchuan.lcc * @version: 1.0 * @description: */ public class ThreadScopeTest { 
    / * 运行结果 * * 线程:Thread[Thread-0,5,main],create BeanScopeModel,{sope=thread},{this=com.scope.blog.self.BeanScopeModel@31823b7a} * Thread[Thread-0,5,main],com.scope.blog.self.BeanScopeModel@31823b7a * Thread[Thread-0,5,main],com.scope.blog.self.BeanScopeModel@31823b7a * 线程:Thread[Thread-1,5,main],create BeanScopeModel,{sope=thread},{this=com.scope.blog.self.BeanScopeModel@13cd50d} * Thread[Thread-1,5,main],com.scope.blog.self.BeanScopeModel@13cd50d * Thread[Thread-1,5,main],com.scope.blog.self.BeanScopeModel@13cd50d * * @throws InterruptedException */ @Test public void get() throws InterruptedException { 
    String beanXml = "classpath:beans-thread.xml"; //手动创建容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); //设置配置文件位置 context.setConfigLocation(beanXml); //启动容器 context.refresh(); //向容器中注册自定义的scope context.getBeanFactory().registerScope(ThreadScope.THREAD_SCOPE, new ThreadScope());//@1 //使用容器获取bean for (int i = 0; i < 2; i++) { 
    //@2 new Thread(() -> { 
    System.out.println(Thread.currentThread() + "," + context.getBean("threadBean")); System.out.println(Thread.currentThread() + "," + context.getBean("threadBean")); }).start(); TimeUnit.SECONDS.sleep(1); } } } 

注意上面代码,重点在@1,这个地方向容器中注册了自定义的ThreadScope。

@2:创建了2个线程,然后在每个线程中去获取同样的bean 2次,然后输出,我们来看一下效果。

从输出中可以看到,bean在同样的线程中获取到的是同一个bean的实例,不同的线程中bean的实例是不同的。

3.总结

  1. spring容器自带的有2种作用域,分别是singleton和prototype;还有3种分别是spring web容器环境中才支持的request、session、application
  2. singleton是spring容器默认的作用域,一个spring容器中同名的bean实例只有一个,多次获取得到的是同一个bean;单例的bean需要考虑线程安全问题
  3. prototype是多例的,每次从容器中获取同名的bean,都会重新创建一个;多例bean使用的时候需要考虑创建bean对性能的影响
  4. 一个应用中可以有多个spring容器
  5. 自定义scope 3个步骤,实现Scope接口,将实现类注册到spring容器,使用自定义的sope
小讯
上一篇 2025-02-25 19:47
下一篇 2025-03-24 18:53

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/44676.html