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) 收藏 举报
浙公网安备 33010602011771号