《系列二》-- 9、bean属性填充

系列,bean,属性,填充 · 浏览次数 : 7

小编点评

# Property Value Generation Property Value generation involves creating a new PropertyValue object for each property in the deep copy. **Properties:** * `deepCopy.getDeclaredProperties()` : This retrieves all the properties in the deep copy as an array of PropertyValue objects. * `deepCopy.getPropertyValue(property)`: This retrieves the property value for each property in the deep copy as a PropertyValue object. * `deepCopy.setConvertedValue(propertyValue)` : This sets the converted property value for each property in the deep copy as a PropertyValue object. **Generating Property Values:** * `PropertyValue.getDeclaredProperty(property)` : This retrieves the PropertyValue object for each property in the deep copy. * `PropertyValue.getProperty(property)` : This retrieves the PropertyValue object for each property in the deep copy. * `PropertyValue.setProperty(property, value)` : This sets the property value for each property in the deep copy as a PropertyValue object. **Setting Deep Copy Property Values:** * `deepCopy.setConvertedValue(propertyValue)` : This sets the converted property value for each property in the deep copy as a PropertyValue object. * `deepCopy.setPropertyValue(property, value)` : This sets the property value for each property in the deep copy as a PropertyValue object. **Creating Property Value Objects:** * `PropertyValue.new(property)` : This creates a new PropertyValue object for each property in the deep copy. * `PropertyValue.from(deepCopy.getDeclaredProperty(property))` : This creates a PropertyValue object for each property in the deep copy from a PropertyValue object. **Examples:** * `PropertyValue value = new PropertyValue(deepCopy.getDeclaredProperty(property), value);` * `deepCopy.setPropertyValue(property, value);` * `PropertyValue propertyValue = PropertyValue.from(deepCopy.getDeclaredProperty(property));` **Note:** The property value generation process involves creating new PropertyValue objects for each property in the deep copy. This ensures that the generated property values are correctly set for each property in the deep copy.

正文

阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。

写在开始前的话:

阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了:

  • beans
  • core
  • context

实际上我在博客里贴出来的还只是一部分内容,更多的内容,我放在了个人,fork自 spring 官方源码仓了; 而且对源码的学习,必须是要跟着实际代码层层递进的,不然只是干巴巴的文字味同嚼蜡。

https://gitee.com/bokerr/spring-framework-5.0.x-study

这个仓设置的公共仓,可以直接拉取。



Spring源码阅读系列--全局目录.md



一、概述: populateBean 在什么时候执行?

在getBean 的调用链路中,如果是从零开始创建一个bean, 按照潦草的说法大致如下:

  • 1.根据 beanName 从 beanFactory 读取 BeanDefinition

  • 2.对BeanDefinition 进行简单的加工、转化 -> RootBeanDefinition

  • 2.根据bean元数据(BeanDefinition) 中记录的信息,递归的去加载该bean 依赖的其它bean

  • 3.根据bean 的作用域: [prototype、singleton...等等] 决定具体的创建bean的行为。

    • singleton 需要应用三级缓存,保证全局唯一
    • prototype 每次直接创建全新bean
  • 4.bean的实例化: (所谓实例化,可以理解为,从java 堆上创建了一个'全新'的对象。)
    根据 BeanDefinition 获取: bean的类型、参数、自定义构造函数、 注入方式(直接注入: 无参构造函数,间接注入: FactoryBean、factory-bean + factory-method)。

    最终根据上述的信息的配置情况,实例化一个bean。

  • 5.上一步也提到,它只完成了实例化,但是如果有属性需要填充呢? 参考如下的例子:


<beans>
  <bean id="myBean" class="demo.self.MySimpleBean">
    <!-- property 是常量 -->
    <property name="name" value="Bokerr"/>
    <property name="age" value="27"/>
  </bean>

  <bean id="myBean" class="demo.self.MyBean">
    <!-- property 是一个bean -->
    <property name="bean1"><ref bean="populate1"/></property>
  </bean>
  <bean id="populate1" class="demo.self.populateBean"/>
</beans>

为什么说填充的就是 property 属性呢? 【详见后文,populateBean方法的第一个判断。】

bean 创建实例化之后,还处于一个 '空白' 状态,所以需要经过填充操作,将配置的 property 属性的值注入。

这就引入了本文需要讲述的操作:populateBean

这里有个很重要、很重要、很重要的起手式:

PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

你需要记住: PropertyValues(它是 bean的全部 property 解析后得到的元数据信息)
它是来自于 mbd 的。(也就是 BeanDefinition),它的对象声明周期受到 BeanFactory 管理;

下文中任意对 PropertyValues 及其属性的 set 操作,都可等效认为,实在对 BeanDefinition 作缓存操作。

二、populateBean 的重要操作

/**
 * Populate the bean instance in the given BeanWrapper with the property values
 * from the bean definition.
 *
 * @param beanName the name of the bean
 * @param mbd      the bean definition for the bean
 * @param bw       the BeanWrapper with bean instance
 */
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    if (bw == null) {
        if (mbd.hasPropertyValues()) {
        //  bean 有属性需要被填充,但是对象包装器 bw 为空
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
        } else {
            //  包装器为空,且不需要 填充任何属性
            // Skip property population phase for null instance.
            return;
        }
    }

    // 给 InstantiationAwareBeanPostProcessors  最后一次机会,通过属性注入改变 bean.
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                // 所有 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()的返回值将决定
                // [是/否] 继续进行property属性填充
                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    return;
                }
            }
        }
    }

    // 所有需要注入的属性, 对应的bean缓存 【这里明确从 BeanDefinition 上获取的 property 的定义】
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        // Add property values based on autowire by name if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
            // {重要操作1-根据Bean名称注入}   [下文将展开详说]
            autowireByName(beanName, mbd, bw, newPvs);  //  提取依赖的bean  保存到 pvs
        }
        // Add property values based on autowire by type if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            // {重要操作1-根据Bean类型注入 <Class>}  [下文将展开详说]
            autowireByType(beanName, mbd, bw, newPvs);  //  提取依赖的bean  缓存到 pvs
        }
        pvs = newPvs;
    }

    // beanFactory 是否已经注册了: bean 实例化相关的 "后置处理器"
    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    // 是否需要依赖检查
    boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

    if (hasInstAwareBpps || needsDepCheck) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        // 过滤属性描述符,为依赖检查做准备
        PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        if (hasInstAwareBpps) {//  后处理器 ?? 
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    // 主要这里是相同的后置处理器,不同的方法
                    // 字段填充的前置检查
                    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    if (pvs == null) {
                        return;
                    }
                }
            }
        }
        if (needsDepCheck) {//  依赖检查, depend-on 属性,spring-3.0弃用
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }
    }

    if (pvs != null) {
        // 属性填充  {重要操作2} [下文将展开详说]
        applyPropertyValues(beanName, mbd, bw, pvs);  //  将  propertyValue 应用/注入 到 bean 中
    }
}

这里重点说两个操作:

  • 1.属性值的注入, 将propertyName视作beanName, 并尝试从spring容器中获取该bean, 如果成功获取则通过 PropertyValues:pvs 保存

autowireByName(beanName, mbd, bw, newPvs);
autowireByType(beanName, mbd, bw, newPvs);

  • 2.属性值的应用, 上述操作的后续只要存在 property 属性配置就进行 property 值的'应用'操作。

applyPropertyValues(beanName, mbd, bw, pvs);

三、重点操作一 propertyValue 的注入

3.1 根据 Bean名称注入

    /**
	 * Fill in any missing property values with references to
	 * other beans in this factory if autowire is set to "byName".
	 *
	 * @param beanName the name of the bean we're wiring up.
	 *                 Useful for debugging messages; not used functionally.
	 * @param mbd      bean definition to update through autowiring
	 * @param bw       the BeanWrapper from which we can obtain information about the bean
	 * @param pvs      the PropertyValues to register wired objects with
	 */
	protected void autowireByName(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

        // 从 BeanDefinition 读取,非-常量注入的属性,这里返回的都是 property 指向别的 bean的场景
        // 这里会忽略: 基本类型、字符串常量等等 property
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			if (containsBean(propertyName)) {
				// 递归加载依赖的bean
				Object bean = getBean(propertyName);
				pvs.add(propertyName, bean);
				// 在容器中记录 bean 及其依赖的 bean之间的关系, 不妨进去瞅瞅
				registerDependentBean(propertyName, beanName);
				if (logger.isDebugEnabled()) {
					logger.debug("Added autowiring by name from bean name '" + beanName +
							"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
				}
			} else {
				// 根据beanName 未定位到bean, 记录到日志。[说明该 property 所依赖的可能并不是一个 bean,可能只是一个普通的常量。]
				if (logger.isTraceEnabled()) {
					logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
							"' by name: no matching bean found");
				}
			}
		}
	}

上文的内容很简单:

  • 获取所有的property中的非 【基本类型、常量】形式的 property
  • 然后遍历并逐个解析
  • 应用 containsBean 方法,判断该 propertyName 是否是一个beanName,如果不是则说明property 可能是常量。
  • registerDependentBean(propertyName, beanName); 见名知意,就是记录当前加载的bean,依赖 propertyName 指向的bean。

3.2 浅看一下,获取非'简单' 类型 property 的方法

    /**
	 * Return an array of non-simple bean properties that are unsatisfied.
	 * These are probably unsatisfied references to other beans in the
	 * factory. Does not include simple properties like primitives or Strings.
	 *
	 * @param mbd the merged bean definition the bean was created with
	 * @param bw  the BeanWrapper the bean was created with
	 * @return an array of bean property names
	 * @see org.springframework.beans.BeanUtils#isSimpleProperty
	 */
	protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
		Set<String> result = new TreeSet<>();
		PropertyValues pvs = mbd.getPropertyValues();
		PropertyDescriptor[] pds = bw.getPropertyDescriptors();
		for (PropertyDescriptor pd : pds) {
			// 有 setter方法
			// 		&& 没有别识别过的依赖
			// 			&& 已经解析过的property字段中不包含该字段
			// 				&& property的类型不是简单类型(Date、Number、CharSequence、Enum等等)
			if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
					!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
				result.add(pd.getName());
			}
		}
		return StringUtils.toStringArray(result);
	}

3.3 根据 Bean类型注入

    /**
	 * Abstract method defining "autowire by type" (bean properties by type) behavior.
	 * <p>This is like PicoContainer default, in which there must be exactly one bean
	 * of the property type in the bean factory. This makes bean factories simple to
	 * configure for small namespaces, but doesn't work as well as standard Spring
	 * behavior for bigger applications.
	 *
	 * @param beanName the name of the bean to autowire by type
	 * @param mbd      the merged bean definition to update through autowiring
	 * @param bw       the BeanWrapper from which we can obtain information about the bean
	 * @param pvs      the PropertyValues to register wired objects with
	 */
	protected void autowireByType(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

		TypeConverter converter = getCustomTypeConverter();  //  获取自定义类型转换器
		if (converter == null) {
			converter = bw;
		}
		Set<String> autowiredBeanNames = new LinkedHashSet<>(4);  //  缓存依赖关系映射
		// 从 BeanDefinition 读取,非-常量注入的属性,这里返回的都是 property 指向别的 bean的场景
		// 这里会忽略: 基本类型、字符串常量等等 property
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			try {
				// 我们这是根据 bean Type 注入,所以这里浅浅的获取了一下, bean的类对象反射信息
				PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
				// Don't try autowiring by type for type Object: never makes sense,
				// even if it technically is a unsatisfied, non-simple property.
				if (Object.class != pd.getPropertyType()) {
					MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);  //  探测属性指定的set 方法 
					// Do not allow eager init for type matching in case of a prioritized post-processor.
					boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);

					//  依赖描述符
					DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); 

					// 解析指定 beanName 的属性所匹配的值,
					// 并把解析到的属性名称存储在 autowiredBeanNames 中
					// 当存在多个属性被封装到一起时:
					// @Autowired List<A> aList;  会把 所有 A 类型的bean 都注入其中 autowiredBeans (复数个)
					Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);  //  寻找类型匹配的  bean  
					if (autowiredArgument != null) {
						pvs.add(propertyName, autowiredArgument);
					}
					for (String autowiredBeanName : autowiredBeanNames) {
						registerDependentBean(autowiredBeanName, beanName);  //  注册依赖关系
						if (logger.isDebugEnabled()) {
							logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
									propertyName + "' to bean named '" + autowiredBeanName + "'");
						}
					}
					autowiredBeanNames.clear();
				}
			} catch (BeansException ex) {
				throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
			}
		}
	}

这里是根据 property 的类型来注入的,比起根据 propertyName 注入, 这里的操作复杂得多

  • 获取所有的property中的非 【基本类型、常量】形式的 property
  • 依次遍历
  • 结合 BeanDefinition 中的 property 定义,以及包装器BeanWrapper 中的记录信息, 得到 property的类型
  • 根据 bean类型描述进入方法: resolveDependency() 及其后文其实内容是相当丰富的,这里就不再展开了;
    该方式看似没有返回值,实际上你看下 desc 的构造,你就能发现有什么东西偷偷摸摸的进去了。

如果想要深究 resolveDependency() 方法,可以下载我个人fork 的spring源码仓; 我在源码里也加了详细注释。

https://gitee.com/bokerr/spring-framework-5.0.x-study

四、注入依赖的应用

书接上回 现在我们看第二个重点操作:

  • populateBean().applyPropertyValues()

    /**
	 * Apply the given property values, resolving any runtime references
	 * to other beans in this bean factory. Must use deep copy, so we
	 * don't permanently modify this property.
	 *
	 * @param beanName the bean name passed for better exception information
	 * @param mbd      the merged bean definition
	 * @param bw       the BeanWrapper wrapping the target object
	 * @param pvs      the new property values
	 */
	protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
		if (pvs.isEmpty()) {
			return;  //  不需要任何的注入操作
		}

		if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
			// 权限处理
			((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
		}

		MutablePropertyValues mpvs = null;
		List<PropertyValue> original;

		if (pvs instanceof MutablePropertyValues) {
			// 实际上,如果执行过: autowireByType 和 autowireByName 后注入的 <property, propertyValue>
			mpvs = (MutablePropertyValues) pvs;
			if (mpvs.isConverted()) {
				// 根据 name / type 解析到的 bean 已经被 转化为 具体类型 那么直接进行注入操作
				// Shortcut: use the pre-converted values as-is.
				try {
					// 将解析完成的 <property,propertyValue> 逐个应用到包装器: BeanWrapper
					bw.setPropertyValues(mpvs);
					return;
				} catch (BeansException ex) {
					throw new BeanCreationException(
							mbd.getResourceDescription(), beanName, "Error setting property values", ex);
				}
			}
			original = mpvs.getPropertyValueList();
		} else {
			// 同样 applyPropertyValues 也可能在别的场景中被调用:
			// 该场景中,PropertyValues 尚未进行任意的 property 的注入操作
			// 所以进入当前分支,继续往下分析。
			original = Arrays.asList(pvs.getPropertyValues());
		}

		//  获取自定义-转换器 其实这个应该并不陌生,在 populateBean().autowireByType() 方法中见过
		// 其实到这里,可以简单做个推论了: 接下来的篇幅中,所作的事情,可能会跟 property 的注入操作类似
		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;  //  默认使用 bean 包装器 
		}
		// Spring表达式语言(SpEL) 解析器  #{bean.xxx} ${xx.key}
		// 可由 ApplicationContext 注入
		BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

		// Create a deep copy, resolving any references for values.
		List<PropertyValue> deepCopy = new ArrayList<>(original.size());
		boolean resolveNecessary = false;
		for (PropertyValue pv : original) {
			//  遍历 property 属性,将需要转换的属性,转换为对应的类型
			if (pv.isConverted()) {
				// 已经转化过,直接添加 [BeanDefinition 缓存应用 或者 前边的环节已经处理过]
				deepCopy.add(pv);
			} else {
				// 先转换 后添加
				String propertyName = pv.getName();
				// 原始值
				Object originalValue = pv.getValue();
				// 包含SpEL表达式的解析
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;

				// property 是否可覆盖?
				// 根据包装器 bw 判断 property 字段可写 && 并且 property 不是(索引/嵌套类型的属性)。
				// 自引入概念:property 应该是一个 "原子属性"
				boolean convertible = bw.isWritableProperty(propertyName) &&
						!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
				if (convertible) {
					// 可以写入,且是一个"原子属性", 那么就可以进行转化了
					// 这里不太适合再钻进去了,除非spring源码出bug了,否则如下的逻辑是不太有可能接触到的,
					// 可以简单做个盖棺定论: 这里会把 resolvedValue 转化为 property 字段所期望的值类型:
					// 		String: 2023-07-10   ->  Date: 2023-07-10 00:00:00.000
					// 		String: false        ->  Boolean: false
					convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
				}

				// Possibly store converted value in merged bean definition,
				// in order to avoid re-conversion for every created bean instance.
				if (resolvedValue == originalValue) {
					// 转换后的值(对象)  等价  原始值
					if (convertible) {
						// PropertyValue 是元数据 BeanDefinition 的一环,这里等价为缓存
						// 避免重复的转换操作
						pv.setConvertedValue(convertedValue);
					}
					deepCopy.add(pv);
				} else if (convertible && originalValue instanceof TypedStringValue &&
						!((TypedStringValue) originalValue).isDynamic() &&
						!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
					// 转化后的值不等于原始值
					// 		&& 支持转换
					// 		&& 原始值是String
					// 		&& 原始值不是动态的(常量)
					// 		&&  转换后的值,不是集合、不是数组
					pv.setConvertedValue(convertedValue);  // BeanDefinition 设置缓存
					deepCopy.add(pv);
				} else {
					// 上述情形之外的情况, 可以看到,区别是,这里没有被当作缓存设回 BeanDefinition 中
					resolveNecessary = true;
					deepCopy.add(new PropertyValue(pv, convertedValue));
				}
			}
		}
		if (mpvs != null && !resolveNecessary) {
			// mpvs 同样是 BeanDefinition 所包含的信息,这里也可视作缓存设置
			mpvs.setConverted();
		}

		// Set our (possibly massaged) deep copy.
		try {
			// 将解析完成的 <property,propertyValue> 逐个应用到包装器: BeanWrapper
			bw.setPropertyValues(new MutablePropertyValues(deepCopy));
		} catch (BeansException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Error setting property values", ex);
		}
	}

重要信息都放在注释里了。

与《系列二》-- 9、bean属性填充相似的内容:

《系列二》-- 9、bean属性填充

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了

《系列二》-- 3、FactoryBean 的使用

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了

深度解读《深度探索C++对象模型》之C++虚函数实现分析(二)

本系列深入分析编译器对于C++虚函数的底层实现,最后分析C++在多态的情况下的性能是否有受影响,多态究竟有多大的性能损失。

[Linux] 安装JDK

一、将linux的jdk安装包上传到linux系统中 二、解压后放到安装路径下 tar -zxvf jdk-8u121-linux-x64.tar 三、配置JDK环境变量 1、vim /etc/profile 2、按 i 进入编辑状态 3、添加环境变量 #java environment expor

[转帖]containerd命令操作

一、版本信息 内核版本:5.19.12-1.el7.elrepo.x86_64 系统版本:CentOS Linux release 7.9.2009 二、下载文件: wget https://github.com/opencontainers/runc/releases/download/v1.1.

MySQL基础9-事务基础

一、事务简介 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有操作作为一个整体一起向系统提交或者撤销操作请求,即这些操作要么同时成功,要么同时失败。mysql的事务默认是自动提交的,也就是说,当执行一条DML语句,Mysql会立即隐式的提交事务 二、事务操作 2.1 查看事务提交方式

Cilium系列-9-主机路由切换为基于 BPF 的模式

## 系列文章 * [Cilium 系列文章](https://ewhisper.cn/tags/Cilium/) ## 前言 将 Kubernetes 的 CNI 从其他组件切换为 Cilium, 已经可以有效地提升网络的性能. 但是通过对 Cilium 不同模式的切换/功能的启用, 可以进一步提

Redis系列9:Geo 类型赋能亿级地图位置计算

Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) Redis系列5:深入分析Cluster 集群模式 追求性能极致:Redis6.0的多线程模型 追求性能极致:客户端缓

MQ系列9:高可用架构分析

MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系列6:消息的消费 MQ系列7:消息通信,追求极致性能 MQ系列8:数据存储,消息队列的高可用保障 1

< Python全景系列-9 > Python 装饰器:优雅地增强你的函数和类

装饰器在 Python 中扮演了重要的角色,这是一种精巧的语言特性,让我们能够修改或增强函数和类的行为,无需修改它们的源代码。这篇文章将深入探讨装饰器的所有相关主题,包括装饰器的基础知识、实现与使用、工作原理,以及通过实际例子学习装饰器的独特用法。