当前位置: 首页 > news >正文

【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大部分: 实例化, 属性复制, 初始化, 使用和销毁

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • AI/机器学习(计算机视觉/NLP)方向面试复习3
  • 如何通过一条SQL变更多个分库分表?
  • iptables 限制端口仅特定IP访问。
  • Apache DolphinScheduler 3.2.2 版本正式发布!
  • 一文解析:代理IP的五大优势
  • 【C#】获取DICOM图像像素的像素值
  • 【CTFWP】ctfshow-web42
  • Spark实时(一):StructuredStreaming 介绍
  • 推荐系统三十六式学习笔记:工程篇.常见架构25|Netflix个性化推荐架构
  • 【SpringBoot教程:从入门到精通】掌握Springboot开发技巧和窍门(四)-Vue项目配置环境、导航栏
  • MySQL常见指令
  • Python 高阶语法
  • MiniExcel:.NET中处理Excel的高效方案
  • 金蝶插件调用HTTP请求 并解析JSON
  • RabbitMQ普通集群搭建指南
  • 【mysql】环境安装、服务启动、密码设置
  • idea + plantuml 画流程图
  • leetcode-27. Remove Element
  • Lsb图片隐写
  • PHP那些事儿
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • python学习笔记 - ThreadLocal
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • Vue.js源码(2):初探List Rendering
  • 从setTimeout-setInterval看JS线程
  • 当SetTimeout遇到了字符串
  • 浅谈web中前端模板引擎的使用
  • 小而合理的前端理论:rscss和rsjs
  • 在weex里面使用chart图表
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • #define用法
  • #QT(QCharts绘制曲线)
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (1)bark-ml
  • (2)nginx 安装、启停
  • (3)nginx 配置(nginx.conf)
  • (4)(4.6) Triducer
  • (STM32笔记)九、RCC时钟树与时钟 第一部分
  • (二)windows配置JDK环境
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (六)软件测试分工
  • (十八)三元表达式和列表解析
  • (五)关系数据库标准语言SQL
  • (学习日记)2024.02.29:UCOSIII第二节
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .bat批处理(六):替换字符串中匹配的子串
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .NET 中选择合适的文件打开模式(CreateNew, Create, Open, OpenOrCreate, Truncate, Append)
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .net图片验证码生成、点击刷新及验证输入是否正确
  • [ Linux ] git工具的基本使用(仓库的构建,提交)
  • [202209]mysql8.0 双主集群搭建 亲测可用