luckylbl

Spring中的注解@Scope

@Scope : 设置Spring 对象的作用域

1. 源码:

点击查看代码
import org.springframework.core.annotation.AliasFor;
import org.springframework.context.annotation.ScopedProxyMode;
import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {

    @AliasFor("scopeName")
    String value() default "";

    @AliasFor("value")
    String scopeName() default "";

    ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}

2. @Scope注解的作用域类型

• singleton :单例作用域,是默认的作用域。在整个Spring IoC容器中,使用 singleton 定义的Bean将只有一个实例。容器启动时会创建该Bean的实例,并将其存储在容器中,后续每次获取该Bean时都会返回同一个实例。
• prototype :原型作用域。每次通过容器的 getBean 方法获取 prototype 定义的Bean时,都将产生一个新的Bean实例。这种作用域适用于有状态的Bean,每次请求都需要一个新的实例。
• request :请求作用域。对于每次HTTP请求,使用 request 定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效。
• session :会话作用域。对于每次HTTP Session,使用 session 定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效。
• application :应用作用域。对于每个ServletContext,使用 application 定义的Bean都将产生一个新实例。在Web应用中,所有用户共享同一个 application 作用域的Bean实例。
• websocket :WebSocket作用域。对于每个WebSocket会话,使用 websocket 定义的Bean都将产生一个新实例。

3. 使用方法

使用 @Scope 注解指定作用域
使用 proxyMode 属性

  • 示例1 单例里注入「request 作用域」Bean —— 最常见的 proxyMode 场景
点击查看代码
@SpringBootApplication
public class ScopeDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(ScopeDemoApplication.class, args);
    }
}

/* ========== 配置类 ========== */
@Configuration
class AppConfig {

    /**
     * 每个 HTTP 请求都会创建一个新的 RequestBean,
     * 但在单例 SingletonBean 里注入时,Spring 会生成 CGLIB 代理,
     * 保证运行时总能拿到当前线程对应的最新实例。
     */
    @Bean
    @Scope(value = WebApplicationContext.SCOPE_REQUEST,
           proxyMode = ScopedProxyMode.TARGET_CLASS)   // CGLIB 代理
    public RequestBean requestBean() {
        return new RequestBean();
    }

    @Bean
    public SingletonBean singletonBean() {
        return new SingletonBean();
    }
}

/* ========== 请求作用域 Bean ========== */
class RequestBean {
    private final Instant born = Instant.now();
    public String hello() {
        return "RequestBean@" + born;
    }
}

/* ========== 单例 Bean ========== */
class SingletonBean {
    @Autowired
    private RequestBean requestBean;   // 这里注入的是代理对象

    public void show() {
        System.out.println("SingletonBean 里拿到的 → " + requestBean.hello());
    }
}

/* ========== 简单 Controller 验证 ========== */
@RestController
class DemoController {
    @Autowired private SingletonBean singletonBean;

    @GetMapping("/test")
    public String test() {
        singletonBean.show();          // 每次刷新页面,时间戳都变
        return "ok";
    }
}

  • 示例2 自定义「refresh 作用域」—— 配置热刷新
点击查看代码
@Component
@Scope(value = "refresh",               // Spring Cloud 提供的自定义作用域
       proxyMode = ScopedProxyMode.TARGET_CLASS)
@Data
public class FeatureFlags {
    @Value("${switch.newAlgo:false}")
    private boolean newAlgo;
}
  • 示例3 会话作用域 + 接口代理(JDK 动态代理)
点击查看代码
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION,
       proxyMode = ScopedProxyMode.INTERFACES)   // 要求 Bean 实现接口
public class ShoppingCart implements CartService {
    private List<Item> items = new ArrayList<>();

    @Override
    public void addItem(Item i) {
        items.add(i);
    }

    @Override
    public List<Item> getItems() {
        return items;
    }
}

public interface CartService {
    void addItem(Item i);
    List<Item> getItems();
}

proxyMode 取值速记表

取值 说明
DEFAULT 默认值,同 NO;除非特殊作用域,否则不代理。
NO 不创建代理,注入时直接给原始实例(会报错如果作用域短于注入者)。
INTERFACES 基于 JDK 动态代理,要求目标类实现接口。
TARGET_CLASS 基于 CGLIB 生成子类代理,目标类无需接口,最常用。
总结

@Scope 负责“什么时候创建 Bean”,proxyMode 负责“注入时是否用代理把短生命周期 Bean 装进长生命周期 Bean”,两者配合就能安全地把 request/session/refresh 等短命 Bean 注入到 singleton 中使用

posted on 2025-12-12 14:20  lubingliang  阅读(0)  评论(0)    收藏  举报

导航