Spring的Bean的生命周期,Bean的装配和注入

news2024/12/23 1:28:30

Spring容器启动获得BeanDefinition对象中有一个scope 属性。该属性控制着bean对象的作用域。本章节介绍Bean的作用域及生命周期,了解bean是怎么来的又怎么没的。

一、Bean的作用域
在Bean容器启动会读取bean的xml配置文件,然后将xml中每个bean元素分别转换成BeanDefinition对象。在BeanDefinition对象中有scope 属性,就是它控制着bean的作用域。
Spring框架支持5种作用域,有三种作用域是当开发者使用基于web的ApplicationContext的时候才生效的。下面就是Spring直接支持的作用域了,当然开发者也可以自己定制作用域。

作用域

描述

单例(singleton)

(默认)每一个Spring IoC容器都拥有唯一的一个实例对象

原型(prototype)

一个Bean定义,任意多个对象

请求(request)

一个HTTP请求会产生一个Bean对象,也就是说,每一个HTTP请求都有自己的Bean实例。只在基于web的Spring ApplicationContext中可用

会话(session)

限定一个Bean的作用域为HTTPsession的生命周期。同样,只有基于web的Spring ApplicationContext才能使用

全局会话(global session)

限定一个Bean的作用域为全局HTTPSession的生命周期。通常用于门户网站场景,同样,只有基于web的Spring ApplicationContext可用

我们可以以XMLInstance类为基础演示一下singleton和prototype作用域。
这里使用通过BeanFactory的getBean方法获取两次bean对象。

        XMLInstance instance=(XMLInstance)factory.getBean("xmlinstance");
        instance.setName("123");
        instance.Breath();
        instance=(XMLInstance)factory.getBean("xmlinstance");
        instance.Breath();

如果我们采用bean默认的作用域singleton,如下配置,则两个getbean获取的对象是一致的。

  <bean id="xmlinstance" class="com.demo.model.XMLInstance" scope="singleton">
      <property name="air" ref="CleanAir"></property>
      <property name="name" value="abc"></property>
  </bean>
输出结果:
Name:123;Air:CleanAir
Name:123;Air:CleanAir

如果我们采用bean默认的作用域prototype,如下配置,则两个getbean获取的对象是不一致的。

  <bean id="xmlinstance" class="com.demo.model.XMLInstance" scope="prototype">
      <property name="air" ref="CleanAir"></property>
      <property name="name" value="abc"></property>
  </bean>
输出结果:
Name:123;Air:CleanAir
Name:abc;Air:CleanAir

二、Bean的生命周期
前面章节介绍了bean容器以及bean的配置与注入,本章节学习bean的生命周期,了解bean是怎么来的又是怎么没的。

ApplicationContext容器中,Bean的生命周期流程如上图所示,流程大致如下:
1.首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化,
2.按照Bean定义信息配置信息,注入所有的属性,
3.如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id,此时该Bean就获得了自己在配置文件中的id,
4.如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory,
5.如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext,
6.如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation()方法,
7.如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法,
8.如果Bean配置了init-method方法,则会执行init-method配置的方法,
9.如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterInitialization()方法,
10.经过流程9之后,就可以正式使用该Bean了,对于scope为singleton的Bean,Spring的ioc容器中会缓存一份该bean的实例,而对于scope为prototype的Bean,每次被调用都会new一个新的对象,期生命周期就交给调用方管理了,不再是Spring容器进行管理了
11.容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法,
12.如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个Bean的生命周期结束。

这里在UserBean类基础上进行改造,增加了name属性并实现了ApplicationContextAware接口。

package com.demo.model;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class UserBean implements BeanNameAware,BeanFactoryAware,InitializingBean,DisposableBean,ApplicationContextAware {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("set方法被调用");
    }

    public UserBean() {
         System.out.println("UserBean类构造方法");
    }
    
    public void setBeanName(String name) {
        System.out.println("BeanNameAware被调用");
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryAware被调用");    
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean被调用");    
    }

    public void destroy() throws Exception {
        System.out.println("DisposableBean被调用");
    }
    //自定义的初始化函数 
    public void myInit() { 
        System.out.println("myInit被调用");
    } 
    //自定义的销毁方法 
    public void myDestroy() {
        System.out.println("myDestroy被调用"); 
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("setApplicationContext被调用");
    }

}

定义了后置处理器CusBeanPostProcessor 实现了BeanPostProcessor 接口。

package com.demo.model;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class CusBeanPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization被调用");
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization被调用");
        return bean;
    }
}

在xml中配置bean和BeanPostProcessor。

  <bean id="user" class="com.demo.model.UserBean" destroy-method="myDestroy" init-method="myInit">
      <property name="name" value="abc"></property>
  </bean> 
  <bean id="postProcessor" class="com.demo.model.CusBeanPostProcessor" />

测试:

        ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
        BeanFactory factory=context;
        UserBean user=(UserBean)context.getBean("user");
        ((ClassPathXmlApplicationContext)context).close();

输出结果:
UserBean类构造方法
set方法被调用
BeanNameAware被调用
BeanFactoryAware被调用
setApplicationContext被调用
postProcessBeforeInitialization被调用
InitializingBean被调用
myInit被调用
postProcessAfterInitialization被调用
DisposableBean被调用
myDestroy被调用

注解配置与xml配置的区别:
xml配置:书写灵活,修改方便,单修改xml时不需要修改源码,不用重新编译和部署。但维护起来相对麻烦。是一种集中式的的元数据,与源代码无绑定。
注解配置:写起来比较简单方便,配置、维护简单,但是修改很麻烦。是一种分散式的的元数据,与源代码紧绑定。
关于xml
xml优点:

1.使用xml配置可以让软件更具有扩展性;
比如,我们在spring中,我们不想使用接口而是想用接口的实现类,这个时候只需要修改xml配置中bean的class值就可以了。

2.对象之间的关系一目了然;

3.xml定义:可扩展标记语言,标准通用标记语言的子集,简称XML。从这个定义中我们可以发现,xml最大的优势就在于,开发者(程序员)能够为软件量身定做使用的标记,使得xml更通俗易懂;

4.成熟的校验机制,来保证正确。可以使用Schema或者是DTD来对xml的正确性进行校验。

5.基于xml配置的时候,只需要修改xml即可,不需要对现有的程序进行修改。

6.容易与其他系统进行数据交互。数据共享方便
xml的缺点

1.应用程序中如果使用了xml配置,需要解析xml的工具或者是是第三方类库的支持。

2.解析xml的时候必然会占用资源,势必会影响到应用程序的性能;以java为例,无论是将xml一次性装置到内存中,还是一行一行读取解析的,都会占用资源的。

3.在程序编译期间无法对其配置项的正确性进行验证,只能在运行期发现。

4.出错后,排错变得困难。往往在配置的时候,一个手误就会出现莫名其妙的错误。

5.开发的时候,既要维护代码又要维护配置文件,使得开发的效率降低。

6.代码与配置项之间有时候会存在很多“潜规则”.改变了任意一方,都有可能影响到另一方的使用。这是个大坑。比如:自定义的标记,如果其他开发不清楚这些的话,修改了无论是代码还是xml的配置,都会导致程序不能正常运行。

7.开发工具对xml的验证支持的不是很好。比如idea,对xml正确性,如果是自定义的,验证就不是很好。

关于注解
注解的优点
1.注解的解析可以不依赖于第三方库,可以之间使用Java自带的反射。

2.注解和代码在一起的,之间在类上,降低了维护两个地方的成本。

3.注解如果有问题,在编译期间,就可以验证正确性,如果出错更容易找。

4.使用注解开发能够提高开发效率。不用多个地方维护,不用考虑是否存在“潜规则”。

5.xml配置起来有时候冗长,此时注解可能是更好的选择,如jpa的实体映射;注解在处理一些不变的元数据时有时候比XML方便的多,比如springmvc的数据绑定,如果用xml写的代码会多的多;

6.注解最大的好处就是简化了XML配置;其实大部分注解一定确定后很少会改变,所以在一些中小项目中使用注解反而提供了开发效率,所以没必要一头走到黑;

7.注解相对于XML的另一个好处是类型安全的,XML只能在运行期才能发现问题。
注解的缺点
1.修改的话比较麻烦。如果需要对注解进行修改的话,就需要对整个项目重新编译。

2.处理业务类之间的复杂关系,不然xml那样容易修改,也不及xml那样明了。

3.在程序中注解太多的话,会影响代码质量,代码简洁会有影响。

4.如果后来的人对注解不了解,会给维护带来成本。

5.注解功能没有xml配置齐全。
总结
注解优点:简化配置,使用起来直观且容易,提升开发的效率,类型安全,容易检测出问题
缺点:修改起来比xml麻烦,如果不项目不了解,可能给开发和维护带来麻烦
Xml优点:把类与类之间松解偶;修改方便;容易扩展,容易和其他系统进行数据交互,对象之间的关系一目了然
缺点:配置冗长,需要额外维护;影响开发效率,类型不安全,校验不出来,出错不好排查

注解配置与xml配置都有各自的优缺点,没有那个是比对方更好的,只有在合适的地方选择合适的配置才是最优的。

三、Bean的管理操作——装配注入

bean管理指的是这两个操作:1.创建对象, 2.注入属性。

Bean装配注入的两种方式:

1.基于xml配置文件的方式实现, 2.基于注解方式实现。

(1)不同点: xml配置注入需要在spring的配置文件中添加bean及其相关属性,而使用注解注入可以实现自动装配,代码无需任何额外操作即可自动装配; (2)xml配置对于bean的依赖性更有优势,因为可以直接在配置文件中指定属性的依赖关系,而注解注入只能使用接口定义依赖; (3)xml配置提供更多的配置属性,允许您细化bean的更多特性,而注解只是简单地把bean装配在一起。使用XML配置的话,实现过程非常繁琐,不容易理解,同时配置文件较为凌乱,不便于查错和调试,同时必须依赖于XML文件,不能够进行代码编译。

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

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

相关文章

Bean作用域和生命周期

目录 Bean作用域的例子 作用域定义 Bean的六种作用域 设置作用域 Spring的执行过程和Bean的生命周期 Spring的主要执行流程 Bean的生命周期 在上篇博客中我们使用Spring存储和获取Bean,因此Bean是Spring中最重要的资源,今天这篇博客就深入了解Bean对象 Bean作用域的例子 …

redis(8)有序集合Zset

有序集合Zset Redis 有序集合 zset 与普通集合 set 非常相似&#xff0c;是一个没有重复元素的字符串集合。 不同之处是有序集合的每个成员都关联了一个评分(score)&#xff0c;这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的&#xf…

如何在Linux中实现进程间通信

致前行路上的人&#xff1a; 要努力&#xff0c;但不要着急&#xff0c;繁花锦簇&#xff0c;硕果累累都需要过程&#xff01; 目录 1.进程间通信介绍 1.1进程间通信的目的 1.2进程间通信发展 1.3进程间通信分类 1.4进程间通信的本质 2.管道 2.1什么是管道 2.2管道与进程的关系…

华为OD机试题,用 Java 解【玩牌高手】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

智能算法实现PID智能车控制系统

智能算法实现PID智能车控制系统摘要关键词第一章绪论1.1智能车概述1.2智能PID研究现状1.3本文工作第二章 PID控制简介第三章 内模PID简介3.1 内模PID控制第四章内模智能PID智能车控制系统设计4.1 系统设计4.2内模控制原理第五章 系统仿真及结果分析5.1 系统仿真分析5.2 控制效果…

前端资源浏览器渲染原理

浏览器的渲染页面过程 HTML解析过程 一般情况下服务器会给浏览器返回 xx.html 文件 解析html 其实就是 Dom 树的构建过程 我们可以根据以下html 结构 来简单的分析出 html 的解析过程 解析CSS 规则树 在解析的过程中&#xff0c;如果遇到CSS的link元素&#xff0c;那么会由浏览…

分享 7 个有用的 JavaScript 库,也许你会用的上

使用这7个库&#xff0c;加速你的项目开发当我们可以通过使用库轻松实现相同的结果时&#xff0c;为什么还要编写自定义功能&#xff1f;开发人员最好的朋友和救星就是这些第三方库。我相信一个好的项目会利用一些可用的最佳库。本系列就是帮助前端开发者整理一些相关的优秀的J…

一文了解互联网中的UI设计师

&#xff08;点击即可收听&#xff09;一文了解UI设计师UI设计师是什么岗位,以及它的职责所在是什么,初入职场,今天带你一起来了解一下宏观上:设计师微观上: 上游对接产品经理,根据产品经理产出的PRD(也就是产品文档手册),以及产品原型,设计产品的UI界面,产出设计稿(如:蓝湖,Sk…

vite:常见的配置

最近在捣鼓一下vite&#xff0c;因为自己一直在使用react&#xff0c;就选择vite、react来体验一下vite。 使用最简单的方法创建一个应用&#xff1a;yarn create vite&#xff0c;然后选择react框架。 vite默认配置是使用了defineConfig工具函数&#xff1a; import { defi…

TCP协议原理二

文章目录四、滑动窗口二、流量窗口三、拥塞控制四、滑动窗口 前面我们学习了 确认应答&#xff0c;超时重传&#xff0c;连接管理&#xff0c;这些机制都为我们TCP的可靠性提供了保证&#xff0c;当然在保证TCP的可靠性的同时&#xff0c;传输效率也受到了一定的影响&#xff…

[创业之路-56] :职场真相:越便宜的员工其实越贵

引言&#xff1a;创业公司由于资金的限制&#xff0c;喜欢招聘经验不多、价格便宜的员工&#xff0c;殊不知&#xff0c;放在宏观的视角看&#xff0c;越发发现&#xff0c;越便宜的员工其实越贵&#xff0c;虽然他们能加班、好学习、听话&#xff0c;但创业公司不是大学&#…

【华为OD机试模拟题】用 C++ 实现 - 剩余可用字符集 or @分割可用字符集(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 获得完美走位(2023.Q1) 文章目录 最近更新的博客使用说明剩余可用字符集 or @分割可用字符集题目输入输出示例一输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才…

LLVM高级架构介绍

LLVM 为什么要开一个LLVM的新坑呢&#xff1f; 我从智能穿戴转行到芯片软件行业&#xff0c;从事编译器开发&#xff0c;不过是AI编译器。不过基本的传统编译器还是绕不过去啊&#xff0c;所以开始学习LLVM&#xff0c;后面开始学习TVM&#xff0c;MLIR。 LLVM GitHub地址 L…

vue-知识点总结

历史 2015年 10月27 1.0.0 Evangelion 新福音战士2016年 10月1日 2.0.0 Ghost in the Shell 攻壳机动队2019年 2月 2.62020年 9月18日 3.0.0 One Piece(海贼王)2021年 8月10日 3.2.0 .sync where 父向子传递props, 需要双向绑定的时候(子组件想更新这个值) how <one-comp…

vscode配置drawio绘制流程图

目录 vscode配置drwaio drawio使用 添加元素 调整元素 连接元素 添加公式 图像与表格 组合元素 保存以及导出 最近课程设计需要画流程框图&#xff0c;所以在网上找找绘制流程图的软件。然后我选择了这个drawio&#xff0c;无他&#xff0c;只是因为用vscode插件就…

同步和异步promise

进程和线程进程&#xff08;厂房&#xff09;&#xff1a;程序的运行环境线程&#xff08;工人&#xff09;&#xff1a;进行运算的东西同步和异步同步&#xff1a;一件事干完才去干下一件事&#xff0c;前面的代码不执行&#xff0c;后面的代码也不执行。同步的代码可能会出现…

lesson01_ts知识回顾

基本数据类型 undefined 和 null 可赋值给其他基本数据类型 let num: number 3; let flag: boolean false; let str: string "abc";let a: null null; let b: undefined undefined;联合数据类型 let variable: number | boolean false; variable 123;数组 …

指针的进阶【中篇】

文章目录&#x1f4c0;4.数组参数&#x1f4bf;4.1.一维数组传参&#x1f4bf;4.2.二维数组传参&#x1f4c0;5.指针参数&#x1f4bf;5.1.一级指针传参&#x1f4bf;5.2.二级指针传参&#x1f4c0;6.函数指针&#x1f4bf;6.1. 代码1&#x1f4bf;6.2. 代码2&#x1f4c0;7.函…

Linux 进程:理解进程和pcb

目录一、进程的概念二、CPU分时机制三、并发与并行1.并发2.并行四、pcb的概念一、进程的概念 什么是进程&#xff1f; 进程就是进行中的程序&#xff0c;即运行中的应用程序。比如&#xff1a;电脑上打开的LOL、QQ…… 这些都是一个个的进程。 什么是应用程序&#xff1f; 应用…

【java基础】static和final关键字的作用及其用法详解

文章目录static关键字静态字段静态方法静态代码块静态内部类final关键字final字段final方法final类static关键字 这个关键字表示静态的&#xff0c;用于不同地方意思不一样 静态字段 如果我们将其作用到字段上&#xff0c;那么该字段为类所拥有&#xff0c;我们使用new关键字…