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

0104BeanDefinition合并和BeanClass加载-Bean生命周期详解-spring

文章目录

    • 1 前言
    • 2 BeanDefinition合并
      • 2.1 BeanDefinition合并在做什么?
      • 2.2 BeanDefinition怎么合并
      • 2.3 示例演示
    • 3 Bean Class 加载
    • 后记

1 前言

下面要介绍的阶段,都是在调用getBean()从容器中获取bean对象的过程中发生的操作,我们需要更多的去跟进源码。

在以后的讲解中,我们都采用API配置bean的方式,通过BeanDefinitionBuilder来完成bean的配置。

2 BeanDefinition合并

BeanDefinition合并核心方法

org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition()

2.1 BeanDefinition合并在做什么?

不管我们在定义bean的时候,bean有没有父子关系。容器在注册初始BeanDefinition后,后续的相关操作都是在使用合并后的RootBeanDefinition。RootBeanDefinition会放入mergedBeanDefinitions中。

private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
  • mergedBeanDefinitions:AbstractBeanFactory类中存放beanName-RootBeanDefinition键值对的map

定义了bean当然是为了使用,我们通过追踪下getBean()方法,看看是不是如上面我们所描述的一样是使用RootBeanDefinition而不是初始BeanDefinition。

factory.getBean()是调用AbstractBeanFactory中的方法,

	@Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);}

继续调用doGetBean()方法,

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {// 如果是别名,转换为最初的beanNameString beanName = transformedBeanName(name);Object beanInstance;// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {// 我们第一次获取bean容器中不会有bean实例// 省略...}else {// 省略...StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate").tag("beanName", name);try {if (requiredType != null) {beanCreation.tag("beanType", requiredType::toString);}// 后续使用中使用的是合并后的RootBeanDefinitionRootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// 省略...
}

2.2 BeanDefinition怎么合并

我们通过源码来详细看看是如果合并的。在上面我们已经通过源码追踪到了执行合并的方法getMergedLocalBeanDefinition(),源码如下:

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {// Quick check on the concurrent map first, with minimal locking.RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);if (mbd != null && !mbd.stale) {// 第一次获取,mergedBeanDefinitions并不会有beanName对应的RootBeanDefinitionreturn mbd;}return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
  • getBeanDefinition(beanName)获取初始注册的BeanDefinition

继续调用getMergedBeanDefinition()

	protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)throws BeanDefinitionStoreException {return getMergedBeanDefinition(beanName, bd, null);}

继续调用getMergedBeanDefinition()即合并BeanDefinition的主要方法

protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)throws BeanDefinitionStoreException {synchronized (this.mergedBeanDefinitions) {RootBeanDefinition mbd = null;RootBeanDefinition previous = null;// Check with full lock now in order to enforce the same merged instance.if (containingBd == null) {mbd = this.mergedBeanDefinitions.get(beanName);}if (mbd == null || mbd.stale) {previous = mbd;if (bd.getParentName() == null) {// 父bean为空// Use copy of given root bean definition.if (bd instanceof RootBeanDefinition) {// 原始的BeanDefinition为RootBeanDefinition,直接克隆mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();}else {// 原始的BeanDefinition不是RootBeanDefinition,newmbd = new RootBeanDefinition(bd);}}else {// 父bean不为空,需要合并BeanDefinition pbd;try {String parentBeanName = transformedBeanName(bd.getParentName());if (!beanName.equals(parentBeanName)) {// 默认beanName唯一,获取map中parentBeanName对应的RootBeanDefinitionpbd = getMergedBeanDefinition(parentBeanName);}// 省略...// new,深拷贝mbd = new RootBeanDefinition(pbd);// 子bean初始BeanDefinition的相关信息覆盖掉继承自RootBeanDefinition的相同信息mbd.overrideFrom(bd);}// 省略...return mbd;}}

合并逻辑如下:

  • 我们定义的bean没有父子关系时,合并bean其实就是新生成RootBeanDefinition对象,内容拷贝自初始BeanDefinition,并放入mergedBeanDefinitions集合;如果bean就是定义的RootBeanDefinition,直接拷贝。

  • 我们定义bean的有父子bean关系时,此时子BeanDefinition中的信息是不完整的,比如设置属性的时候配置在父BeanDefinition中,此时子BeanDefinition中是没有这些信息的。需要将子bean的BeanDefinition和从mergedBeanDefinitions集合获取的父bean对应的RootBeanDefinition进行合并,得到最终的一个RootBeanDefinition,合并之后得到的RootBeanDefinition包含bean定义的所有信息,包含了从父bean中继继承过来的所有信息,后续bean的所有创建工作就是依靠合并之后RootBeanDefinition来进行的。

  • 当定义的bean有多级父子关系,重复上述步骤;合并当前bean的初始BeanDefinition和从mergedBeanDefinitions 映射中获取父beanName对应的RootBeanDefinition。

2.3 示例演示

因为是做演示测试,没有通过factory.getBean()去debug调试,而是直接调用getMergedBeanDefinition()方法。

// User
package com.gaogzhen.myspring.bean;/*** @author: Administrator* @version: 1.0* @createTime: 2023/02/20 07:49*/
public class User {private String name;public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +'}';}
}// 测试方法public void testMergeAPI() {DefaultListableBeanFactory factory = new DefaultListableBeanFactory();BeanDefinitionBuilder builder1 = BeanDefinitionBuilder.genericBeanDefinition(User.class).addPropertyValue("name", "张三").addPropertyValue("desc", "创立张氏制药厂");factory.registerBeanDefinition("user1", builder1.getBeanDefinition());BeanDefinitionBuilder builder2 = BeanDefinitionBuilder.genericBeanDefinition().addPropertyValue("name", "张小三").addPropertyValue("desc", "接管张氏制药厂").addPropertyValue("manage", "开设分厂").setParentName("user1");factory.registerBeanDefinition("user2", builder2.getBeanDefinition());BeanDefinitionBuilder builder3 = BeanDefinitionBuilder.genericBeanDefinition().addPropertyValue("name", "张小小三").addPropertyValue("operate", "成立张氏制药集团并上市").setParentName("user2");factory.registerBeanDefinition("user3", builder3.getBeanDefinition());//遍历容器中注册的所有bean信息for (String beanName : factory.getBeanDefinitionNames()) {//通过bean名称获取原始的注册的BeanDefinition信息BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);//获取合并之后的BeanDefinition信息BeanDefinition mergedBeanDefinition = factory.getMergedBeanDefinition(beanName);// 获取beanSystem.out.println(beanName);System.out.println("初始beanDefinition:" + beanDefinition);System.out.println("beanDefinition中的属性信息" + beanDefinition.getPropertyValues());System.out.println("-----------");System.out.println("合并之后mergedBeanDefinition:" + mergedBeanDefinition);System.out.println("mergedBeanDefinition中的属性信息" + mergedBeanDefinition.getPropertyValues());System.out.println("=================================");}}

打印输出:

user1
初始beanDefinition:Generic bean: class [com.gaogzhen.myspring.bean.User]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
beanDefinition中的属性信息PropertyValues: length=2; bean property 'name'; bean property 'desc'
-----------
合并之后mergedBeanDefinition:Root bean: class [com.gaogzhen.myspring.bean.User]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
mergedBeanDefinition中的属性信息PropertyValues: length=2; bean property 'name'; bean property 'desc'
=================================
user2
初始beanDefinition:Generic bean with parent 'user1': class [null]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
beanDefinition中的属性信息PropertyValues: length=3; bean property 'name'; bean property 'desc'; bean property 'manage'
-----------
合并之后mergedBeanDefinition:Root bean: class [com.gaogzhen.myspring.bean.User]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
mergedBeanDefinition中的属性信息PropertyValues: length=3; bean property 'name'; bean property 'desc'; bean property 'manage'
=================================
user3
初始beanDefinition:Generic bean with parent 'user2': class [null]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
beanDefinition中的属性信息PropertyValues: length=2; bean property 'name'; bean property 'operate'
-----------
合并之后mergedBeanDefinition:Root bean: class [com.gaogzhen.myspring.bean.User]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
mergedBeanDefinition中的属性信息PropertyValues: length=4; bean property 'name'; bean property 'desc'; bean property 'manage'; bean property 'operate'
=================================Process finished with exit code 0
  • 从输出的结果中可以看到,合并之前,BeanDefinition是不完整的,比比如user2和user3中的class是null,属性信息也不完整,但是合并之后这些信息都完整了。

bean生命周期的后续阶段使用的是合并后的RootBeanDefinition。没有父bean的直接new RootBeanDefinition,属性拷贝自该bean的初始BeanDefinition;有父bean的,深拷贝父bean对应的RootBeanDefinition,然后用该bean的初始BeanDefinition覆盖相同属性。

3 Bean Class 加载

这个阶段就是将bean的class名称转换为Class类型的对象。

BeanDefinition中有个Object类型的字段:beanClass

private volatile Object beanClass;

用来表示bean的class对象,通常这个字段的值有2种类型,一种是bean对应的Class类型的对象,另一种是bean对应的Class的类名(或者类路径),第一种情况不需要解析,第二种情况:即这个字段是bean的类名的时候,就需要通过类加载器将其转换为一个Class对象。

此时会对阶段4中合并产生的RootBeanDefinition中的beanClass进行解析,将bean的类名转换为Class对象,然后赋值给beanClass字段。

源码位置:

org.springframework.beans.factory.support.AbstractBeanFactory#resolveBeanClass()

在BeanDefinition合并阶段,我们追踪到了AbstracBeanFactory的doGetbean方法,完成BeanDefinition合并,我们继续追踪,看下在哪里完成beanClass的加载?具体加载怎么做的?

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {// 省略RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// 省略// Create bean instance.if (mbd.isSingleton()) {// 我们通常bean scope都是单例的sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});
// 省略...
}

getSingleton()方法获取bean的单例实例

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {// 省略...try {singletonObject = singletonFactory.getObject();// 省略
}

singletonObject初始为空,通过上一步传递的singletonFactory获取实例,即箭头函数里面return createBean(beanName, mbd, args),继续看下createBean()方法,继续调用AbstractAutowireCapableBeanFactory中的createBean()方法:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {RootBeanDefinition mbdToUse = mbd;// 这里解析beanClassClass<?> resolvedClass = resolveBeanClass(mbd, beanName);
// 省略..
}

继续调用AbstractBeanFactory中的resolveBeanClass()方法

@Nullable
protected Class<?> resolveBeanClass(RootBeanDefinition mbd, String beanName, Class<?>... typesToMatch)throws CannotLoadBeanClassException {try {if (mbd.hasBeanClass()) {return mbd.getBeanClass();}return doResolveBeanClass(mbd, typesToMatch);
// 省略异常处理
}public boolean hasBeanClass() {return (this.beanClass instanceof Class);}
  • 先判断RootBeanDefinition mbd中beanClass是否是Class类型,是直接返回
  • 不是说明是类路径字符串,调用doResolveBeanClass解析

查看doResolveBeanClass()方法

@Nullable
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)throws ClassNotFoundException {ClassLoader beanClassLoader = getBeanClassLoader();ClassLoader dynamicLoader = beanClassLoader;boolean freshResolve = false;// 省略...String className = mbd.getBeanClassName();if (className != null) {Object evaluated = evaluateBeanDefinitionString(className, mbd);if (!className.equals(evaluated)) {// 省略...}// Resolve regularly, caching the result in the BeanDefinition...return mbd.resolveBeanClass(beanClassLoader);
}
  • 如果指定了beanExpressionResolver,通过beanExpressionResolver解析,默认未指定

我们继续查找AbstracBeanDefinition 的resolveBeanClass方法

	@Nullablepublic Class<?> resolveBeanClass(@Nullable ClassLoader classLoader) throws ClassNotFoundException {String className = getBeanClassName();if (className == null) {return null;}Class<?> resolvedClass = ClassUtils.forName(className, classLoader);this.beanClass = resolvedClass;return resolvedClass;}

classUtils的forName()方法

public static Class<?> forName(String name, @Nullable ClassLoader classLoader)throws ClassNotFoundException, LinkageError {Assert.notNull(name, "Name must not be null");Class<?> clazz = resolvePrimitiveClassName(name);if (clazz == null) {clazz = commonClassCache.get(name);}if (clazz != null) {return clazz;}// 省略.. 非常规ClassLoader clToUse = classLoader;if (clToUse == null) {clToUse = getDefaultClassLoader();}try {return Class.forName(name, false, clToUse);}// 异常处理..
}
  • 通常我们配置的类路径形式:包名+类名字符串,通过Class.forName(name, false, clToUse)加载。

后记

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/spring6-study

参考:

[1]Spring系列之Bean生命周期详解[CP/OL].

相关文章:

0104BeanDefinition合并和BeanClass加载-Bean生命周期详解-spring

文章目录1 前言2 BeanDefinition合并2.1 BeanDefinition合并在做什么&#xff1f;2.2 BeanDefinition怎么合并2.3 示例演示3 Bean Class 加载后记1 前言 下面要介绍的阶段&#xff0c;都是在调用getBean()从容器中获取bean对象的过程中发生的操作&#xff0c;我们需要更多的去…...

Java集合进阶(三)

文章目录一、Map1. 概述2. 基本功能3. 遍历4. 遍历学生对象5. 集合嵌套6. 统计字符出现次数二、Collections1. 常用方法2. 学生对象排序三、模拟斗地主一、Map 1. 概述 Interface Map<K, V>&#xff1a;K 是键的类型&#xff0c;V 是值的类型。 将键映射到值的对象&…...

【网络】什么是RPC?RPC与HTTP有什么关系?

文章目录RPC是什么RPC和HTTP的关系和区别[附]关于REST论文中提到的"HTTP不是RPC"重点参考 凤凰架构-远程过程调用 既然有HTTP为什么还要有RPC&#xff1f; RPC是什么 RPC(Remote Procedure Call)&#xff1a;即远程过程调用&#xff0c;目的是为了让计算机能够跟调用…...

[手撕数据结构]栈的深入学习-java实现

CSDN的各位uu们你们好,今天千泽带来了栈的深入学习,我们会简单的用代码实现一下栈, 接下来让我们一起进入栈的神奇小世界吧!0.速览文章一、栈的定义1. 栈的概念2. 栈的图解二、栈的模拟实现三.栈的经典使用场景-逆波兰表达式总结一、栈的定义 1. 栈的概念 栈&#xff1a;一种…...

2.线性表的顺序表示

数据结构很重要&#xff01; 数据结构很重要&#xff01;&#xff01;&#xff01; 数据结构很重要&#xff01;&#xff01;&#xff01;&#xff01; 思考 1.线性表的顺序表示内容有哪些&#xff1f;&#xff08;What&#xff09; 2.为什么要学线性表的顺序表示? ? (Why)…...

eps文件删除了能恢复吗?恢复误删eps文件的三种方法

eps文件格式专为矢量图像和图形而设计。虽然没有被广泛使用&#xff0c;但它仍然受到各种插画家和平面设计师的钟爱。eps文件十分适合创建徽标和商标设计&#xff0c;主要应用见于广告牌、海报和横幅。可是在使用设备过程中&#xff0c;难免会遇到数据丢失问题&#xff0c;如果…...

【C++】运算符重载练习——Date 类

文章目录&#x1f449;日期类介绍&#x1f448;&#x1f449;日期类实现&#x1f448;&#x1f4d5; 成员变量&#x1f4d5; 构造函数&#x1f4d5; 对应月份天数&#x1f4d5; 赋值重载&#x1f4d5; 比较运算符重载&#x1f4d5; 计算 运算符重载&#x1f449;源代码&#x1…...

Redis学习(13)之Lua脚本【环境准备】

文章目录一 Lua入门环境准备1.1 Lua简介1.2 Linux 系统安装Lua1.2.1 Lua 下载1.2.2 Lua 安装1.3 Hello World1.3.1 命令行模式1.3.2 脚本文件模式1.3.3 两种脚本运行方式1.4 Win安装Lua1.4.1 LuaForWindows的安装1.4.2 SciTE修改字体大小1.4.3 SciTE中文乱码1.4.4 SciTE快捷键工…...

关于BLE的一些知识总结

数据包长度对于BLE4.0/4.1来说&#xff0c;一个数据包的有效载荷最大为20字节对于BLE4.2以上&#xff0c;数据包的有效载荷扩大为251字节传输速率在不考虑跳频间隔的情况下&#xff0c;最大传输速率为&#xff1a;1&#xff09;BLE4.0/4.1的理论吞吐率为39kb/s&#xff1b;2&am…...

Spring框架源码分析一

如何看源码&#xff08;方法论&#xff09;不要忽略源码中的注释使用翻译工具先梳理脉络&#xff0c;然后梳理细节即总分总&#xff0c;先总体过一遍&#xff0c;再看细节&#xff0c;再做一个总结大胆猜测&#xff08;8分靠猜&#xff09;&#xff0c;小心验证&#xff0c;再调…...

CSS常用内容总结(扫盲)

文章目录前言相关概念【了解】脚本语言什么是脚本语言脚本语言有什么特点常见的脚本语言什么是动态语言&#xff0c;什么是静态语言动态语言和静态语言两者之间有何区别CSSCSS是什么CSS的特点一、CSS代码怎么写基本语法规则引入方式内部样式内联样式表外部样式代码风格二、CSS的…...

Java启蒙之语言基础

目录 一.Java标识符和关键字 1.1Java标识符 1.2Java关键字 二.数据类型和变量的概述和关系 2.1Java变量 2.2Java的数据类型 2.2.1数据类型的分类的概述 2.2.2数据类型的转换 3.Java运算符 总结 &#x1f63d;个人主页&#xff1a;tq02的博客_CSDN博客-领域博主 &#…...

数据库系统--T-SQL数据查询功能-多表查询(超详细/设计/实验/作业/练习)

目录课程名&#xff1a;数据库系统内容/作用&#xff1a;设计/实验/作业/练习学习&#xff1a;T-SQL数据查询功能-多表查询一、前言二、环境与设备三、内容四、内容练习题目&#xff1a;对应题目答案&#xff1a;五、总结课程名&#xff1a;数据库系统 内容/作用&#xff1a;设…...

Spring Boot 3.0系列【14】核心特性篇之Configuration相关注解汇总介绍

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot版本3.0.3 源码地址:https://gitee.com/pearl-organization/study-spring-boot3 文章目录 前言@Configuration@ConfigurationProperties@EnableConfigurationProperties@ConfigurationPropertiesScan@Configuratio…...

[ubuntu][jetson]给jetson增加swap空间类似于给windows加虚拟内存

具体操作如下&#xff1a; #打开性能模式 sudo nvpmodel -m 0 && sudo jetson_clocks #增加swap空间&#xff0c;防止爆内存 swapoff -a sudo fallocate -l 15G /swapfile sudo chmod 600 /var/swapfile sudo mkswap /swapfile sudo swapon /swapfile…...

小黑子—Java从入门到入土过程:第二章

Java零基础入门2.0Java系列第二章1. 注释和关键字2. 字面量3. 变量3.1 基本用法3.2 使用方式3.3 注意事项4. 变量练习5. 计算机中的数据存储5.1 计算机的存储规则5.2 进制5.3 进制间转换二进制转十八进制转十十六进制转十十进制转其他进制6. 数据类型7. 定义变量的练习8. 标识符…...

ElasticSearch搜索详细讲解与操作

全文检索基础 全文检索流程 流程&#xff1a; #mermaid-svg-7Eg2qFEl06PIEAxZ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-7Eg2qFEl06PIEAxZ .error-icon{fill:#552222;}#mermaid-svg-7Eg2qFEl06PIEAxZ .error…...

web实现太极八卦图、旋转动画、定位、角度、坐标、html、css、JavaScript、animation

文章目录前言1、html部分2、css部分3、JavaScript部分4、微信小程序演示前言 哈哈 1、html部分 <div class"great_ultimate_eight_diagrams_box"><div class"eight_diagrams_box"><div class"eight_diagrams"><div class&…...

【LeetCode】33. 搜索旋转排序数组、1290. 二进制链表转整数

作者&#xff1a;小卢 专栏&#xff1a;《Leetcode》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 33. 搜索旋转排序数组 1290. 二进制链表转整数 33. 搜索旋转排序数组 33. 搜索旋转排序…...

IBM Semeru Windows 下的安装 JDK 17

要搞清楚下载那个版本&#xff0c;请参考文章&#xff1a;来聊聊 OpenJDK 和 JVM 虚拟机下载地址semeru 有认证版和非认证版&#xff0c;主要是因为和 OpenJ9 的关系和操作系统的关系而使用不同的许可证罢了&#xff0c;本质代码是一样的。在 Windows 下没有认证版&#xff0c;…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...