【JavaEE】Bean的作用域和生命周期

一.Bean的作用域.

1.1 Bean的相关概念.

  • 通过Spring IoC和DI的学习(不清楚的可以看的前面写过的总结,可以快速入门, http://t.csdnimg.cn/K8Xr0),我们知道了Spring是如何帮助我们管理对象的

      1. 通过 @Controller , @Service , @Repository , @Component , @Configuration ,
        @Bean 来声明Bean对象.
      1. 通过 ApplicationContext 或者 BeanFactory 来获取对象
      1. 通过 @Autowired , Setter 方法或者构造方法等来为应用程序注入所依赖的Bean对象
  • 通过代码来演示回顾一下:
    创建需要注入的对象

public class Dog {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}

把这个对象进行实例化,并且交给Spring进行管理

@Configuration
public class DogConfig {@Beanpublic Dog dog() {Dog dog = new Dog();dog.setName("旺财");return dog;}
}

通过ApplicationContext来获取对象.

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class SpringPrincipleApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(com.tuanzi.springprinciple.SpringPrincipleApplication.class, args);Dog bean = context.getBean(Dog.class);System.out.println(bean.getName());}
}

在这里插入图片描述
也可以通过在代码中直接注入ApplicationContext的方式来获取Spring容器

@SpringBootTest
class SpringPrincipleApplicationTests {@Autowiredpublic ApplicationContext context;@Testvoid contextLoads() {Dog bean1 = context.getBean("dog", Dog.class);System.out.println(bean1);Dog bean2 = context.getBean("dog", Dog.class);System.out.println(bean2);}
}

在这里插入图片描述

根据运行结果我们可以看出, 这两次获取的对象是同一个对象, 默认情况下从Spring容器中取出来的对象是同一个

  • 默认情况下, Spring容器中的bean都是单例的, 这种行为模式, 我们就称之为Bean的作用域

二.Bean的作用域

2.1 七种Bean的作用域

  • Bean的作用域定义了Bean实例的生命周期及其在Spring容器中的可见性。
  • 在Spring中支持6中作用域, 后四种在MVC环境下才能生效:
名称作用域说明
单例作用域singleton每个Spring IoC容器内同名称的Bean只有一个实例(单例) 默认情况
原型作用域prototype每次使用该Bean的时侯,都会创建新的实例
请求作用域request每个HTTP请求都会创建一个新的Bean实例,请求结束时销毁
会话作用域session每个HTTP会话都会创建一个新的Bean实例,会话结束时销毁
全局作用域Application在ServletContext范围内,整个Web应用程序共享同一个Bean实例
HTTP WebSocket作用域websocket在ServletContext范围内,整个Web应用程序共享同一个Bean实例
全局作用域Application一个Bean定义对应于单个websocket的生命周期,仅在WebApplicationContext环境中有效
自定义作用域custom除了以上六种标准作用域,Spring还允许开发人员创建自定义作用域,通过实现Scope接口并注册到Spring容器中

参考文档: https://docs.spring.io/spring-framework/reference/core/beans/factory-scopes.html

2.2 代码演示.

2.2.1 单例作用域(singleton)

@Configuration
public class DogConfig {@Bean@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)public Dog singletonDog() {return new Dog();}
}
@RestController
public class DogController {@Autowiredprivate ApplicationContext applicationContext;@Autowiredprivate Dog singletonDog;@RequestMapping("/single")public String single(){Dog contextDog = applicationContext.getBean("singletonDog", Dog.class);return "contextDog = " + contextDog+", "+"autowiredDog = " + singletonDog;}

观察Bean的作用域. 单例作⽤域: http://127.0.0.1:8080/single
多次访问, 得到的都是同⼀个对象, 并且 @Autowired 和 applicationContext.getBean()
也是同⼀个对象
在这里插入图片描述

2.2.2 原型作用域( prototype)

	@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public Dog prototypeDog() {return new Dog();}
	@Autowiredprivate Dog prototypeDog;@RequestMapping("/prototype")public String prototype(){Dog contextDog = applicationContext.getBean("prototypeDog", Dog.class);return "contextDog = " + contextDog+", "+"autowiredDog = " + prototypeDog;}

多例作用域: http://127.0.0.1:8080/prototype
观察ContextDog, 每次获取的对象都不一样==(注入的对象在Spring容器启动时, 就已经注入了, 所以多次请求也不会发生变化==)
在这里插入图片描述
在这里插入图片描述

2.2.3 请求作用域(request)

	@Bean@RequestScopepublic Dog requestDog() {return new Dog();}
	@Autowiredprivate Dog requestDog;@RequestMapping("/request")public String request(){Dog contextDog = applicationContext.getBean("requestDog", Dog.class);return "contextDog = " + contextDog+", autowiredDog = " + requestDog;}

请求作用域: http://127.0.0.1:8080/request
在一次请求中, @Autowired 和 applicationContext.getBean() 也是同一个对象.但是每次请求(刷新浏览器当前页面), 都会重新创建对象.
在这里插入图片描述
在这里插入图片描述

2.2.4 会话作用域(session)

	@Bean@SessionScopepublic Dog sessionDog() {return new Dog();}
	@Autowiredprivate Dog sessionDog;@RequestMapping("/session")public String session(){Dog contextDog = applicationContext.getBean("sessionDog", Dog.class);return "contextDog = " + contextDog+", autowiredDog = " + sessionDog;}

在一个session中, 多次请求, 获取到的对象都是同一个, 换一个浏览器访问, 发现会重新创建对象.(另一个Session)
Google浏览器运行结果:
在这里插入图片描述
Edge浏览器运行结果:
在这里插入图片描述

2.2.5 Application(全局作用域)

	@Bean@ApplicationScopepublic Dog applicationDog() {return new Dog();}
	@Autowiredprivate Dog applicationDog;@RequestMapping("/application")public String application(){Dog contextDog = applicationContext.getBean("applicationDog", Dog.class);return "contextDog = " + contextDog+", autowiredDog = " + applicationDog;}

在一个应用中, 多次访问都是同一个对象(不同的浏览器也是同一个对象).
Application scope就是对于整个web容器来说, bean的作⽤域是ServletContext级别的. 这个和
singleton有点类似,==区别在于: Application scope是ServletContext的单例, singleton是⼀个
ApplicationContext的单例. 在⼀个web容器中ApplicationContext可以有多个. ==
Google浏览器运行结果:
在这里插入图片描述
Edge浏览器运行结果:
在这里插入图片描述

三.Bean的生命周期.

3.1 基本概念

  • 生命周期指的是一个对象从诞生到销毁的整个生命过程, 我们把这个过程就叫做一个对象的生命周期.
  • Bean 的生命周期分为以下5个部分:
    • 1.实例化(为Bean分配内存空间)
    • 2.属性赋值(Bean注入和装配, 比如 @AutoWired )
    • 3.初始化
      • a. 执行各种通知, 如 BeanNameAware , BeanFactoryAware ,ApplicationContextAware 的接口方法.
      • b. 执行初始化方法
        • xml定义 init-method
        • 使用注解的方式 @PostConstruct
        • 执行初始化后置方法( BeanPostProcessor )
      1. 使用Bean
      1. 销毁Bean
      • a. 销毁容器的各种方法, 如 @PreDestroy , DisposableBean 接口方法, destroy method.

实例化和属性赋值对应构造⽅法和setter⽅法的注⼊. 初始化和销毁是⽤⼾能⾃定义扩展的两个阶段,
可以在实例化之后, 类加载完成之前进⾏⾃定义"事件"处理.
⽐如我们现在需要买⼀栋房⼦, 那么我们的流程是这样的:

  1. 先买房(实例化, 从⽆到有)
  2. 装修(设置属性)
  3. 买家电, 如洗⾐机, 冰箱, 电视, 空调等([各种]初始化,可以⼊住);
  4. ⼊住(使⽤ Bean)
  5. 卖房(Bean 销毁)
  • 执⾏流程如下图所⽰:
    在这里插入图片描述

3.2 代码实现.

@Component
public class BeanLifeComponent implements BeanNameAware {private Dog singletonDog;public BeanLifeComponent() {System.out.println("执行构造函数....");}@Autowiredpublic void setSingletonDog(Dog singletonDog) {this.singletonDog = singletonDog;System.out.println("执行了setSingletonDog");}@Overridepublic void setBeanName(String name) {System.out.println("setBeanName"+name);}@PostConstructpublic void init() {System.out.println("执行PostConstruct方法");}public void use(){System.out.println("执行use方法");}@PreDestroypublic void destroy(){System.out.println("执行destroy");}
}

执行结果:
在这里插入图片描述
通过运行结果观察

  1. 先执行构造函数
  2. 设置属性
  3. Bean初始化
  4. 使用Bean
  5. 销毁Bean

3.3 生命周期的源码阅读.

  • 创建Bean的代码入口在 AbstractAutowireCapableBeanFactory#createBean

== 点进去继续看源码: AbstractAutowireCapableBeanFactory#createBean ==

//---------------------------------------------------------------------// Implementation of relevant AbstractBeanFactory template methods//---------------------------------------------------------------------/*** Central method of this class: creates a bean instance,* populates the bean instance, applies post-processors, etc.* @see #doCreateBean*/@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {if (logger.isTraceEnabled()) {logger.trace("Creating instance of bean '" + beanName + "'");}RootBeanDefinition mbdToUse = mbd;// Make sure bean class is actually resolved at this point, and// clone the bean definition in case of a dynamically resolved Class// which cannot be stored in the shared merged bean definition.Class<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);try {mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}}try {// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}try {Object beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;}catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {// A previously detected exception with proper bean creation context already,// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.throw ex;}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);}}

三方法与三个生命周期阶段一一对应

  1. createBeanInstance() -> 实例化
  2. populateBean() -> 属性赋值
  3. initializeBean() -> 初始化

继续点进去 initializeBean

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {invokeAwareMethods(beanName, bean);Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);}if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;}private void invokeAwareMethods(String beanName, Object bean) {if (bean instanceof Aware) {if (bean instanceof BeanNameAware beanNameAware) {beanNameAware.setBeanName(beanName);}if (bean instanceof BeanClassLoaderAware beanClassLoaderAware) {ClassLoader bcl = getBeanClassLoader();if (bcl != null) {beanClassLoaderAware.setBeanClassLoader(bcl);}}if (bean instanceof BeanFactoryAware beanFactoryAware) {beanFactoryAware.setBeanFactory(AbstractAutowireCapableBeanFactory.this);}}}

可以看到,调用了三个Bean开头的Aware方法.

四. 总结

  1. Bean的作用域共分为6种: singleton, prototype, request, session, application和websocket.
  2. Bean的生命周期共分为5大部分: 实例化, 属性复制, 初始化, 使用和销毁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1487453.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

开发桌面程序-Electron入门

Electron是什么 来自官网的介绍 Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。 总…

JL 跳转指令的理解

一般情况下&#xff0c;JU 和 JC 是最常见的跳转指令&#xff1b;但有时会用到JL 指令&#xff0c;JL 说起来更像是一组指令&#xff0c;类似C,C# 语言中的 switch case 语句&#xff0c;但是有个明显的不同&#xff0c;前者的判断条件可以是任意合理数字&#xff0c;后者范围…

C语言 之 理解指针(1)

文章目录 1. 内存和地址2. 指针变量和地址2.1 取地址操作符&#xff08;&&#xff09;2.2 指针变量和解引用操作符&#xff08;*&#xff09;2.2.1 指针变量2.2.2 指针类型的理解2.2.3 解引用操作符(*) 2.3 指针变量的大小 3. 指针变量类型的意义3.1 指针的解引用3.2 指针-…

【科研】# Taylor Francis 论文 LaTeX template模版 及 Word模版

【科研写论文】系列 文章目录 【科研写论文】系列前言一、Word 模板&#xff08;附下载网址&#xff09;&#xff1a;二、LaTeX 版本方法1&#xff1a;直接网页端打开&#xff08;附网址&#xff09;方法2&#xff1a;直接下载到本地电脑上编辑下载地址说明及注意事项 前言 给…

计算机网络基础:4.HTTP与HTTPS

一、回顾设定 想象你在经营一家繁忙的餐厅&#xff0c;顾客们通过点餐系统&#xff08;网卡&#xff09;下单&#xff0c;订单被前台&#xff08;路由器&#xff09;接收并分发到各个厨房区域&#xff08;网络设备&#xff09;。光猫像是食材供应商&#xff0c;通过高效的物流系…

lua 游戏架构 之 游戏 AI (四)ai_autofight_find_target

定义一个名为 ai_autofight_find_target 的类&#xff0c;继承自 ai_base 类。 lua 游戏架构 之 游戏 AI &#xff08;一&#xff09;ai_base-CSDN博客文章浏览阅读237次。定义了一套接口和属性&#xff0c;可以基于这个基础类派生出具有特定行为的AI组件。例如&#xff0c;可…

初识redis:通用命令

今天我们简单介绍一些关于redis的基础命令。 目录 get 和 set 全局命令 keys EXISTS del&#xff08;delete&#xff09; expire TTL Redis的key过期策略是怎么实现的&#xff1f; type get 和 set 连接到云服务器后&#xff0c;通过redis-cli命令进入到redis客户端…

设计模式--创建型

实现 #include <iostream> #include <memory>// 抽象产品类 class Product {public:virtual void Operation() const 0; };// 具体产品 类A class ConcreteProductA : public Product {public:virtual void Operation() const override {std::cout << &quo…

学术研讨 | 区块链治理与应用创新研讨会顺利召开

学术研讨 近日&#xff0c;国家区块链技术创新中心组织&#xff0c;长安链开源社区支持的“区块链治理与应用创新研讨会”顺利召开&#xff0c;会议围绕区块链治理全球发展现状、研究基础、发展趋势以及区块链行业应用创新展开研讨。北京大学陈钟教授做了“区块链治理与应用创…

【STM32嵌入式系统设计与开发拓展】——12_Timer(定时器中断实验)

目录 1、什么是定时器&#xff1f;定时器用于测量时间间隔&#xff0c;而计数器用于计数外部事件的次数 2、定时器的主要功能和用途&#xff1f;3、定时器类型&#xff1f;4、定时器的编写过程5、代码分析定时器计算&#xff1f;计算过程周期&#xff08;arr&#xff09;&#…

Apollo使用(3):分布式docker部署

Apollo 1.7.0版本开始会默认上传Docker镜像到Docker Hub&#xff0c;可以按照如下步骤获取 一、获取镜像 1、Apollo Config Service 获取镜像 docker pull apolloconfig/apollo-configservice:${version} 我事先下载过该镜像&#xff0c;所以跳过该步骤。 2、Apollo Admin S…

Leetcode3219. 切蛋糕的最小总开销 II

Every day a Leetcode 题目来源&#xff1a;3219. 切蛋糕的最小总开销 II 解法1&#xff1a;贪心 谁的开销更大&#xff0c;就先切谁&#xff0c;并且这个先后顺序与切的次数无关。 代码&#xff1a; /** lc appleetcode.cn id3219 langcpp** [3219] 切蛋糕的最小总开销 I…

医疗信息化之PACS系统源码,C#医学影像系统源码,成熟在用稳定运中

C#语言开发的一套PACS系统源码&#xff0c;C/S架构&#xff0c;成熟稳定&#xff0c;多家大型综合医院应用案例。自主版权&#xff0c;支持二次开发&#xff0c;授权后可商用。 医学影像存储与传输系统是针对数据库存储、传输服务、图像处理进行了优化,存储更安全、传输更稳定、…

人工智能与机器学习原理精解【6】

文章目录 数值优化基础理论凹凸性定义在国外与国内存在不同国内定义国外定义总结示例与说明注意事项 国内凹凸性二阶定义的例子凹函数例子凸函数例子 凸函数&#xff08;convex function&#xff09;的开口方向凸函数的二阶导数凸函数的二阶定义单变量函数的二阶定义多变量函数…

C# 知识点总结

入门 C#程序在.NET上运行&#xff0c;.NET framework包含两个部分&#xff1a; ①&#xff1a;.NET framework类库 ②&#xff1a;公共语言运行库CLR&#xff08;.NET虚拟机&#xff09; CLS&#xff08;公共语言规范&#xff09; CTS&#xff08;通用类型系统&#xff09; .N…

【Node.js基础04】包的理解与使用

一&#xff1a;包的理解与简介 1 什么是包 包是一个将模块、代码、以及其他资料聚合成的文件夹 2 包的分类 项目包&#xff1a;编写项目代码的文件夹 软件包&#xff1a;封装工具和方法供开发者使用 3 为什么要在软件包中编写package.json文件 记录包的清单信息 二&…

09-optee内核-线程处理

快速链接: . 👉👉👉 个人博客笔记导读目录(全部) 👈👈👈 付费专栏-付费课程 【购买须知】:【精选】TEE从入门到精通-[目录] 👈👈👈线程处理 OP-TEE内核使用几个线程来支持在 并行(未完全启用!有用于不同目的的处理程序。在thread.c中,您将找到一个名为…

Binius-based zkVM:为Polygon AggLayer开发、FPGA加速的zkVM

1. 引言 近日&#xff0c;ZK硬件加速巨头Irreducible和Polygon团队宣布联合开发生产级的Binius-based zkVM&#xff0c;用于助力Polygon AggLayer&#xff0c;实现具有低开销、硬件加速的binary proofs。 Irreducible&#xff08;曾用名为Ulvetanna&#xff09;团队 Benjamin …

JavaSE从零开始到精通(八) - 单列集合类

1. 集合概述 集合类是一种用于存储对象的容器&#xff0c;Java 提供了一组预定义的集合类&#xff0c;它们位于 java.util 包中。这些集合类提供了各种数据结构&#xff0c;包括列表、集合、队列、映射等&#xff0c;每种数据结构都有其独特的特性和用途。 Java 中的集合类是…

secureCRT同时在所有已打开窗口执行命令、mac-os下使用的SecureCRT版本 以及 SecureCRT一段时间不操作没有响应的问题

一、secureCRT命令行工具一次性同时在所有已打开窗口执行命令 公司的服务器比较多&#xff0c;最近因为opcache&#xff0c;上线发布后&#xff0c;需要重启所有的WEB服务器上的php。目前使用的jenkins发布&#xff0c;不过账号安全问题&#xff0c;给jenkins的账号权限受限不能…