• 【源码】SpringBoot事务注册原理


    Spring事务

    1、【源码】SpringBoot事务注册原理

    2、【源码】Spring Data JPA原理解析之事务注册原理

    3、【源码】Spring Data JPA原理解析之事务执行原理

    4、【源码】SpringBoot编程式事务使用及执行原理

    5、【源码】Spring事务之传播特性的详解

    6、【源码】Spring事务之事务失效及原理

    前言

    对于数据库的操作,可能存在脏读、不可重复读、幻读等问题,从而引入了事务的概念。

    事务

    1.1 事务的定义

    事务是指在数据库管理系统中,一系列紧密相关的操作序列,这些操作作为一个单一的工作单元执行。事务的特点是要么全部成功,要么全部失败,不会出现部分完成的情况。如果事务中的任何一个操作失败,那么整个事务都会被回滚到开始之前的状态,以确保数据库的一致性和完整性。

    1.2 事务的特性

    事务具有4个特性:原子性、一致性、隔离性和持久性。通常称为ACID。

    1)原子性(Atomicity)

    事务是一个不可分割的工作单位,事务中包含的所有操作要么全部成功,要么全部失败。

    2)隔离性(Isolation)

    一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的。

    3)持久性(Durability)

    一旦事务提交成功,则其所做的修改就会永久保存在数据库中,即使发生系统故障也不会丢失。

    4)一致性(Consistency)

    事务必须确保数据库从一个一致性状态转变到另一个一致性状态。

    更准确的说,事务是通过原子性、隔离性和持久性,实现了事务的一致性。另外,一致性还需要额外的工作来保证,如转账,从A转给B,A的资金减少了,B的资金增加了。转账要么成功、要么失败,但不管成功还是失败,事务的一致性要求两人的资金之和必须一致,要么成功,A减少了、B增加了,要么失败,A和B的资金都不变。为了一致性,需要程序来开启事务,同时修改A和B的资金,从而才能保证一致性。

    SpringBoot自动装载

    在SpringBoot框架中,会自动引入TransactionAutoConfiguration。且在META-INF的spring-autoconfigure-metadata.properties有如下配置:

    1. org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration=
    2. org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration=
    3. org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration.ConditionalOnBean=org.springframework.transaction.TransactionManager

    说明在自动引入TransactionAutoConfiguration时,必须存在TransactionManager的bean,且要先引入内部类EnableTransactionManagementConfiguration。

    EnableTransactionManagementConfiguration

    内部类EnableTransactionManagementConfiguration的源码如下:

    1. public class TransactionAutoConfiguration {
    2. @Configuration(proxyBeanMethods = false)
    3. @ConditionalOnBean(TransactionManager.class)
    4. @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
    5. public static class EnableTransactionManagementConfiguration {
    6. @Configuration(proxyBeanMethods = false)
    7. @EnableTransactionManagement(proxyTargetClass = false)
    8. @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
    9. public static class JdkDynamicAutoProxyConfiguration {
    10. }
    11. @Configuration(proxyBeanMethods = false)
    12. @EnableTransactionManagement(proxyTargetClass = true)
    13. @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
    14. matchIfMissing = true)
    15. public static class CglibAutoProxyConfiguration {
    16. }
    17. }
    18. }

    EnableTransactionManagementConfiguration内中会自动引入JdkDynamicAutoProxyConfiguration和CglibAutoProxyConfiguration,且添加了@EnableTransactionManagement注解。该注解会自动引入TransactionManagementConfigurationSelector。TransactionManagementConfigurationSelector的父类实现了ImportSelector接口,该类的selectImports()方法返回AutoProxyRegistrar和ProxyTransactionManagementConfiguration类名数组,即AutoProxyRegistrar和ProxyTransactionManagementConfiguration会自动注入到Spring IOC容器中。

    Spring的配置类解析的时候,会执行ConfigurationClassParser.processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection importCandidates, boolean checkForCircularImports),在该方法中,判断importCandidates是否继承于ImportSelector接口,如果是,调用ImportSelector的selectImports()方法,获取需要额外自动加入容器的类。

    ProxyTransactionManagementConfiguration

    ProxyTransactionManagementConfiguration的源码如下:

    1. package org.springframework.transaction.annotation;
    2. /**
    3. * Transaction事务管理代理的配置类
    4. */
    5. @Configuration
    6. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    7. public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    8. /**
    9. * 返回一个BeanFactoryTransactionAttributeSourceAdvisor【继承AbstractBeanFactoryPointcutAdvisor,重
    10. * 写了getPointcut()方法。即当前Advisor包含了事务需要的advice以及pointcut】
    11. * @return
    12. */
    13. @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    14. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    15. public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
    16. // 声明一个BeanFactoryTransactionAttributeSourceAdvisor对象,
    17. // 传入的transactionAttributeSource为AnnotationTransactionAttributeSource
    18. BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    19. advisor.setTransactionAttributeSource(transactionAttributeSource());
    20. advisor.setAdvice(transactionInterceptor());
    21. if (this.enableTx != null) {
    22. advisor.setOrder(this.enableTx.getNumber("order"));
    23. }
    24. return advisor;
    25. }
    26. /**
    27. * 创建一个AnnotationTransactionAttributeSource对象,该继承AbstractFallbackTransactionAttributeSource,
    28. * 对外提供了getTransactionAttribute()方法。该方法返回某个方法的事务注解信息
    29. */
    30. @Bean
    31. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    32. public TransactionAttributeSource transactionAttributeSource() {
    33. return new AnnotationTransactionAttributeSource();
    34. }
    35. /**
    36. * 创建一个TransactionInterceptor对象
    37. * @return
    38. */
    39. @Bean
    40. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    41. public TransactionInterceptor transactionInterceptor() {
    42. TransactionInterceptor interceptor = new TransactionInterceptor();
    43. interceptor.setTransactionAttributeSource(transactionAttributeSource());
    44. if (this.txManager != null) {
    45. interceptor.setTransactionManager(this.txManager);
    46. }
    47. return interceptor;
    48. }
    49. }

    在ProxyTransactionManagementConfiguration中,自动注入了BeanFactoryTransactionAttributeSourceAdvisor、AnnotationTransactionAttributeSource和TransactionInterceptor。添加了事务处理的advisor,该advisor生效时,会执行TransactionInterceptor拦截器。

    BeanFactoryTransactionAttributeSourceAdvisor

    BeanFactoryTransactionAttributeSourceAdvisor的源码如下:

    1. package org.springframework.transaction.interceptor;
    2. /**
    3. * 继承AbstractBeanFactoryPointcutAdvisor,重写了getPointcut()方法。即当前Advisor包含了事务需要的advice以及pointcut
    4. * 1、维护TransactionAttributeSource对象;
    5. * 2、创建一个TransactionAttributeSourcePointcut,重写getTransactionAttributeSource(),返回TransactionAttributeSource对
    6. * 象给TransactionAttributeSourcePointcut,进行方法事务注解判断
    7. */
    8. @SuppressWarnings("serial")
    9. public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
    10. // 在ProxyTransactionManagementConfiguration类中的transactionAdvisor()方法,
    11. // 设置transactionAttributeSource为AnnotationTransactionAttributeSource
    12. @Nullable
    13. private TransactionAttributeSource transactionAttributeSource;
    14. private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
    15. @Override
    16. @Nullable
    17. protected TransactionAttributeSource getTransactionAttributeSource() {
    18. return transactionAttributeSource;
    19. }
    20. };
    21. public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
    22. this.transactionAttributeSource = transactionAttributeSource;
    23. }
    24. public void setClassFilter(ClassFilter classFilter) {
    25. this.pointcut.setClassFilter(classFilter);
    26. }
    27. @Override
    28. public Pointcut getPointcut() {
    29. return this.pointcut;
    30. }
    31. }

    在BeanFactoryTransactionAttributeSourceAdvisor切面中,切点为自定义的实现抽象类TransactionAttributeSourcePointcut的对象,抽象方法getTransactionAttributeSource()返回的TransactionAttributeSource对象为ProxyTransactionManagementConfiguration的transactionAdvisor()方法传入的AnnotationTransactionAttributeSource对象。

    TransactionAttributeSourcePointcut

    TransactionAttributeSourcePointcut的源码如下:

    1. package org.springframework.transaction.interceptor;
    2. /**
    3. * 抽象类,继承StaticMethodMatcherPointcut,属于静态的方法匹配。在方法匹配中,通过抽象方法getTransactionAttributeSource()获
    4. * 取TransactionAttributeSource对象,然后执行TransactionAttributeSource.getTransactionAttribute(),判断方法是否有事务注解,
    5. * 如果有,表示匹配;没有则不匹配
    6. */
    7. @SuppressWarnings("serial")
    8. abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
    9. @Override
    10. public boolean matches(Method method, @Nullable Class targetClass) {
    11. if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
    12. return false;
    13. }
    14. // 返回AnnotationTransactionAttributeSource
    15. TransactionAttributeSource tas = getTransactionAttributeSource();
    16. // 在AnnotationTransactionAttributeSource的getTransactionAttribute()方法中,
    17. // 获取方法添加的@Transactional注解的信息,如果没有添加@Transactional注解,返回null
    18. return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    19. }
    20. @Override
    21. public boolean equals(Object other) {
    22. if (this == other) {
    23. return true;
    24. }
    25. if (!(other instanceof TransactionAttributeSourcePointcut)) {
    26. return false;
    27. }
    28. TransactionAttributeSourcePointcut otherPc = (TransactionAttributeSourcePointcut) other;
    29. return ObjectUtils.nullSafeEquals(getTransactionAttributeSource(), otherPc.getTransactionAttributeSource());
    30. }
    31. @Override
    32. public int hashCode() {
    33. return TransactionAttributeSourcePointcut.class.hashCode();
    34. }
    35. @Override
    36. public String toString() {
    37. return getClass().getName() + ": " + getTransactionAttributeSource();
    38. }
    39. /**
    40. * 抽象方法,获得一个TransactionAttributeSource对象
    41. * @return
    42. */
    43. @Nullable
    44. protected abstract TransactionAttributeSource getTransactionAttributeSource();
    45. }

    TransactionAttributeSourcePointcut继承StaticMethodMatcherPointcut,StaticMethodMatcherPointcut的类匹配默认为ClassFilter.TRUE,即匹配所有的类。

    当Spring中的bean对象声明之后,都会调用BeanFactoryTransactionAttributeSourceAdvisor切面,遍历bean的方法,执行切点的matches(Method method, @Nullable Class targetClass)方法,该方法执行如下:

    6.1)调用getTransactionAttributeSource(),获取一个TransactionAttributeSource。此处为AnnotationTransactionAttributeSource对象;

    6.2)因为TransactionAttributeSource对象不为空,所以执行TransactionAttributeSource的getTransactionAttribute()方法,该方法会执行AnnotationTransactionAttributeSource的父类AbstractFallbackTransactionAttributeSource的getTransactionAttribute()方法,如果方法有返回值,则matches()方法返回true。对应的bean创建代理类,并添加Advisor中的拦截器,即添加TransactionInterceptor;

    在该方法中,最终会调用子类AnnotationTransactionAttributeSource的determineTransactionAttribute()方法,遍历事务的解析器,从解析器中获取事务属性信息;结合1)中的描述,会解析org.springframework.transaction.annotation包和javax.transaction包下的@Transactional注解。如果没有@Transactional注解,getTransactionAttribute()最终返回null。matches()会返回false,即不支持,如果有注解,则返回true。对应的bean创建代理类,并添加Advisor中的拦截器,即添加TransactionInterceptor;

    AnnotationTransactionAttributeSource

    AnnotationTransactionAttributeSource的相关源码如下:

    1. package org.springframework.transaction.annotation;
    2. /**
    3. * 继承AbstractFallbackTransactionAttributeSource,对外提供了getTransactionAttribute()方法。该方法返回某个方法的事务注解信息
    4. * 1、添加不同类型的事务注解解析器,用于解析注解信息。支撑jta、ejb和spring的事务
    5. */
    6. @SuppressWarnings("serial")
    7. public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
    8. implements Serializable {
    9. /** 判断包中是否有javax.transaction.Transactional这个类,即项目中是否有对应的包。如果有,则需要添加对应的事务解析器 */
    10. private static final boolean jta12Present = ClassUtils.isPresent(
    11. "javax.transaction.Transactional", AnnotationTransactionAttributeSource.class.getClassLoader());
    12. /** 判断包中是否有javax.ejb.TransactionAttribute这个类,即项目中是否有对应的包。如果有,则需要添加对应的事务解析器 */
    13. private static final boolean ejb3Present = ClassUtils.isPresent(
    14. "javax.ejb.TransactionAttribute", AnnotationTransactionAttributeSource.class.getClassLoader());
    15. /** 标记是否只有public方法才可以添加事务,默认为true */
    16. private final boolean publicMethodsOnly;
    17. private final Set annotationParsers;
    18. public AnnotationTransactionAttributeSource() {
    19. this(true);
    20. }
    21. /**
    22. * 设置publicMethodsOnly的值,并添加事务的注解解析器集合
    23. */
    24. public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
    25. this.publicMethodsOnly = publicMethodsOnly;
    26. if (jta12Present || ejb3Present) {
    27. this.annotationParsers = new LinkedHashSet<>(4);
    28. // 添加spring的Transactional注解解析器
    29. this.annotationParsers.add(new SpringTransactionAnnotationParser());
    30. // 根据条件,添加jta和ejb3两种事务的注解解析器
    31. if (jta12Present) {
    32. this.annotationParsers.add(new JtaTransactionAnnotationParser());
    33. }
    34. if (ejb3Present) {
    35. this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
    36. }
    37. }
    38. else {
    39. // 如果不支持jta和ejb3两种注解的事务,则只添加spring的Transactional注解解析器
    40. this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
    41. }
    42. }
    43. /**
    44. * 调用determineTransactionAttribute()方法,从类中查找事务注解信息
    45. */
    46. @Override
    47. @Nullable
    48. protected TransactionAttribute findTransactionAttribute(Class clazz) {
    49. return determineTransactionAttribute(clazz);
    50. }
    51. /**
    52. * 调用determineTransactionAttribute()方法,从方法中查找事务注解信息
    53. */
    54. @Override
    55. @Nullable
    56. protected TransactionAttribute findTransactionAttribute(Method method) {
    57. return determineTransactionAttribute(method);
    58. }
    59. /**
    60. * 确定事务属性。遍历事务解析器集合,对当前element进行解析,如果能够正常解析,则返回对应
    61. * 的TransactionAttribute【RuleBasedTransactionAttribute对象】;否则返回null
    62. */
    63. @Nullable
    64. protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
    65. for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
    66. TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
    67. if (attr != null) {
    68. return attr;
    69. }
    70. }
    71. return null;
    72. }
    73. }

    在AnnotationTransactionAttributeSource构造方法中,会添加SpringTransactionAnnotationParser和JtaTransactionAnnotationParser事务注解解析器。

    7.1 SpringTransactionAnnotationParser

    SpringTransactionAnnotationParser的源码如下:

    1. package org.springframework.transaction.annotation;
    2. /**
    3. * 实现TransactionAnnotationParser,解析org.springframework.transaction.annotation.Transactional的注解信息,
    4. * 返回一个RuleBasedTransactionAttribute对象
    5. */
    6. @SuppressWarnings("serial")
    7. public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
    8. /**
    9. * 解析@Transaction注解,获取一个TransactionAttribute对象
    10. */
    11. @Override
    12. @Nullable
    13. public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
    14. // 在所提供的element上方的注释层次结构中查找Transactional的第一个注释,
    15. // 并将该注释的属性与注释层次结构较低级别注释中的匹配的attribute合并
    16. AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
    17. element, Transactional.class, false, false);
    18. // 解析Transactional注解,返回一个RuleBasedTransactionAttribute对象
    19. if (attributes != null) {
    20. return parseTransactionAnnotation(attributes);
    21. }
    22. else {
    23. return null;
    24. }
    25. }
    26. public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
    27. return parseTransactionAnnotation(AnnotationUtils.getAnnotationAttributes(ann, false, false));
    28. }
    29. /**
    30. * 从注解属性对象中,获取事务注解相关属性信息,封装成RuleBasedTransactionAttribute对象
    31. */
    32. protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    33. // 根据注解的信息,创建一个RuleBasedTransactionAttribute对象
    34. RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
    35. Propagation propagation = attributes.getEnum("propagation");
    36. rbta.setPropagationBehavior(propagation.value());
    37. Isolation isolation = attributes.getEnum("isolation");
    38. rbta.setIsolationLevel(isolation.value());
    39. rbta.setTimeout(attributes.getNumber("timeout").intValue());
    40. rbta.setReadOnly(attributes.getBoolean("readOnly"));
    41. rbta.setQualifier(attributes.getString("value"));
    42. // 添加回滚规则
    43. List rollbackRules = new ArrayList<>();
    44. for (Class rbRule : attributes.getClassArray("rollbackFor")) {
    45. rollbackRules.add(new RollbackRuleAttribute(rbRule));
    46. }
    47. for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
    48. rollbackRules.add(new RollbackRuleAttribute(rbRule));
    49. }
    50. for (Class rbRule : attributes.getClassArray("noRollbackFor")) {
    51. rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    52. }
    53. for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
    54. rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    55. }
    56. rbta.setRollbackRules(rollbackRules);
    57. return rbta;
    58. }
    59. @Override
    60. public boolean equals(Object other) {
    61. return (this == other || other instanceof SpringTransactionAnnotationParser);
    62. }
    63. @Override
    64. public int hashCode() {
    65. return SpringTransactionAnnotationParser.class.hashCode();
    66. }
    67. }

    SpringTransactionAnnotationParser用于解析org.springframework.transaction.annotation包下的@Transactional的注解信息。

    7.2 JtaTransactionAnnotationParser

    JtaTransactionAnnotationParser的源码如下:

    1. package org.springframework.transaction.annotation;
    2. /**
    3. * 实现TransactionAnnotationParser,用于解析javax.transaction.Transactional注解的事务属性。返回一个RuleBasedTransactionAttribute对象
    4. */
    5. @SuppressWarnings("serial")
    6. public class JtaTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
    7. /**
    8. * 判断是否有javax.transaction.Transactional,如果有,则进行注解解析【解析传播行为,回滚的异常类、不回滚的异常类设置回滚规则】,
    9. * 返回一个RuleBasedTransactionAttribute对象
    10. * @param element
    11. * @return
    12. */
    13. @Override
    14. @Nullable
    15. public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
    16. AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(
    17. element, javax.transaction.Transactional.class);
    18. if (attributes != null) {
    19. return parseTransactionAnnotation(attributes);
    20. }
    21. else {
    22. return null;
    23. }
    24. }
    25. public TransactionAttribute parseTransactionAnnotation(javax.transaction.Transactional ann) {
    26. return parseTransactionAnnotation(AnnotationUtils.getAnnotationAttributes(ann, false, false));
    27. }
    28. protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    29. RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
    30. // 通过注解的value设定传播行为。默认为REQUIRED
    31. rbta.setPropagationBehaviorName(
    32. RuleBasedTransactionAttribute.PREFIX_PROPAGATION + attributes.getEnum("value").toString());
    33. // 根据设置的回滚的异常类,添加回滚规则
    34. List rollbackRules = new ArrayList<>();
    35. for (Class rbRule : attributes.getClassArray("rollbackOn")) {
    36. rollbackRules.add(new RollbackRuleAttribute(rbRule));
    37. }
    38. // 根据设置的不回滚的异常类,添加回滚规则,定义为不回滚
    39. for (Class rbRule : attributes.getClassArray("dontRollbackOn")) {
    40. rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    41. }
    42. rbta.setRollbackRules(rollbackRules);
    43. return rbta;
    44. }
    45. @Override
    46. public boolean equals(Object other) {
    47. return (this == other || other instanceof JtaTransactionAnnotationParser);
    48. }
    49. @Override
    50. public int hashCode() {
    51. return JtaTransactionAnnotationParser.class.hashCode();
    52. }
    53. }

    JtaTransactionAnnotationParser用于解析javax.transaction包下的@Transactional注解。

    在6.2)中调用AnnotationTransactionAttributeSource的getTransactionAttribute(),最终会调用AnnotationTransactionAttributeSource的determineTransactionAttribute()方法,遍历事务注解的解析器,从解析器中获取事务属性信息。只要方法中添加了org.springframework.transaction.annotation包或javax.transaction包的@Transactional注解,则能够正常获取事务属性信息。

    小结

    限于篇幅,本篇先分享到这里。以下做一个小结:

    1)在SpringBoot框架的项目,项目启动时,会自动开启@EnableTransactionManagement注解,从而自动添加BeanFactoryTransactionAttributeSourceAdvisor切面,添加TransactionAttributeSourcePointcut切点。该切点的ClassFilter为ClassFilter.TRUE,即过滤所有的类。

    2)Spring的bean自动注入时,会执行BeanFactoryTransactionAttributeSourceAdvisor切面,遍历bena的方法,执行TransactionAttributeSourcePointcut的matches()。在该方法中,会执行AnnotationTransactionAttributeSource的getTransactionAttribute()方法,判断方法是否添加了org.springframework.transaction.annotation包或javax.transaction包下的@Transactional注解,如果有,则bean的代理类中添加TransactionInterceptor拦截器;

    关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

  • 相关阅读:
    第二章第三节:字符串常规操作
    简述Mysql中索引类型及对数据库的性能的影响
    5- 进入docker 打包的文件,查看内部文件
    Java 复习笔记 - 集合进阶篇:List集合
    pv操作题目笔记
    初识树结构和二叉树
    发布Android库至MavenCentral详解
    【Linux】实时线程的优先级设置、调度和抢占
    IF:14+ “冒烟型”骨髓瘤的分子组成突显了导致多发性骨髓瘤的进化途径
    C++ Reference: Standard C++ Library reference: C Library: cmath: rint
  • 原文地址:https://blog.csdn.net/JingAi_jia917/article/details/139475205