Spring Bean基础

2023-11-13

1.1 定义Spring Bean

AbstractBeanDefinition中有Bean的配置元信息

1.1.1 什么是BeanDefinition?

BeanDefinition是Spring Framework中定义Bean的配置元信息接口,包含:

  • Bean的类名
  • Bean行为配置元素,如作用域,自动绑定的模式(@Autowired),声明周期回调等
  • 其他Bean引用,又称合作者或者依赖
  • 配置设置,比如Bean属性(Properties),例如数据库连接池的配置

1.2 通过BeanDefinition构建Bean

  • 通过BeanDefinitionBuilder构建
 // 1. 通过BeanDefinitionBuilder构建
       BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
       beanDefinitionBuilder.addPropertyValue("age", 14);
       beanDefinitionBuilder.addPropertyValue("name", "xiaomage");

       // 获取BeanDefinition实例
       BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
  • 通过AbstractBeanDefinition构建
// 2. 通过AbstractBeanDefinition
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClass(User.class);

        // 通过MutablePropertyValues临时存储properties属性值
        MutablePropertyValues propertyValues = new MutablePropertyValues();
//        propertyValues.addPropertyValue("age", 14);
//        propertyValues.addPropertyValue("name", "xiaomage");
        propertyValues
                .add("age", 14)
                .add("name", "xiaomage");
        // setPropertyValues:设置Bean的属性值
        genericBeanDefinition.setPropertyValues(propertyValues);

1.3 注册Bean(BeanDefinition 注册)

  • XML配置元信息
    • <bean id="…" class="…"… />
  • Java注解配置元信息(AnnotationConfigApplicationContext)
    • @Bean
    • @Component
    • @Import
  • Java API配置元信息
    • 命名方式:org.springframework.beans.factory.support.BeanDefinitionRegistry#registerBeanDefinition
    • 非命名方式:org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerWithGeneratedName
    • 配置类方式:org.springframework.context.annotation.AnnotatedBeanDefinitionReader#register

1.4 实例化Spring bean

  • 常规方式

    • 通过构造器(配置元信息:XML,JAVA注解和JavaApi)
    • 通过静态工厂
    • 通过Bean工厂方法
    • 通过FactoryBean
  • 特殊方式

    • 通过ServiceLoaderFactoryBean
    • 通过AutowireCapableBeanFactory#createBean
    • 通过BeanDefininationReistry#registerBeanDefinition(String,BeanDefinition)

静态工厂:

 <!--  添加factory-method-->
  <bean id="user-create-by-static-method" class="edu.ahau.thinking.in.spring.ioc.overview.domain.User" factory-method="createUser"/>

在User类里添加createUser静态方法,跟factory-method中的字符串一样;

public static User createUser(){
        User user = new User();
        user.setAge(12);
        user.setName("zasn");
        return user;
    }

1.4.1 ServiceLoaderFactoryBean

1.4.1.1 先介绍java的控制反转(ServiceLoader)

  1. 点进ServiceLoader,查看:发现有前缀限制,
    在这里插入图片描述
  2. 因此需要在resource包下建立META-INF/services/xxx文件,文件的名字就是接口名称,文件里面的全限定类名是实现接口的类;
    在这里插入图片描述
  3. java代码获取
ServiceLoader<UserFactory> serviceLoader = ServiceLoader.load(UserFactory.class, Thread.currentThread().getContextClassLoader());

对ServiceLoader对象遍历Iterator<UserFactory> iterator = serviceLoader.iterator();

1.4.1.2 通过AutowireCapableBeanFactoryBean获取ServiceLoader

<bean id="serviceLoaderFactoryBean" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
<!--    org.springframework.beans.factory.serviceloader.AbstractServiceLoaderBasedFactoryBean中的serviceType-->
    <property name="serviceType" value="edu.ahau.thinking.in.spring.bean.factory.UserFactory"/>
  </bean>
  • 使用Spring应用上下文获取AutowireCapableBeanFactory
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");
        AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
        ServiceLoader<UserFactory> serviceLoaderFactoryBean = beanFactory.getBean("serviceLoaderFactoryBean", ServiceLoader.class);

通过AutowireCapableBeanFactory可以实例化Bean,通过调用createBean(Class),入参的class对象和不能是接口,而是一个实体类。

UserFactory bean = beanFactory.createBean(DefaultUserFactory.class);

1.5 Bean的初始化

三种方法:

  • @PostConstruct
  • 实现InitializingBean接口
  • 自定义方法(initMethod)

代码如下:

public class DefaultUserFactory implements UserFactory, InitializingBean {
    @PostConstruct
    public void init() {
        System.out.println("由@PostConstruct,UserFactory初始化中....");
    }

    @Override
    public void initUserFactory(){
        System.out.println("由自定义,UserFactory初始化中....");
    }

    // 实现InitializingBean接口,在设置属性后初始化
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("实现InitializingBean接口,在设置属性后初始化....");
    }
}
public class BeanInitializationDemo {
    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册配置类
        applicationContext.register(BeanInitializationDemo.class);
        // 启动Spring应用上下文
        applicationContext.refresh();
        // 依赖查找
        applicationContext.getBean(UserFactory.class);
        // 关闭Spring应用上下文
        applicationContext.close();
    }

    @Bean(initMethod = "initUserFactory")
    public UserFactory userFactory() {
        return new DefaultUserFactory();
    }
}

输出结果:

由@PostConstruct,UserFactory初始化中....
实现InitializingBean接口,在设置属性后初始化....
由自定义,UserFactory初始化中....

得到初始化的顺序:@PostConstruct > 实现InitializingBean接口 > 自定义自定义方法(优先级从大到小)

@Bean(initMethod = "initUserFactory") 在@Bean注解中需要取到initMethod的映射关系;

查看AbstractBeanDefinition中的setInitMethodName(String initMethodName)方法,在这个地方进行debug,需要加上debug的condition条件才能很明显的看到initMethodName.在这里插入图片描述

1.5.1 Bean的延迟初始化

@Bean处加上@Lazy,比较一下即可:
非延迟加载,控制台如下

由@PostConstruct,UserFactory初始化中....
实现InitializingBean接口,在设置属性后初始化....
由自定义,UserFactory初始化中....
Spring应用上下文已启动

延迟加载,控制台如下

Spring应用上下文已启动
由@PostConstruct,UserFactory初始化中....
实现InitializingBean接口,在设置属性后初始化....
由自定义,UserFactory初始化中....

根据控制台打印数据可以看出:延迟加载影响Bean初始化位置。延迟加载,Spring应用上下文启动是在Bean初始化之前;反之,非延迟加载。

查看org.springframework.context.support.AbstractApplicationContext#refresh源码,应用上下文启动的时候会初始化没有延迟加载的Bean
在这里插入图片描述

1.6 Bean的销毁阶段

  • @PreDestroy
  • 实现DisposableBean接口
  • 自定义方法@Bean(destroyMethod = “destroyFactory”)

原理与1.5 Bean的实例化时类似的,查看close()方法,
@PreDestroy到最后的实现依然是调用DisposableBean的destroy()方法;

1.7 SpringBean的GC

正常的注册Bean,调用System.gc()进行强制GC

public class BeanGarbageCollectionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext beanFactory = new AnnotationConfigApplicationContext();

        beanFactory.register(BeanInitializationDemo.class);

        beanFactory.refresh();

        beanFactory.close();

        System.gc();

    }
}

在Bean对象中覆写Object中的finalize()方法

@Override
protected void finalize() throws Throwable{
    System.out.println("当前 DefaultUserFactory 对象正在被回收。。。");
}

控制台:

当前 DefaultUserFactory 对象正在被回收。。。

如果在调用System.gc()后,没有显示"对象正在被回收。。。",这是正常的行为,需要在gc之前将线程睡眠大概5s左右;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Bean基础 的相关文章

随机推荐

  • Centos7 修改主机名

    简介 在CentOS中 有三种定义的主机名 静态的 static 瞬态的 transient 和灵活的 pretty 静态主机名也称为内核主机名 是系统在启动时从 etc hostname自动初始化的主机名 瞬态主机名是在系统运行时临时分配
  • Windows+Ubuntu双系统下卸载Ubuntu

    记录一下自己卸载Ubuntu的步骤 防止以后再卸载重新找教程 1 删除Ubuntu的分区 步骤1 打开 我的电脑 选择 管理 点击 磁盘管理 步骤2 确定Ubuntu系统所在的磁盘分区 我的是磁盘1的磁盘分区6 7 8 9 然后一一删除 选
  • python每日一题_Python每日一题 001

    Talk is Cheap show me the code Linus Torvalds 将你的 QQ 头像 或者微博头像 右上角加上红色的数字 类似于微信未读信息数量那种提示效果 类似于图中效果 环境准备 安装PIL模块 Windows
  • MySQL之KEY分区和LINEAR KEY分区

    KEY分区 KEY分区与HASH分区相似 当然有不同点 1 在HASH分区中 可以使用整数列或者基于列值的表达式 即PARTITION BY HASH expr 而在KEY分区中 直接基于列 PARTITION BY KEY column
  • css清除默认样式

    清除默认样式 初始化样式 清除内外边距 margin 0 padding 0 自减盒子模型 box sizing border box ul ol 清除默认圆点 list style none a 取消下划线 text decoration
  • 华南x79 主板说明书下载_主板说明书找不到 机箱连线照样秒安装

    点击上方 电脑爱好者关注我们 小伙伴们在安装 升级电脑的时候 在主板上安装各种配件应该问题不大 但是连线呢 特别是机箱上那些细碎的小接口们 即使照着说明书都要琢磨半天 如果是老主板 一下子找不到说明书怎么办 这里小编就跟大家说一些简单的办法
  • windows上 nginx 配置好了access.log路径后,但是访问日志并没有记录

    windows 上配置 nginx access log 日志 1 问题 2 解决方案 2 1 为当前文件夹配置权限 3 结果 1 问题 之前 本机上配置了 nginx access log 日志 但是 并没有 记录 当时因为太忙了 就没有
  • 【Mybatis源码分析】动态标签的底层原理,DynamicSqlSource源码分析

    DynamicSqlSource 源码分析 一 DynamicSqlSource 源码分析 DynamicContext源码分析 SqlNode源码分析 动态SQL标签 Mybatis 动态SQL标签 举例 调试 SqlNode源码分析 M
  • Ubuntu/Linux下安装DosBox配置汇编环境

    Ubuntu Linux下安装DosBox配置汇编环境 微信关注公众号 夜寒信息 致力于为每一位用户免费提供更优质技术帮助与资源供给 感谢支持 一 首先我们去DosBox官网下载DosBox 0 73 或者直接启用终端命令行输入以下代码 s
  • leveldb深度剖析-查询流程

    至此 将插入流程以及压缩流程都已介绍完毕了 本篇主要介绍查询流程 一 查询流程 首先来看一下查询接口具体实现内容 查询 param options 查询选项 param key 查询key param value 输出参数 如果找到则赋值给
  • TypeScript 基础教程,适合新手

    TypeScript 基本用法 本章介绍 TypeScript 的一些最基本的语法和用法 最全教程 https tut qzxdp cn typescript 在线工具 https tools qzxdp cn 类型声明 TypeScrip
  • 本地部署IIS服务及MQTT服务

    本地部署IIS服务及MQTT服务 概述 配置IIS 安装windows功能 配置应用程序 打开IIS服务 安装aspnetcore runtime 安装dotnet hosting 检查 添加网站 配置应用程序 配置IIS通过外部IP访问
  • YOLOv5 or YOLOv8 快速划分训练集 验证集 测试集

    该脚本实现了将原始数据集自动划分为yolo训练数据集排列形式 import os import random import shutil 该脚本实现了将原始数据集自动划分为yolo训练数据集排列形式 目录排序如下 old root data
  • 【mind+】机器人对话互动游戏编程

    目录 前言 不要多言 请看下面的代码 一 代码 1 机器人回答问题 2 机器人互动和状态改变 前言 应用mind 软件写一个机器人互动的程序 程序要求 1 提出问题 机器人做出相对应的回答 2 点击机器人 它做出随机语录回复 提前准备 添加
  • 【SSL_1232】雷达覆盖

    思路 以一个点作为平角 计算几何统计 c o d e code code include
  • 在虚拟机中win10启用远程桌面的方法

    1 打开虚拟机 选择此电脑 右键属性 2 选择远程桌面 开启服务 3 打开cmd 输入ipconfig 查看IP 4 在宿主机上按住win r快捷键 输入matsc 打开远程桌面连接 输入虚拟机的ip 5 输入虚拟机的用户名和密码 6 连接
  • 虚拟机网络桥接,详细操作步骤,本地连接虚拟机

    虚拟机网络桥接 文章目录 虚拟机网络桥接 一 首先查看主机连接网络的网关 二 打开虚拟机的worksation服务 三 修改主机的VMnet8的IPV4属性 四 修改虚拟机的workstation的虚拟网络 五 修改VMnet8的IP 网关
  • ATK&ck靶场系列二

    信息收集 nmap sP 192 168 111 0 24 nmap sS T4 A v p 192 168 111 80 nmap sS T4 A v p 192 168 111 80 Starting Nmap 7 93 https n
  • 录音时分离左右声道的数据

    平台录音默认为8通道数据 保存到文件中取左右声道数据 当mic 1时 取左声道数据 当mic 2时 取右声道数据 private byte splitStereoPcm byte data int monoLength data lengt
  • Spring Bean基础

    Spring Bean基础 1 1 定义Spring Bean 1 1 1 什么是BeanDefinition 1 2 通过BeanDefinition构建Bean 1 3 注册Bean BeanDefinition 注册 1 4 实例化S