冰河技术
导读
♻学习路线
  • 面试必问系列

    • 面试必问
  • 架构与模式

    • Java极简设计模式
    • 实战高并发设计模式
  • Java核心技术

    • Java8新特性
    • IOC核心技术
    • JVM调优技术
  • 容器化核心技术

    • Dockek核心技术
  • 分布式存储

    • Mycat核心技术
  • 数据库核心技术

    • MySQL基础篇
  • 服务器核心技术

    • Nginx核心技术
  • 渗透核心技术

    • 渗透实战技术
  • 底层技术
  • 源码分析
  • 基础案例
  • 实战案例
  • 面试
  • 系统架构
  • Spring6核心技术
  • 分布式事务

    • 分布式事务系列视频
  • SpringBoot
  • SpringCloudAlibaba
  • 🔥AI大模型项目

    • 一站式AI智能平台
    • AI智能客服系统
    • AI智能问答系统
    • 实战AI大模型
  • 中间件项目

    • 手写高性能Redis组件
    • 手写高性能脱敏组件
    • 手写线程池项目
    • 手写高性能SQL引擎
    • 手写高性能Polaris网关
    • 手写高性能RPC项目
  • 高并发项目

    • 分布式IM即时通讯系统(新)
    • 分布式Seckill秒杀系统
    • 实战高并发设计模式
  • 微服务项目

    • 简易电商脚手架项目
  • 手撕源码

    • 手撕Spring6源码
🌍知识星球
  • 总览

    • 《书籍汇总》
  • 出版图书

    • 《深入理解高并发编程:核心原理与案例实战》
    • 《深入理解高并发编程:JDK核心技术》
    • 《深入高平行開發:深度原理&專案實戰》
    • 《深入理解分布式事务:原理与实战》
    • 《MySQL技术大全:开发、优化与运维实战》
    • 《海量数据处理与大数据技术实战》
  • 电子书籍

    • 《实战高并发设计模式》
    • 《深入理解高并发编程(第2版)》
    • 《深入理解高并发编程(第1版)》
    • 《从零开始手写RPC框架(基础篇)》
    • 《SpringCloud Alibaba实战》
    • 《冰河的渗透实战笔记》
    • 《MySQL核心知识手册》
    • 《Spring IOC核心技术》
  • 关于自己
  • 关于学习
  • 关于职场
B站
Github
导读
♻学习路线
  • 面试必问系列

    • 面试必问
  • 架构与模式

    • Java极简设计模式
    • 实战高并发设计模式
  • Java核心技术

    • Java8新特性
    • IOC核心技术
    • JVM调优技术
  • 容器化核心技术

    • Dockek核心技术
  • 分布式存储

    • Mycat核心技术
  • 数据库核心技术

    • MySQL基础篇
  • 服务器核心技术

    • Nginx核心技术
  • 渗透核心技术

    • 渗透实战技术
  • 底层技术
  • 源码分析
  • 基础案例
  • 实战案例
  • 面试
  • 系统架构
  • Spring6核心技术
  • 分布式事务

    • 分布式事务系列视频
  • SpringBoot
  • SpringCloudAlibaba
  • 🔥AI大模型项目

    • 一站式AI智能平台
    • AI智能客服系统
    • AI智能问答系统
    • 实战AI大模型
  • 中间件项目

    • 手写高性能Redis组件
    • 手写高性能脱敏组件
    • 手写线程池项目
    • 手写高性能SQL引擎
    • 手写高性能Polaris网关
    • 手写高性能RPC项目
  • 高并发项目

    • 分布式IM即时通讯系统(新)
    • 分布式Seckill秒杀系统
    • 实战高并发设计模式
  • 微服务项目

    • 简易电商脚手架项目
  • 手撕源码

    • 手撕Spring6源码
🌍知识星球
  • 总览

    • 《书籍汇总》
  • 出版图书

    • 《深入理解高并发编程:核心原理与案例实战》
    • 《深入理解高并发编程:JDK核心技术》
    • 《深入高平行開發:深度原理&專案實戰》
    • 《深入理解分布式事务:原理与实战》
    • 《MySQL技术大全:开发、优化与运维实战》
    • 《海量数据处理与大数据技术实战》
  • 电子书籍

    • 《实战高并发设计模式》
    • 《深入理解高并发编程(第2版)》
    • 《深入理解高并发编程(第1版)》
    • 《从零开始手写RPC框架(基础篇)》
    • 《SpringCloud Alibaba实战》
    • 《冰河的渗透实战笔记》
    • 《MySQL核心知识手册》
    • 《Spring IOC核心技术》
  • 关于自己
  • 关于学习
  • 关于职场
B站
Github
  • 专栏介绍

    • 第00章:开篇:我要带你一步步调试Spring6.0源码啦!
  • 第一篇:IOC容器

    • 第01章:深度解析@Configuration注解
    • 第02章:深度解析@ComponentScan注解
    • 第03章:深度解析@Bean注解
    • 第04章:深度解析从IOC容器中获取Bean的过程
    • 第05章:深度解析@Import注解
    • 第06章:深度解析@PropertySource注解
    • 第07章:深度解析@DependsOn注解
    • 第08章:深度解析@Conditional注解
    • 第09章:深度解析@Lazy注解
    • 【付费】 第10章:深度解析@Component注解(含@Repository、@Service和@Controller)
    • 【付费】 第11章:深度解析@Value注解
    • 【付费】 第12章:深度解析@Autowired注解
    • 【付费】 第13章:深度解析@Qualifier注解
    • 【付费】 第14章:深度解析@Resource注解
    • 【付费】 第15章:深度解析@Inject注解
    • 【付费】 第16章:深度解析@Primary注解
    • 【付费】 第17章:深度解析@Scope注解
    • 【付费】 第18章:深度解析@PostConstruct注解与@PreDestroy注解
    • 【付费】 第19章:深度解析@Profile注解
    • 【付费】第20章:深度解析循环依赖(史上最全)
    • 【付费】第21章:深度解析事件监听机制
  • 第二篇:AOP切面

    • 【付费】第22章:AOP切面型注解实战
    • 【付费】第23章:深度解析@EnableAspectJAutoProxy注解
    • 【付费】第24章:深度解析切入点表达式
    • 【付费】第25章:深度解析构建AOP拦截器链的流程
    • 【付费】第26章:深度解析调用通知方法的流程
    • 【付费】第27章:深度解析@DeclareParents注解
    • 【付费】第28章:@EnableLoadTimeWeaving注解
  • 第三篇:声明式事务

    • 【付费】第29章:Spring事务概述与编程实战
    • 【付费】第30章:深度解析Spring事务三大接口
    • 【付费】第31章:深度解析Spring事务隔离级别与传播机制
    • 【付费】第32章:深度解析@EnableTransactionManagement注解
    • 【付费】第33章:深度解析@Transactional注解
    • 【付费】第34章:深度解析Spring事务的执行流程
    • 【付费】第35章:深度解析Spring底层事务传播机制源码
    • 【付费】第36章:深度解析@TransactionEventListener注解
    • 【付费】第37章:七大场景深度分析Spring事务嵌套最佳实践
    • 【付费】第38章:深度解析Spring事务失效的八大场景
  • 第四篇:AOT预编译

    • 第39章:AOT预编译技术概述
    • 【付费】第40章:手动构建Native Image
    • 【付费】第41章:Maven构建Native Image
  • 第五篇:SpringMVC

    • 【付费】第42章:注解型SpringMVC通用SpringBoot启动模型设计与实现
    • 【付费】第43章:深度解析@Controller注解
    • 【付费】第44章:深度解析@RestController注解
    • 【付费】第45章:深度解析@RequestMapping注解
    • 【付费】第46章:深度解析@RequestParam注解
    • 【付费】第47章:深度解析@PathVariable注解
    • 【付费】第48章:深度解析@RequestBody注解
    • 【付费】第49章:深度解析@RequestHeader注解
    • 【付费】第50章:深度解析@CookieValue注解
    • 【付费】第51章:深度解析@ModelAttribute注解
    • 【付费】第52章:深度解析@ExceptionHandler注解
    • 【付费】第53章:深度解析@InitBinder注解
    • 【付费】第54章:深度解析@ControllerAdvice注解
    • 【付费】第55章:深度解析@RequestAttribute注解
    • 【付费】第56章:深度解析@SessionAttribute注解
    • 【付费】第57章:深度解析@SessionAttributes注解
    • 【付费】第58章:深度解析@ResponseBody注解
    • 【付费】第59章:深度解析@CrossOrigin注解
  • 作业篇

    • 作业:专栏整体作业

《Spring核心技术》第17章-生命周期型注解:深度解析@Scope注解

作者:冰河
星球:http://m6z.cn/6aeFbs
博客:https://binghe.site
文章汇总:https://binghe.site/md/all/all.html
源码地址:https://github.com/binghe001/spring-annotation-book/tree/master/spring-annotation-chapter-17

沉淀,成长,突破,帮助他人,成就自我。

大家好,我是冰河~~


  • 本章难度:★★★★☆

  • 本章重点:进一步学习并掌握@Scope指定Bean对象作用范围的案例和流程,从源码级别彻底掌握@Scope注解在Spring底层的执行流程。


本节目录如下所示:

  • 学习指引
  • 注解说明
    • 注解源码
    • 使用场景
  • 使用案例
    • 实现单例Bean
    • 实现原型Bean
  • 源码时序图
    • 注册Bean的流程
    • 调用Bean工厂后置处理器
    • 获取Bean的流程
  • 源码解析
    • 注册Bean的流程
    • 调用Bean工厂后置处理器
    • 获取Bean的流程
  • 总结
  • 思考
  • VIP服务

一、学习指引

Spring中的@Scope注解,你真的彻底了解过吗?

在使用Spring开发项目时,有时需要指定Bean的作用范围,此时我们又该怎么做呢?

注意:在本章中,忽略了Spring处理循环依赖的细节,后续会用单独的一章专门说明Spring的循环依赖问题。

二、注解说明

关于@Scope注解的一点点说明~~

@Scope注解是Spring中提供的一个能够指定Bean的作用范围的注解,通过@Scope注解可以指定创建的Bean是单例的,还是原型的,也可以使用@Scope注解指定Bean在Web中的作用域,还可以自定义作用域。

2.1 注解源码

@Scope注解的源码详见:org.springframework.context.annotation.Scope。

/**
 * @author Mark Fisher
 * @author Chris Beams
 * @author Sam Brannen
 * @since 2.5
 * @see org.springframework.stereotype.Component
 * @see org.springframework.context.annotation.Bean
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
	@AliasFor("scopeName")
	String value() default "";
	/**
	 * @since 4.2
	 */
	@AliasFor("value")
	String scopeName() default "";
	ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}

从@Scope注解的源码可以看出,@Scope注解是从Spring2.5版本开始提供的注解,并且在@Scope注解中提供了三个属性,具体含义分别如下所示。

  • value:表示作用范围,可以取如下值。
    • singleton:表示单例Bean,IOC容器在启动时,就会创建Bean对象。如果标注了@Lazy注解,IOC容器在启动时,就不会创建Bean对象,会在第一次从IOC容器中获取Bean对象时,创建Bean对象。后续每次从IOC容器中获取的都是同一个Bean对象,同时,IOC容器会接管单例Bean对象的生命周期。
    • prototype:表示原型Bean。IOC容器在启动时,不会创建Bean对象,每次从IOC容器中获取Bean对象时,都会创建一个新的Bean对象。并且@Lazy注解对原型Bean不起作用,同时,IOC容器不会接管原型Bean对象的生命周期
    • request:表示作用域是当前请求范围。
    • session:表示作用域是当前会话范围。
    • application:表示作用域是当前应用范围。
  • scopeName:Spring4.2版本开始新增的属性,作用与value属性相同。
  • proxyMode:指定Bean对象使用的代理方式,可以取如下值。
    • DEFAULT:默认值,作用与NO相同。
    • NO:不使用代理。
    • INTERFACES:使用JDK基于接口的代理。
    • TARGET_CLASS:使用CGLIB基于目标类的子类创建代理对象。

2.2 使用场景

大部分场景下,使用Spring的单例Bean就足够了,Spring默认的类型也是单例Bean。单例Bean能够保证在Spring中不会重复创建相同的Bean对象,对性能有所提高。但是,如果单例Bean中存在非静态成员变量,可能会产生线程安全问题。如果设置为原型Bean,则每次从IOC容器中获取Bean对象时,都会重新生成一个新的Bean对象,每次生成新的Bean对象多少都会影响程序的性能。

早期开发中使用比较多的Struts2框架中的Action,由于其模型驱动和OGNL表达式的原因,就必须将Spring中的Bean配置成原型Bean。

三、使用案例

@Scope注解指定Bean作用范围的案例,我们一起实现吧~~

本章,就基于@Scope注解实现指定Bean的作用范围的案例,总体上会从单例Bean和原型Bean两个作用范围进行说明。

注意:本章的案例和源码解析都是基于@Scope注解标注到方法上,结合@Bean注解进行分析的。

3.1 实现单例Bean

本节,主要基于@Scope注解实现单例Bean,具体实现步骤如下所示。

(1)新增ScopeBean类

ScopeBean类的源码详见:spring-annotation-chapter-17工程下的io.binghe.spring.annotation.chapter17.bean.ScopeBean。

public class ScopeBean {
    public ScopeBean(){
        System.out.println("执行ScopeBean类的构造方法...");
    }
}

可以看到,ScopeBean类就是一个普通的Java类,并且在构造方法中打印了日志。

(2)新增ScopeConfig类

ScopeConfig类的源码详见:spring-annotation-chapter-17工程下的io.binghe.spring.annotation.chapter17.config.ScopeConfig。

@Configuration
public class ScopeConfig {
    @Bean
    @Scope(value = "singleton")
    public ScopeBean scopeBean(){
        return new ScopeBean();
    }
}

可以看到,在ScopeConfig类上标注了@Configuration注解,说明ScopeConfig类是案例程序的配置类。在ScopeConfig类中的scopeBean()方法上使用@Bean注解向IOC容器中注入ScopeBean类的Bean对象。同时,在scopeBean()方法上,使用 @Scope注解指定了Bean的作用范围为singleton,也就是单例Bean。此处,由于Spring默认就是单例Bean,所以,也可以将@Scope注解省略。

(3)新增ScopeTest类

ScopeTest类的源码详见:spring-annotation-chapter-17工程下的io.binghe.spring.annotation.chapter17.ScopeTest。

public class ScopeTest {
    public static void main(String[] args) {
        System.out.println("创建IOC容器开始...");
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScopeConfig.class);
        System.out.println("创建IOC容器结束...");
        System.out.println("从IOC容器中第一次获取Bean对象开始...");
        ScopeBean scopeBean = context.getBean(ScopeBean.class);
        System.out.println(scopeBean);
        System.out.println("从IOC容器中第一次获取Bean对象结束...");
        System.out.println("从IOC容器中第二次获取Bean对象开始...");
        scopeBean = context.getBean(ScopeBean.class);
        System.out.println(scopeBean);
        System.out.println("从IOC容器中第二次获取Bean对象结束...");
    }
}

可以看到,在ScopeTest类中,首先创建了IOC容器,随后连续两次从IOC容器中获取ScopeBean类的Bean对象,并打印对应的日志信息。

(4)运行ScopeTest类

运行ScopeTest类的main()方法,输出的结果信息如下所示。

创建IOC容器开始...
执行ScopeBean类的构造方法...
创建IOC容器结束...
从IOC容器中第一次获取Bean对象开始...
io.binghe.spring.annotation.chapter17.bean.ScopeBean@11fc564b
从IOC容器中第一次获取Bean对象结束...
从IOC容器中第二次获取Bean对象开始...
io.binghe.spring.annotation.chapter17.bean.ScopeBean@11fc564b
从IOC容器中第二次获取Bean对象结束...

从输出的结果信息可以看出,Spring在IOC容器启动时就会创建单例Bean,随后每次从IOC容器中获取的都是同一个Bean对象。

3.2 实现原型Bean

本节实现原型Bean的步骤比较简单,就是在3.1节的基础上进行改造。具体步骤如下所示。

(1)修改ScopeConfig类

将ScopeConfig类中的scopeBean()方法上的@Scope注解的value属性值修改为prototype,如下所示。

@Bean
@Scope(value = "prototype")
public ScopeBean scopeBean(){
    return new ScopeBean();
}

此时,就会在Spring中创建ScopeBean类型的原型Bean。

(2)运行ScopeTest类

运行ScopeTest类的main()方法,输出的结果信息如下所示。

创建IOC容器开始...
创建IOC容器结束...
从IOC容器中第一次获取Bean对象开始...
执行ScopeBean类的构造方法...
io.binghe.spring.annotation.chapter17.bean.ScopeBean@fa36558
从IOC容器中第一次获取Bean对象结束...
从IOC容器中第二次获取Bean对象开始...
执行ScopeBean类的构造方法...
io.binghe.spring.annotation.chapter17.bean.ScopeBean@672872e1
从IOC容器中第二次获取Bean对象结束...

从输出的结果信息可以看出,Spring在IOC容器启动时,并不会创建单例Bean,而是从IOC容器中获取Bean对象时,才会创建Bean对象,并且每次从IOC容器中获取Bean对象时,都会创建新的Bean对象。

四、源码时序图

结合时序图理解源码会事半功倍,你觉得呢?

本章,会从注册Bean的流程、调用Bean工厂后置处理器和获取Bean的流程三个方面分析@Scope注解的源码时序图。

4.1 注册Bean的流程

@Scope注解涉及到的注册Bean流程的源码时序图如图17-1所示。

图17-1

查看完整文章

加入冰河技术知识星球,解锁完整技术文章与完整代码

在 GitHub 上编辑此页
上次更新: 2026/4/29 16:18
Contributors: binghe001
Prev
【付费】 第16章:深度解析@Primary注解
Next
【付费】 第18章:深度解析@PostConstruct注解与@PreDestroy注解
阅读全文
×

扫码或搜索:冰河技术
发送:290992
即可立即永久解锁本站全部文章

星球会员
跳转链接