美烦资源网

专注技术文章分享,涵盖编程教程、IT 资源与前沿资讯

面试题Spring Bean 的作用域

面试题Spring Bean 的作用域

1. Spring 支持的 6 种标准作用域

在 Spring 框架中,Bean 的作用域决定了其实例的创建方式、生命周期和可见范围。以下是核心作用域:

作用域

描述

适用场景

Singleton

默认作用域,整个 Spring 容器中仅存在一个实例,全局共享。

无状态对象(如工具类、服务类)

Prototype

每次请求 Bean 时都会创建新实例,不进行缓存。

有状态对象(如用户会话数据)

Request

每个 HTTP 请求创建一个新实例,仅在当前请求内有效(需 Web 环境)。

HTTP 请求相关的数据(如表单参数)

Session

每个 HTTP Session 创建一个新实例,用户会话期间有效(需 Web 环境)。

用户登录状态、购物车信息

Application

整个 Web 应用生命周期内共享一个实例,作用域为 ServletContext。

全局配置、缓存管理

WebSocket

每个 WebSocket 会话创建一个实例,仅在 WebSocket 连接期间有效。

实时通信场景(如聊天室消息管理)

  • globalSession 是 Portlet 应用中的全局会话作用域,现较少使用。
  • Request、Session、Application 和 WebSocket 作用域需在 Web 环境下启用(如 Spring MVC)。

2. 如何自定义作用域

Spring 允许通过实现 Scope 接口创建自定义作用域,例如实现线程级作用域或分布式作用域。以下是实现步骤:

步骤 1:实现 Scope 接口
自定义作用域类需实现 get()、remove() 等方法管理 Bean 实例的生命周期:

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

public class ThreadLocalScope implements Scope {
    private ThreadLocal<Map<String, Object>> threadLocal = ThreadLocal.withInitial(HashMap::new);

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Map<String, Object> scope = threadLocal.get();
        Object obj = scope.get(name);
        if (obj == null) {
            obj = objectFactory.getObject();
            scope.put(name, obj);
        }
        return obj;
    }

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

    // 其他方法(如 registerDestructionCallback)可空实现
}

步骤 2:注册自定义作用域
在 Spring 配置类中注册作用域,并指定作用域名称(如 threadLocal):

@Configuration
public class AppConfig implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        beanFactory.registerScope("threadLocal", new ThreadLocalScope());
    }
}

步骤 3:使用自定义作用域
在 Bean 定义中通过 @Scope 注解指定自定义作用域:

@Component
@Scope("threadLocal")
public class ThreadLocalBean {
    private String data;

    // Getter/Setter 省略
}

3. 示例演示

场景:使用 ThreadLocalScope 实现线程级作用域,确保每个线程获取独立的 Bean 实例。

验证代码

@RestController
public class ScopeDemoController {
    @Autowired
    private ApplicationContext context;

    @GetMapping("/demo")
    public String demo() {
        // 每次请求(不同线程)获取新的 ThreadLocalBean 实例
        ThreadLocalBean bean = context.getBean(ThreadLocalBean.class);
        return "ThreadLocalBean HashCode: " + bean.hashCode();
    }
}

结果

  • 同一线程多次调用 /demo 返回相同 HashCode。
  • 不同线程调用返回不同 HashCode。

总结

  • 标准作用域:优先选择 Singleton(无状态)和 Prototype(有状态),Web 相关作用域需结合具体场景。
  • 自定义作用域:通过实现 Scope 接口扩展 Spring 容器的能力,适用于分布式锁、线程隔离等复杂场景。
  • 面试注意点:明确作用域的生命周期、线程安全问题,并能结合源码(如 AbstractBeanFactory.doGetBean())解释实现原理。
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言