spring5.x-声明式事务原理及源码实现

news2024/11/22 21:57:37

上文:spring5.x-AOP实现原理及源码分析

本系列文章:
               

                    spring5.x-AOP实现原理及源码分析

                    spring5.x-监听器原理及源码实现

                    spring5.x-解决循环依赖分析

                    spring5.x-IOC模块源码学习

                    spring5.x介绍及搭配spring源码阅读环境


基础知识

请先学习原来的文章:spring事务管理

spring事务的实现原理

Spring框架提供了对事务的支持,使得开发者能够在应用程序中轻松地管理事务的边界、控制事务的传播行为和事务的隔离级别。Spring事务的原理主要基于两个关键概念:事务管理器和切面。

  1. 事务管理器(Transaction Manager):事务管理器负责协调和管理事务的执行。它提供了开始事务、提交事务和回滚事务等操作。Spring框架支持多种事务管理器,如JDBC事务管理器、Hibernate事务管理器和JTA事务管理器等。开发者可以根据具体的需求选择合适的事务管理器。

  2. 切面(Aspect):在Spring框架中,事务管理通过AOP(面向切面编程)实现。通过AOP,Spring能够在方法调用前后添加事务处理逻辑,从而实现对事务的控制。开发者可以使用声明式事务管理来配置事务规则,并将其应用到目标方法上。

Spring事务的工作原理如下:

  1. 配置事务管理器:首先,开发者需要在Spring配置文件中配置一个合适的事务管理器。可以选择使用Spring框架提供的默认事务管理器,也可以自定义实现一个事务管理器。

  2. 定义事务规则:开发者可以使用Spring的声明式事务管理来定义事务规则。通过在配置文件中使用事务切面和切点的方式,可以指定哪些方法需要进行事务管理,并设置事务的传播行为和隔离级别等属性。

  3. 开启事务边界:当一个被事务管理器管理的方法被调用时,Spring会根据事务规则决定是否开启一个事务。如果需要开启事务,则事务管理器会创建一个新的事务,并将其与当前线程关联起来。

  4. 事务提交或回滚:方法执行完成后,事务管理器会根据方法的执行结果决定是提交事务还是回滚事务。如果方法执行成功,事务管理器会提交事务,使其生效;如果方法执行失败,事务管理器会回滚事务,使其失效。

  5. 事务的传播行为:在一个方法内部调用另一个被事务管理器管理的方法时,事务管理器会根据事务的传播行为规则来确定是否加入已有的事务或开启一个新的事务。

总结:Spring事务的原理是基于事务管理器和AOP的实现。事务管理器负责事务的协调和管理,而AOP则负责在方法调用前后添加事务处理逻辑。通过合理配置事务管理和定义事务规则,开发者可以很方便地在Spring应用程序中进行事务管理。

spring事务源码学习

@EnableTransactionManagement注解

c3de05056c545b04abd667d7960501d6.png

这个注解用于启动事务,注意在使用该注解的时候必须要有一个PlatformTransactionManager的bean注册,否则会报错。

@Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {
    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default 2147483647;
}

@EnableTransactionManagement->TransactionManagementConfigurationSelector类

通过import进行导入:TransactionManagementConfigurationSelector

注意下面这个导入呢有代理(PROXY)的和切面(ASPECTJ)

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
    public TransactionManagementConfigurationSelector() {
    }

    protected String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        //代理
        case PROXY:
        //这里有自动注册代理和
            return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
        //切面
        case ASPECTJ:
            return new String[]{"org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration"};
        default:
            return null;
        }
    }
}
@EnableTransactionManagement->TransactionManagementConfigurationSelector->PROXY

代码位置:org.springframework.context.annotation.AutoProxyRegistrar

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context.annotation;

import java.util.Iterator;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
//实现了ImportBeanDefinitionRegistrar接口。该类用于注册自动代理创建器。
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    private final Log logger = LogFactory.getLog(this.getClass());

    public AutoProxyRegistrar() {
    }
  //该方法根据传入的注解元数据和Bean定义注册表,查找具有特定属性的注解,并根据其属性值进行相应的操作。
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean candidateFound = false;
    //获取所有注解类型
        Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
    //获取迭代器
        Iterator var5 = annoTypes.iterator();
    //循环获取
        while(var5.hasNext()) {
      //获取下一个annotYPE
            String annoType = (String)var5.next();
      //获取属性
            AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
      //如果不为空才继续
            if (candidate != null) {
        //获取model属性
                Object mode = candidate.get("mode");
        //获取proxyTargetClass代理类的属性
                Object proxyTargetClass = candidate.get("proxyTargetClass");
        //两个都不为空 且类开为AdviceMode 且 proxyTargetClass
                if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) {
          //标记找到了修选的注解
                    candidateFound = true;
          //判断是AdviceMode类型注解
                    if (mode == AdviceMode.PROXY) {
            //通过aop方式注入到bean中(这里如果多次以第一次为主)
                        AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
            //如果开启了cglib那么这里会进行代理开启(如果出现多次会进行覆盖)
                        if ((Boolean)proxyTargetClass) {
                            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                            return;
                        }
                    }
                }
            }
        }
    //若没有找到任务记录 进行提醒日志打印
        if (!candidateFound && this.logger.isWarnEnabled()) {
            String name = this.getClass().getSimpleName();
            this.logger.warn(String.format("%s was imported but no annotations were found having both 'mode' and 'proxyTargetClass' attributes of type AdviceMode and boolean respectively. This means that auto proxy creator registration and configuration may not have occurred as intended, and components may not be proxied as expected. Check to ensure that %s has been @Import'ed on the same class where these annotations are declared; otherwise remove the import of %s altogether.", name, name, name));
        }

    }
}

从上面的代码衍生:AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);

注意下面:spring在注入的时候如果有多个bean名称是一样的,那么只会保留一个。

代码位l置:org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired

@Nullable
    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
      //如果包含了,那么会直接覆盖(aop的优先级更大)
      //所以如果你的事务注解@EnableTransactionManagement和@EnableAspectJAutoProxy不管你哪个配在上面,都会被@EnableAspectJAutoProxy覆盖掉。切记切记
            BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }

            return null;
        } else {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
            beanDefinition.setSource(source);
            beanDefinition.getPropertyValues().add("order", -2147483648);
            beanDefinition.setRole(2);
            registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
            return beanDefinition;
        }
    }

代码位置:org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration

下面这个类是一个配置类

//被注解为@Configuration。该类用于配置代理模式下的事务管理。
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
  //该Bean对象是BeanFactoryTransactionAttributeSourceAdvisor类型,用于提供事务增强器(TransactionAttributeSource和TransactionInterceptor)。
  @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
    BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    advisor.setTransactionAttributeSource(transactionAttributeSource());
    advisor.setAdvice(transactionInterceptor());
    advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
    return advisor;
  }
  //注册到Spring容器中。该Bean对象是AnnotationTransactionAttributeSource类型,用于从注解中获取事务属性。
  @Bean
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public TransactionAttributeSource transactionAttributeSource() {
    return new AnnotationTransactionAttributeSource();
  }
  //注册到Spring容器中。该Bean对象是TransactionInterceptor类型,用于实现具体的事务拦截逻辑。
  @Bean
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public TransactionInterceptor transactionInterceptor() {
    TransactionInterceptor interceptor = new TransactionInterceptor();
    interceptor.setTransactionAttributeSource(transactionAttributeSource());
    if (this.txManager != null) {
      interceptor.setTransactionManager(this.txManager);
    }
    return interceptor;
  }

}

父类:org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration

//为一个抽象类,被注解为@Configuration。该类用于提供抽象的事务管理配置,并实现了ImportAware接口。
@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {
  //enableTx是一个AnnotationAttributes类型的变量,表示通过@EnableTransactionManagement注解获取的启用事务管理的属性。
  protected AnnotationAttributes enableTx;

  //txManager是一个PlatformTransactionManager类型的变量,表示默认的事务管理器,可以通过TransactionManagementConfigurer进行配置。
  protected PlatformTransactionManager txManager;

//根据传入的注解元数据(importMetadata),通过AnnotationMetadata的getAnnotationAttributes方法获取EnableTransactionManagement注解的属性值,并将其转换为AnnotationAttributes对象赋值给enableTx变量。如果enableTx为空,则抛出IllegalArgumentException异常。
  @Override
  public void setImportMetadata(AnnotationMetadata importMetadata) {
    this.enableTx = AnnotationAttributes.fromMap(
        importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
    if (this.enableTx == null) {
      throw new IllegalArgumentException(
          "@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
    }
  }
//通过该方法将configurers集合中的唯一一个TransactionManagementConfigurer对象的annotationDrivenTransactionManager方法返回的事务管理器赋值给txManager变量。(默认的事务管理器通过这个可以实现)
  @Autowired(required = false)
  void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
    if (CollectionUtils.isEmpty(configurers)) {
      return;
    }
    //仅允许配置一个
    if (configurers.size() > 1) {
      throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
    }
    TransactionManagementConfigurer configurer = configurers.iterator().next();
    this.txManager = configurer.annotationDrivenTransactionManager();
  }

  //注册到Spring容器中。该Bean对象是TransactionalEventListenerFactory类型,用于处理事务相关的事件监听。
  @Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public TransactionalEventListenerFactory transactionalEventListenerFactory() {
    return new TransactionalEventListenerFactory();
  }

}

代码位置:org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor

回到刚才子类中的这个:BeanFactoryTransactionAttributeSourceAdvisor

//继承自AbstractBeanFactoryPointcutAdvisor注意:这里的advisor跟我之前aop是不是很像,其实就是那个advisor。该类用于根据事务属性源(TransactionAttributeSource)创建切点(Pointcut)
public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
  //是一个TransactionAttributeSource类型的变量,用于存储事务属性源。
  private TransactionAttributeSource transactionAttributeSource;
  //是一个TransactionAttributeSourcePointcut类型的成员变量,通过匿名内部类的方式创建,并重写了getTransactionAttributeSource方法,该方法返回transactionAttributeSource对象。
  private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
    @Override
    protected TransactionAttributeSource getTransactionAttributeSource() {
      return transactionAttributeSource;
    }
  };


  //用于设置transactionAttributeSource变量的值。(可以通过这个手动设置事务属性源)
  public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
    this.transactionAttributeSource = transactionAttributeSource;
  }

  //用于设置pointcut的类过滤器(ClassFilter)。默认情况下,使用ClassFilter.TRUE。
  public void setClassFilter(ClassFilter classFilter) {
    this.pointcut.setClassFilter(classFilter);
  }

  @Override
  public Pointcut getPointcut() {
    return this.pointcut;
  }

}

好下接下来注意一下这个:AbstractBeanFactoryPointcutAdvisor

代码位置:org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor

//重点只看这段 用于设置通知(Advice)。这个就是我们原来aop的设置,可以看下原来的aop
public void setAdvice(Advice advice) {
        synchronized(this.adviceMonitor) {
            this.advice = advice;
        }
    }

BeanFactoryTransactionAttributeSourceAdvisor->TransactionAttributeSourcePointcut

回来这个:TransactionAttributeSourcePointcut

代码位置:org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut

这个类就是切面应用到事务中来的实现。重点、重点、重点

//是一个抽象类,继承自StaticMethodMatcherPointcut类并实现了Serializable接口。
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
  //matches方法用于判断给定的方法和目标类是否匹配该切点。如果目标类不为null且是TransactionalProxy的子类或实现类,则返回false;否则,获取事务属性源(TransactionAttributeSource)并判断其是否为null,以及通过事务属性源获取给定方法和目标类的事务属性是否为null。如果事务属性为null,则返回false,否则返回true。(属于静态匹配)
  @Override
  public boolean matches(Method method, Class<?> targetClass) {
    if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
      return false;
    }
    TransactionAttributeSource tas = getTransactionAttributeSource();
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
  }

  @Override
  public boolean equals(Object other) {
    if (this == other) {
      return true;
    }
    if (!(other instanceof TransactionAttributeSourcePointcut)) {
      return false;
    }
    TransactionAttributeSourcePointcut otherPc = (TransactionAttributeSourcePointcut) other;
    return ObjectUtils.nullSafeEquals(getTransactionAttributeSource(), otherPc.getTransactionAttributeSource());
  }

  @Override
  public int hashCode() {
    return TransactionAttributeSourcePointcut.class.hashCode();
  }

  @Override
  public String toString() {
    return getClass().getName() + ": " + getTransactionAttributeSource();
  }


  //用于基于事务属性源来进行方法匹配和拦截。子类可以实现getTransactionAttributeSource方法来提供具体的事务属性源,从而根据事务的需求来进行方法的拦截和处理。
  protected abstract TransactionAttributeSource getTransactionAttributeSource();

}
@EnableTransactionManagement->TransactionManagementConfigurationSelector->ASPECTJ

下面这个就比较简单了。就是返回一个包含TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME的字符串数组。配置事务管理和代理时使用。事务管理是指对数据库事务的管理,而代理是指在方法调用前后添加额外的逻辑处理。

case ASPECTJ:
        return new String[] {
            TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};

public static final String TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME =
      "org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration";

注解是如何使用的?

我们都知道我们在spring体系中是使用了@Transactional这个注解来启动事务。

上文有讲过关于advisor,其实spring事务也是引用了aop的思想进行切面的实现逻辑。只是有自已的独特逻辑。

代码位置:org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans

public List<Advisor> findAdvisorBeans() {
    //检查是否存在缓存的advisor bean名称数组cachedAdvisorBeanNames。如果cachedAdvisorBeanNames为空,那么调用BeanFactoryUtils工具类的beanNamesForTypeIncludingAncestors方法获取Advisor类型的bean名称,并将结果赋值给advisorNames,并将结果缓存到cachedAdvisorBeanNames中。
    String[] advisorNames = this.cachedAdvisorBeanNames; 
    if (advisorNames == null) {
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
  //值为空则直返回一个空数组
    if (advisorNames.length == 0) {
        return new ArrayList();
    } else {
        List<Advisor> advisors = new ArrayList();
        String[] var3 = advisorNames;
        int var4 = advisorNames.length;
      //通过循环判断每个name是否符合条件
        for(int var5 = 0; var5 < var4; ++var5) {
            String name = var3[var5];
            if (this.isEligibleBean(name)) {
                //如果当前bean正在创建中(isCurrentlyInCreation),则跳过该advisor,并记录调试日志。
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipping currently created advisor '" + name + "'");
                    }
                } else {
                    //加入到列表中
                    try {
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    } catch (BeanCreationException var10) {
                        Throwable rootCause = var10.getMostSpecificCause();
                        if (rootCause instanceof BeanCurrentlyInCreationException) {
                            BeanCreationException bce = (BeanCreationException)rootCause;
                            if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Skipping advisor '" + name + "' with dependency on currently created bean: " + var10.getMessage());
                                }
                                continue;
                            }
                        }

                        throw var10;
                    }
                }
            }
        }

        return advisors;
    }
}
@Transactional

通过该注解进行启用事务。那么在前面的TransactionAttributeSourcePointcut.matches中会去识别是不是带了@Transactional注解,如果带了就是意味着启用事务。

前面初始化的逻辑与aop类似可以查看下aop的,这里不详细深入。

代码位置:org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>)

//用于判断给定的类是否适用于指定的切点(Pointcut)。
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        //如果为空直接抛出异常
        Assert.notNull(pc, "Pointcut must not be null");
        //不是这类过滤直接返回
        if (!pc.getClassFilter().matches(targetClass)) {
            return false;
        } else {
            //如果 MethodMatcher 是 MethodMatcher.TRUE,表示匹配任何方法,则直接返回 true。
            MethodMatcher methodMatcher = pc.getMethodMatcher();
            if (methodMatcher == MethodMatcher.TRUE) {
                return true;
            } else {
                //如果 MethodMatcher 是 IntroductionAwareMethodMatcher 的实例,将其赋值给 introductionAwareMethodMatcher。
                IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
                //判断是否这种类型
                if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
                    introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher)methodMatcher;
                }
              //用于保存目标class的对象
                Set<Class<?>> classes = new LinkedHashSet(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
                classes.add(targetClass);
                Iterator var6 = classes.iterator();
              //循环所有类对象
                while(var6.hasNext()) {
                    Class<?> clazz = (Class)var6.next();
                    //获取所有方法
                    Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
                    Method[] var9 = methods;
                    int var10 = methods.length;
                  //通过matches来匹配方法(就是前面那个方法)
                    for(int var11 = 0; var11 < var10; ++var11) {
                        Method method = var9[var11];
                        if (introductionAwareMethodMatcher != null && introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) || methodMatcher.matches(method, targetClass)) {
                            return true;
                        }
                    }
                }

                return false;
            }
        }
    }

注意这里:TransactionAttributeSourcePointcut.matches

这个方法就是刚刚前面初始化的时候实现的。这个就是用来判断是否为事务还是纯的aop。

@Override
  public boolean matches(Method method, Class<?> targetClass) {
    if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
      return false;
    }
    TransactionAttributeSource tas = getTransactionAttributeSource();
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
  }

接下来:org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#getTransactionAttribute

//重写了某个接口或者父类的getTransactionAttribute方法。
@Override
  public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
        //如果是object类型 直接返回
    if (method.getDeclaringClass() == Object.class) {
      return null;
    }

    // 生成一个缓存键(cacheKey),用于在属性缓存中查找或存储事务属性。
    Object cacheKey = getCacheKey(method, targetClass);
        //获取缓存中的值
    TransactionAttribute cached = this.attributeCache.get(cacheKey);
        //有值
    if (cached != null) {
      //判断是否为为空事务属性的对象,是直接返回null
      if (cached == NULL_TRANSACTION_ATTRIBUTE) {
        return null;
      }
      else {
                //不是返回该值
        return cached;
      }
    }
    else {
      // 如果缓存没有,那么查下事务注解中的属性
      TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
      // 解析出来为空 直放入缓存
      if (txAttr == null) {
        this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
      }
      else {
                //通过全类名+方法名生成方法标识
        String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
                //如果为DefaultTransactionAttribute 类型 则添加到属性中
        if (txAttr instanceof DefaultTransactionAttribute) {
          ((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
        }
                //判断日志开关,有则打印日志
        if (logger.isDebugEnabled()) {
          logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
        }
                //最后加到缓存中
        this.attributeCache.put(cacheKey, txAttr);
      }
            //返回
      return txAttr;
    }
  }

注意一下其中的:TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);

实现如下:

protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
    //判断是不是public 修饰的方法(这里也就是我们常说事务必须是public),发果不是直接返回null
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
    }

    // 获取目标类的用户类(userClass),忽略可能存在的 CGLIB 子类,仅对实际的用户类进行分析。
    Class<?> userClass = ClassUtils.getUserClass(targetClass);
    // The method may be on an interface, but we need attributes from the target class.
    // 获取最具体的方法(specificMethod),即在目标类及其父类/接口中查找最匹配的方法。
    Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
    //如果处理的方法包含泛型参数,则找到原始方法(BridgeMethodResolver.findBridgedMethod(specificMethod))。
    specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

    // 获取目前类是否有事务的属性,有则直接返回
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
      return txAttr;
    }

    // 如果目标类的方法中没有找到事务属性,则尝试从目标类本身获取事务属性(findTransactionAttribute(specificMethod.getDeclaringClass()))。
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
        //找到了事务属性,并且当前处理的是用户级别的方法(ClassUtils.isUserLevelMethod(method) 返回 true),则返回该事务属性
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
      return txAttr;
    }
      //如果是接口方法
    if (specificMethod != method) {
      // 去接口上方法找事务注解,如果不为空则直接返回
      txAttr = findTransactionAttribute(method);
      if (txAttr != null) {
        return txAttr;
      }
      // 去实现类找,如果不为空,有则返回
      txAttr = findTransactionAttribute(method.getDeclaringClass());
      if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
        return txAttr;
      }
    }
      //最后啥都没就返回空
    return null;
  }

上面的逻辑是->判断为public->去目标事务注解中找->去接口上面找->去接口实现类上面找

代理的实现

那么下面需要了解一下事务是如何去代理实现的。其实spring调用开始前面一大段都跟aop一样,只是在调用中需要去判断是哪种代理、aop还是cglib或是引入的框架。

这里跳下逻辑,因为与前面的aop类型直接到这个类:TransactionAspectSupport

代码位置:org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

//代理调用模版
  protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
      throws Throwable {

    // If the transaction attribute is null, the method is non-transactional.
        //获取事务属性
    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
        //获取配置 的事务管理器对象
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        //获取全路劲+方法名称+属性获取唯一标识符
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
      //如果属性为空 或 事务管理器对象不是为回滚类型
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
      
            //创建事务
      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
      Object retVal = null;
      try {
        //通过钩子函数回调目标方法( 这个就是调用)
        retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
        // 回滚的实现
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
      }
      finally {
                //清空所有缓存
        cleanupTransactionInfo(txInfo);
      }
            //提交事务
      commitTransactionAfterReturning(txInfo);
      return retVal;
    }
      //编程事务 逻辑上面类似
            
    else {
      final ThrowableHolder throwableHolder = new ThrowableHolder();

      // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
      try {
        Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
            new TransactionCallback<Object>() {
              @Override
              public Object doInTransaction(TransactionStatus status) {
                TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                try {
                  return invocation.proceedWithInvocation();
                }
                catch (Throwable ex) {
                  if (txAttr.rollbackOn(ex)) {
                    // A RuntimeException: will lead to a rollback.
                    if (ex instanceof RuntimeException) {
                      throw (RuntimeException) ex;
                    }
                    else {
                      throw new ThrowableHolderException(ex);
                    }
                  }
                  else {
                    // A normal return value: will lead to a commit.
                    throwableHolder.throwable = ex;
                    return null;
                  }
                }
                finally {
                  cleanupTransactionInfo(txInfo);
                }
              }
            });

        // Check result state: It might indicate a Throwable to rethrow.
        if (throwableHolder.throwable != null) {
          throw throwableHolder.throwable;
        }
        return result;
      }
      catch (ThrowableHolderException ex) {
        throw ex.getCause();
      }
      catch (TransactionSystemException ex2) {
        if (throwableHolder.throwable != null) {
          logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
          ex2.initApplicationException(throwableHolder.throwable);
        }
        throw ex2;
      }
      catch (Throwable ex2) {
        if (throwableHolder.throwable != null) {
          logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
        }
        throw ex2;
      }
    }
  }

其实上面的逻辑就是 创建事务->执行事务->回滚/完成事务

看下里面这个方法:TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

protected TransactionInfo createTransactionIfNecessary(
      PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {

    // 不为空 则名称为空 
    if (txAttr != null && txAttr.getName() == null) {
            //通过连接点的ID定义成事务的名称
      txAttr = new DelegatingTransactionAttribute(txAttr) {
        @Override
        public String getName() {
          return joinpointIdentification;
        }
      };
    }

    TransactionStatus status = null;
    if (txAttr != null) {
      if (tm != null) {
                //获取事务状态
        status = tm.getTransaction(txAttr);
      }
      else {
        if (logger.isDebugEnabled()) {
          logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
              "] because no transaction manager has been configured");
        }
      }
    }
        //将事务的信息封装到 TransactionInfo 并返回
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
  }

代码中:status = tm.getTransaction(txAttr);

代码位置:org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction

@Override
  public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        //获取当前的事务对象。
    Object transaction = doGetTransaction();

    
    boolean debugEnabled = logger.isDebugEnabled();
      //判断方法传递为空
    if (definition == null) {
      //使用默认的进行实始化
      definition = new DefaultTransactionDefinition();
    }
      //该事务已存在
    if (isExistingTransaction(transaction)) {
      // Existing transaction found -> check propagation behavior to find out how to behave.
            //处理存在的事务并返回
      return handleExistingTransaction(definition, transaction, debugEnabled);
    }

    //检事事务设置的超时时间,如果超时则抛出悍异常
    if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
      throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
    }

    // 如果为事务为运行中的事务中,则抛出异常(代表没有事务)
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
      throw new IllegalTransactionStateException(
          "No existing transaction found for transaction marked with propagation 'mandatory'");
    }
            /*
            如果不存在已有的事务,则根据传入的事务定义的传播行为来决定如何处理新的事务。
如果传播行为为 PROPAGATION_MANDATORY,则抛出 IllegalTransactionStateException 异常,表示标记为 "mandatory" 的事务没有找到现有的事务。
如果传播行为为 PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW 或 PROPAGATION_NESTED,则挂起当前事务资源,并创建一个新的事务,并返回一个新的 DefaultTransactionStatus 对象作为事务状态。在创建新事务之前,会检查是否需要同步事务,并准备相应的事务同步。如果在创建新事务过程中发生异常,则恢复挂起的事务资源,并重新抛出异常。
如果传播行为为其他值,则说明不需要实际的事务,但可能需要进行事务同步,所以会返回一个空的事务状态对象。
这里相当啰嗦~
            */
    else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
        definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
        definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
      SuspendedResourcesHolder suspendedResources = suspend(null);
      if (debugEnabled) {
        logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
      }
      try {
                //同步状态
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                //构建事务的状态
        DefaultTransactionStatus status = newTransactionStatus(
            definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                //创建一个新事务
        doBegin(transaction, definition);
                //绑定到线程变量去(这里相当复杂)
        prepareSynchronization(status, definition);
                //返回状态
        return status;
      }
      catch (RuntimeException ex) {
        resume(null, suspendedResources);
        throw ex;
      }
      catch (Error err) {
        resume(null, suspendedResources);
        throw err;
      }
    }
    else {
            //下面是创建一个空事务
      // Create "empty" transaction: no actual transaction, but potentially synchronization.
      if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
        logger.warn("Custom isolation level specified but no actual transaction initiated; " +
            "isolation level will effectively be ignored: " + definition);
      }
      boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
      return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
    }
  }

6b28ad6e22f1c752911cdfbf3dbb6da6.png

df0dbcd6fc632880cffe4113edc73c6c.png

关于事务的传播行为请自行百茺或看文章:spring事务管理

接着代码: doBegin(transaction, definition);

有三个实现。这里用的是第二个。

000a7154a15a6663bf66c17c07fbf1e0.png

org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin

//
protected void doBegin(Object transaction, TransactionDefinition definition) {
    //转为事务对象 
    DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
    
        Connection con = null;

        try {
            //通过数据源获取数据库连接
            if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                //获取边接
                Connection newCon = this.dataSource.getConnection();
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
                }
              //将数据库连接包装成一个ConnectionHolder,并放到 txObject属性中
                txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
            }
          //标记当前连接为同步事务
            txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
            //获取连接
            con = txObject.getConnectionHolder().getConnection();
            //获取事务隔离级别,根据事务定义对连接进行必要的准备工作
            Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
            //设置进属性
            txObject.setPreviousIsolationLevel(previousIsolationLevel);
            //自动提交默认为true
            if (con.getAutoCommit()) {
                txObject.setMustRestoreAutoCommit(true);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
                }
              //开启事务
                con.setAutoCommit(false);
            }
          //判断事务是否只读
            this.prepareTransactionalConnection(con, definition);
            //设置事务为激活状态
            txObject.getConnectionHolder().setTransactionActive(true);
            //获取超时时间
            int timeout = this.determineTimeout(definition);
            //不为-1则进行设置超时时间属性
            if (timeout != -1) {
                txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
            }
          //如果为一个新事我国
            if (txObject.isNewConnectionHolder()) {
                //绑定key 和value 到线程池中
                TransactionSynchronizationManager.bindResource(this.getDataSource(), txObject.getConnectionHolder());
            }

        } catch (Throwable var7) {
            //如果在上述过程中发生异常,则会释放连接并抛出 CannotCreateTransactionException 异常,表示无法为事务打开数据库连接。
            if (txObject.isNewConnectionHolder()) {
                DataSourceUtils.releaseConnection(con, this.dataSource);
                txObject.setConnectionHolder((ConnectionHolder)null, false);
            }

            throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", var7);
        }
    }

上面的逻辑还是比较易懂的。比竟只要学完jdbc基本都看得懂。

最后还有一种事务是嵌套事务,那么这种也了解一下。

回到刚才这个位置:org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction中的

if (isExistingTransaction(transaction)) {
      // Existing transaction found -> check propagation behavior to find out how to behave.
      return handleExistingTransaction(definition, transaction, debugEnabled);
    }
private TransactionStatus handleExistingTransaction(
      TransactionDefinition definition, Object transaction, boolean debugEnabled)
      throws TransactionException {
      //存在事务则跑出异常
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
      throw new IllegalTransactionStateException(
          "Existing transaction found for transaction marked with propagation 'never'");
    }
      //存在外部事务则挂起事务
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
      if (debugEnabled) {
        logger.debug("Suspending current transaction");
      }
            //挂起事务
      Object suspendedResources = suspend(transaction);
      boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            //创建一个新的非事务状态并返回
      return prepareTransactionStatus(
          definition, null, false, newSynchronization, debugEnabled, suspendedResources);
    }
      //存在外部事务,挂起外部事务,新建一个事务
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
      if (debugEnabled) {
        logger.debug("Suspending current transaction, creating new transaction with name [" +
            definition.getName() + "]");
      }
      SuspendedResourcesHolder suspendedResources = suspend(transaction);
      try {
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        DefaultTransactionStatus status = newTransactionStatus(
            definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
        doBegin(transaction, definition);
        prepareSynchronization(status, definition);
        return status;
      }
      catch (RuntimeException beginEx) {
        resumeAfterBeginException(transaction, suspendedResources, beginEx);
        throw beginEx;
      }
      catch (Error beginErr) {
        resumeAfterBeginException(transaction, suspendedResources, beginErr);
        throw beginErr;
      }
    }
      //存在外部事务,融进外部事务中
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
      if (!isNestedTransactionAllowed()) {
        throw new NestedTransactionNotSupportedException(
            "Transaction manager does not allow nested transactions by default - " +
            "specify 'nestedTransactionAllowed' property with value 'true'");
      }
      if (debugEnabled) {
        logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
      }
            //支持保存在
      if (useSavepointForNestedTransaction()) {
      
                //开启一个新的事务并返回状态
        DefaultTransactionStatus status =
            prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
                //为事务创建一个回滚的点
        status.createAndHoldSavepoint();
        return status;
      }
      else {
        // Nested transaction through nested begin and commit/rollback calls.
        // Usually only for JTA: Spring synchronization might get activated here
        // in case of a pre-existing JTA transaction.
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        DefaultTransactionStatus status = newTransactionStatus(
            definition, transaction, true, newSynchronization, debugEnabled, null);
        doBegin(transaction, definition);
        prepareSynchronization(status, definition);
        return status;
      }
    }

    // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
    if (debugEnabled) {
      logger.debug("Participating in existing transaction");
    }
        //是否验证事务
    if (isValidateExistingTransaction()) {
      if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
                //当前事务等级
        Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
                //为空或等级不一致 则抛出异常
        if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
                    
          Constants isoConstants = DefaultTransactionDefinition.constants;
          throw new IllegalTransactionStateException("Participating transaction with definition [" +
              definition + "] specifies isolation level which is incompatible with existing transaction: " +
              (currentIsolationLevel != null ?
                  isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
                  "(unknown)"));
        }
      }
            //不是为只读 且不一致 则抛出异常
      if (!definition.isReadOnly()) {
        if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
          throw new IllegalTransactionStateException("Participating transaction with definition [" +
              definition + "] is not marked as read-only but existing transaction is");
        }
      }
    }
    //最后 创建一个新的同步 并返回状态
    boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
  }

这些操作实最终都会被doComit()

8b19915fe25d782e085bbb67cb94b2ac.png

如果失败则进行回滚。

最后

Spring事务管理的核心思想是将事务的控制与业务逻辑分离,使得业务代码不需要关注事务的开启、提交或回滚等细节。通过使用Spring的事务管理器和声明式事务配置,可以将事务逻辑从业务代码中解耦,提供了更加灵活和简便的事务管理方式。相对来说spring事务比aop更加复杂,需要有aop+jdbc的基础。本文相对来说比较片面,可能理解起来不是很好,如果实在看不懂,建议参考下面的文章,对比看。

参考文章:

https://juejin.cn/post/6887751198737170446

https://blog.csdn.net/rongtaoup/article/details/127688984

https://www.cnblogs.com/dennyzhangdd/p/9602673.html

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

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

相关文章

高职教育应对ChatGPT应用的策略

一、完善顶层设计&#xff0c;提升技术水平 在推广ChatGPT平台的过程中&#xff0c;高职院校需要关注技术本身的问题。这就需要在国家和地方政府的引导下&#xff0c;引入更完善的技术顶层设计&#xff0c;提高人工智能在高职教育中的运用水平。具体来说&#xff0c;一方面需要…

信息化发展3

信息化体系 国家信息化体系包括信息技术应用&#xff08;龙头&#xff09; 、信息资源&#xff08;核心&#xff09; 、信息网络&#xff08;基础设施&#xff09; 、信息技术和产业&#xff08;国家信息化建设基础&#xff09; 、信息化人才&#xff08;关键&#xff09; 、信…

Android常用组件:空布局empty_view,占位图封装

最近在做一件费劲不讨好的事情&#xff0c;那就是把项目中无关业务的代码功能模块抽出来&#xff0c;供以后使用。 既然费劲心机&#xff0c;不妨分享出来&#xff0c;大家可以一块学习借鉴。 直接来看看空布局的界面效果&#xff1a; 基本的布局就是以下结构&#xff1a; xm…

Python中的装饰器介绍

装饰器是Python编程语言中一种强大的特性&#xff0c;用于修改或增强函数或类的行为&#xff0c;而无需对它们本身进行修改。装饰器通常被用于在不改变原始代码的情况下&#xff0c;向函数或方法添加额外的功能&#xff0c;如日志记录、权限检查、数据格式转换等。装饰器本质上…

从小学到高考:讲述我与儿子的成长故事

三年前&#xff0c;当儿子考上高中的时候&#xff0c;我就想着写一篇文章做纪念&#xff0c;然后写了一半就放弃了&#xff0c;因为初中阶段充满了遗憾。在儿子高一拿到好成绩时&#xff0c;我又想着写一篇文章以作鼓励&#xff0c;写了一半也放弃了&#xff0c;因为革命尚未成…

聊天平台Revolt的搭建

经网友 凌尘 提醒&#xff0c;Web-Check 最新的镜像版本&#xff0c;容器端口已经从 8888 改为了 3000&#xff0c;特此更正&#xff01; 什么是 Revolt &#xff1f; Revolt 是一个开源的用户至上的聊天平台。是在不牺牲任何可用性的情况下与朋友和社区保持联系的最佳方式之一…

帝国CMS仿管理资源吧资料下载网站模板源码/下载会员+积分付费下载功能自动采集资源网站源码

帝国CMS仿管理资源吧资料下载网站模板源码&#xff0c;带下载会员积分付费下载功能自动采集资源网站源码&#xff0c;管理资源吧——为中小企业管理者提供全方位的管理资料下载服务&#xff1b;是一个所有资料免费下载&#xff0c;免注册、免登陆、免积分的公益性的管理知识共享…

地球的“第三极”及其在全球气候中的作用

青藏高原是全球气候系统的重要力量&#xff0c;也是气候变化的热点地区。一项新的审查总结了知识状况并确定了与该地区相关的研究需求。 西门措湖是一个冰川冰碛湖&#xff0c;坐落在青藏高原的山​​峰之中。图片来源&#xff1a;Tenace10/Wikimedia Commons &#xff0c; 资料…

ModaHub魔搭社区:WinPlan经营大脑开放API调用指南

本文主要是介绍开放API调用流程 1. 开放API调用流程 1.1 创建API-TOKEN 即API访问凭证, 是用户在调用API接口时的应用和组织身份凭证,请妥善保管。如遇到丢失或者泄漏的情况,请及时重新生成。 API-TOKEN生成方式: 请前往[管理中心]->[数据集成]页面创建/重新生成。Win…

将Series序列中的缺失值用后一个值填充Series.bfill()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 对于Series序列中的缺失值n1 用n1后面的值n2来填充替代 Series.bfill() [太阳]选择题 关于以下代码的说法中错误的是? import numpy as np import pandas as pd a pd.Series([1,np.nan,2,np.…

PCI控制器扫描PCI总线过程解析

1、PCI的三种地址空间介绍 地址空间描述命令说明I/O空间供给设备驱动程序使用I/O操作命令对设备对应的I/O地址空间进行访问&#xff0c;此类访问不可预取存储空间供给设备驱动程序使用memory操作命令对设备的Memory空间进行访问&#xff0c;其中Memory操作命令又可分为Prefech…

Linux部署RocketMQ并使用SpringBoot创建生产、消费者

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;RocketMQ、消息队列☀️每日 一言&#xff1a;在你心灰意冷、心烦意乱时也不要停下你的脚步&#xff01; 一、前言 RocketMQ&#xff08;Apache RocketMQ&#xff09;是一种开源的分布式消息中间…

【Day-21慢就是快】代码随想录-栈与队列-逆波兰表达式求值

逆波兰表达式&#xff1a;是一种后缀表达式&#xff0c;所谓后缀就是指运算符写在后面。 平常使用的算式则是一种中缀表达式&#xff0c;如 ( 1 2 ) * ( 3 4 ) 。 该算式的逆波兰表达式写法为 ( ( 1 2 ) ( 3 4 ) * ) 。 逆波兰表达式主要有以下两个优点&#xff1a; 去掉…

Cesium 显示经纬高

文章目录 需求分析 需求 页面展示经、纬度和高 分析 html <div id"latlng_show" style"width:340px;height:30px;position:absolute;bottom:40px;right:200px;z-index:1;font-size:15px;"><div style"width:100px;height:30px;float:left;…

浅析三维模型OBJ格式轻量化压缩文件大小的技术方法

浅析三维模型OBJ格式轻量化压缩文件大小的技术方法 在减小三维模型OBJ格式轻量化文件大小方面&#xff0c;有许多技术和方法可以使用。下面我将介绍一些常用的方法来减小OBJ文件的大小。 1、优化顶点数量&#xff1a;减少OBJ文件中的顶点数量是减小文件大小的一种有效方法。可…

LSF 安装目录,快速参考 LSF 命令、守护程序、配置文件、日志文件和重要集群配置参数

样本 UNIX 和 Linux 安装目录 守护程序错误日志文件 守护程序错误日志文件存储在 LSF_LOGDIR 在 lsf.conf 文件中定义的目录中。 LSF 基本系统守护程序日志文件LSF 批处理系统守护程序日志文件pim.log.host_namembatchd.log.host_namembatchd.log.host_namesbatchd.log.host_…

【Go 基础篇】切片:Go语言中的灵活数据结构

在Go语言中&#xff0c;切片&#xff08;Slice&#xff09;是一种强大且灵活的数据结构&#xff0c;用于管理和操作一系列元素。与数组相比&#xff0c;切片的大小可以动态调整&#xff0c;这使得它成为处理动态数据集合的理想选择。本文将围绕Go语言中切片的引入&#xff0c;介…

Java【手撕双指针】LeetCode 18. “四数之和“, 图文详解思路分析 + 代码

文章目录 前言一、四数之和1, 题目2, 思路分析3, 代码 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4d5; JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等 &#x1f4d7; Java数据结构: 顺序表, 链表, 堆…

C# Emgu.CV 条码检测

效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using Emgu.CV; using Emgu.CV.Util; using static Emgu.C…

ThinkPHP 资源路由的简单使用,restfull风格API

ThinkPHP 资源路由的简单使用&#xff0c;restfull风格API 一、资源控制器二、资源控制器简单使用 一、资源控制器 资源控制器可以轻松的创建RESTFul资源控制器&#xff0c;可以通过命令行生成需要的资源控制器&#xff0c;例如生成index应用的TestR资源控制器使用&#xff1a…