Spring源码解析(十一):spring事务配置类源码

news2024/10/5 16:20:02

Spring源码系列文章

Spring源码解析(一):环境搭建

Spring源码解析(二):bean容器的创建、默认后置处理器、扫描包路径bean

Spring源码解析(三):bean容器的刷新

Spring源码解析(四):单例bean的创建流程

Spring源码解析(五):循环依赖

Spring源码解析(六):bean工厂后置处理器ConfigurationClassPostProcessor

Spring源码解析(七):bean后置处理器AutowiredAnnotationBeanPostProcessor

Spring源码解析(八):bean后置处理器CommonAnnotationBeanPostProcessor

Spring源码解析(九):AOP源码之@Aspect所有相关注解解析

Spring源码解析(十):spring整合mybatis源码

Spring源码解析(十一):spring事务配置类源码


目录

  • 一、编程式事务
    • 1、基础使用
    • 2、PlatformTransactionManager
    • 3、TransactionDefinition
    • 4、TransactionStatus
  • 二、声明式事务注册bean
    • 1、基本使用
    • 2、@EnableTransactionManagement
    • 3、AutoProxyRegistrar注册自动代理创建者
    • 4、ProxyTransactionManagementConfiguration注册事务管理bean
  • 三、创建代理对象

一、编程式事务

1、基础使用

  • TransactionManager 管理事务对象
@Bean
public TransactionManager transactionManager() {
    DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
    dataSourceTransactionManager.setDataSource(dataSource());
    return dataSourceTransactionManager;
}
  • TransactionDefinition 事务的属性对象
@Bean
public TransactionDefinition transactionDefinition() {
    return new DefaultTransactionDefinition();
}
  • 开启事务(获取事务)、提交事务、回滚事务
// 通过事务管理器开启一个事务
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
    // 完成自己的业务逻辑
}
catch (MyException ex) {
    // 出现异常,进行回滚
    transactionManager.rollback(transactionStatus);
    throw ex;
}
// 提交事务
transactionManager.commit(transactionStatus);

2、PlatformTransactionManager

  • 定义了完成一个事务必须的三个步骤:开启事务、提交事务、回滚事务
  • 抽象父类AbstractPlatformTransactionManager
    • 主要用作事务管理的模板
    • 实现了事务的传播行为以及跟事务相关的同步管理
  • 开启事务的方法就是通过一个TransactionDefinition来获取一个TransactionStatus类型的对象
public interface PlatformTransactionManager extends TransactionManager {
	// 开启事务
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
	
    // 提交事务
    void commit(TransactionStatus status) throws TransactionException;
	
    // 回滚事务
    void rollback(TransactionStatus status) throws TransactionException;
}

// 标记接口
public interface TransactionManager {
}

PlatformTransactionManager的实现类

在这里插入图片描述
AbstractPlatformTransactionManager,Spring提供的一个事务管理的基类,提供了事务管理的模板,实现了Spring事务管理的一个标准流程

  1. 判断当前是否已经存在一个事务
  2. 应用合适的事务传播行为
  3. 在必要的时候挂起/恢复事务
  4. 提交时检查事务是否被标记成为rollback-only
  5. 在回滚时做适当的修改(是执行真实的回滚/还是将事务标记成rollback-only
  6. 触发注册的同步回调

AbstractPlatformTransactionManager提供了四个常见的子类,其说明如下

在这里插入图片描述

3、TransactionDefinition

public interface TransactionDefinition {
	
    // 定义了7中事务的传播机制
	int PROPAGATION_REQUIRED = 0;
	int PROPAGATION_SUPPORTS = 1;
	int PROPAGATION_MANDATORY = 2;
	int PROPAGATION_REQUIRES_NEW = 3;
	int PROPAGATION_NOT_SUPPORTED = 4;
	int PROPAGATION_NEVER = 5;
	int PROPAGATION_NESTED = 6;

    // 4种隔离级别,-1代表的是使用数据库默认的隔离级别
    // 比如在MySQL下,使用的就是ISOLATION_REPEATABLE_READ(可重复读)
	int ISOLATION_DEFAULT = -1;
	int ISOLATION_READ_UNCOMMITTED = 1;  
	int ISOLATION_READ_COMMITTED = 2; 
	int ISOLATION_REPEATABLE_READ = 4; 
	int ISOLATION_SERIALIZABLE = 8;  
    
    // 事务的超时时间,默认不限制时间
	int TIMEOUT_DEFAULT = -1;
	
    // 提供了对上面三个属性的get方法
	default int getPropagationBehavior() {
		return PROPAGATION_REQUIRED;
	}
	default int getIsolationLevel() {
		return ISOLATION_DEFAULT;
	}
	default int getTimeout() {
		return TIMEOUT_DEFAULT;
	}
	
    // 事务是否是只读的,默认不是
	default boolean isReadOnly() {
		return false;
	}
    
    // 事务的名称
	@Nullable
	default String getName() {
		return null;
	}
    
    // 返回一个只读的TransactionDefinition
    // 只对属性提供了getter方法,所有属性都是接口中定义的默认值
	static TransactionDefinition withDefaults() {
		return StaticTransactionDefinition.INSTANCE;
	}
}
  • 有些是数据库层面本身就有的,例如隔离级别、是否只读、超时时间、名称
  • 也有些是Spring赋予的,例如事务的传播机制

Spring中一共定义了7种事务的传播机制

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务(默认
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常
  • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于默认方式

TransactionDefinition默认实现类DefaultTransactionDefinition

  • 为定义属性提供了默认值
public class DefaultTransactionDefinition implements TransactionDefinition{
	...

	// 默认的传播机制为required,没有事务新建一个事务
	// 有事务的话加入当前事务
	private int propagationBehavior = PROPAGATION_REQUIRED;
	
	// 隔离级别跟数据库默认的隔离级别一直
	private int isolationLevel = ISOLATION_DEFAULT;
	
	// 默认为-1,不设置超时时间
	private int timeout = TIMEOUT_DEFAULT;
	
	// 默认不是只读的
	private boolean readOnly = false;

	...
}

4、TransactionStatus

  • 这个接口主要用于描述Spring事务的状态,其继承关系如下:

在这里插入图片描述

  • TransactionExecution,这个接口也是用于描述事务的状态
// 判断当前事务是否是一个新的事务
// 不是一个新事务的话,那么需要加入到已经存在的事务中
boolean isNewTransaction();

// 事务是否被标记成RollbackOnly
// 如果被标记成了RollbackOnly,意味着事务只能被回滚
void setRollbackOnly(); 
boolean isRollbackOnly();

// 是否事务完成,回滚或提交都意味着事务完成了
boolean isCompleted();
  • SavepointManager,定义了管理保存点(Savepoint)的方法
  • 隔离级别为NESTED时就是通过设置回滚点来实现的
// 创建保存点
Object createSavepoint() throws TransactionException;

// 回滚到指定保存点
void rollbackToSavepoint(Object savepoint) throws TransactionException;

// 移除回滚点
void releaseSavepoint(Object savepoint) throws TransactionException;
  • TransactionStatus,继承了上面这些接口,额外提供了两个方法
//用于判断当前事务是否设置了保存点
boolean hasSavepoint();

// 这个方法复写了父接口Flushable中的方法
// 主要用于刷新会话
// 对于Hibernate/jpa而言就是调用了其session/entityManager的flush方法
void flush();

总结:

  • TransactionDefinition的主要作用是给出一份事务属性的定义,然后事务管理器根据给出的定义来创建事务
  • TransactionStatus主要是用来描述创建后的事务的状态

二、声明式事务注册bean

1、基本使用

  • spring整合mybatis并添加事务配置和使用
@Configuration // 声明该类是核心配置类
@ComponentScan("com.xc") // 开启spring注解扫描
@MapperScan("com.xc.mapper") // MyBatis扫描Mapper接口
@EnableTransactionManagement
public class MybatisConfig {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setPassword("root");
        driverManagerDataSource.setUsername("root");
        driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true");
        return driverManagerDataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean() {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource());
        return sqlSessionFactoryBean;
    }

     // 使用Spring中的DataSourceTransactionManager管理事务
     @Bean
     public DataSourceTransactionManager transactionManager() {
         DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
         dataSourceTransactionManager.setDataSource(dataSource());
         return dataSourceTransactionManager;
     }

	// 执行操作
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MybatisConfig.class);
        UserMapper userMapper = context.getBean(UserMapper.class);
        User user = new User(200,"李四");
        userMapper.insertOne(user);
    }
}
  • 通过开启AOP自动代理并向容器中注册事务需要的通知(Transaction Advisor
  • 其内部也是调用了TransactionManager的方法
  • 主要涉及到两个核心注解
    • @EnableTransactionManagement
    • @Transactional

2、@EnableTransactionManagement

  • Spring事务管理的入口就是@EnableTransactionManagement注解
  • 与之前讲过的@EnableAspectJAutoProxy很相似
    • 可以选择使用jdk或cglib代理
    • 都通过@Import注册bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
	
	// 是否使用cglib代理,默认是jdk代理
	boolean proxyTargetClass() default false;
	
    // 使用哪种代理模式,Spring AOP还是AspectJ
	AdviceMode mode() default AdviceMode.PROXY;
	
    // 为了完成事务管理,会向容器中添加通知
    // 这个order属性代表了通知的执行优先级
    // 默认是最低优先级
	int order() default Ordered.LOWEST_PRECEDENCE;
}

TransactionManagementConfigurationSelector

  • 核心内容就是注册AutoProxyRegistrarProxyTransactionManagementConfiguration
/**
 * 子类实现了AdviceModeImportSelector,泛型类型为@EnableTransactionManagement注解
 * 重写了selectImports(AdviceMode adviceMode)方法
 */
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

    /**
     * 如果mode是PROXY,那么返回AutoProxyRegistrar和ProxyTransactionManagementConfiguration
     * 如果mode是ASPECTJ,那么返回AspectJ(Jta)TransactionManagementConfiguration
     */
    @Override
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                //一般都是PROXY,因此将会注册AutoProxyRegistrar和ProxyTransactionManagementConfiguration的bean定义,进行动态代理
                return new String[]{AutoProxyRegistrar.class.getName(),
                        ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                //支持Aspectj的静态代理织入
                return new String[]{determineTransactionAspectClass()};
            default:
                //其他值则返回null,将会抛出异常
                return null;
        }
    }

    private String determineTransactionAspectClass() {
        return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
                TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
                TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
    }
}
  • 父类AdviceModeImportSelector实现了ImportSelector接口
  • 通用基类,根据注解的AdviceMode来选择注册不同的bean定义
/**
 * 这是一个通用基类,用于根据AdviceMode来选择注册不同的bean定义,该基类被用于广泛的支持@Enable*注解
 * 比如@EnableAsync、@EnableCaching、@EnableTransactionManagement(需要spring-tx依赖)
 *
 * @param <A> 注解类型泛型
 */
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {

    /**
     * AdviceMode属性的默认属性名字为"mode"
     */
    public static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode";


    /**
     * 获取AdviceMode属性的属性名,默认为"mode",子类可以覆盖重写
     */
    protected String getAdviceModeAttributeName() {
        return DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME;
    }

    /**
     * 该方法就是核心方法,在解析@Import注解时就会自动调用
     * <p>
     * 该方法首先会校验指定的注解中是否存在类型为AdviceMode名字为getAdviceModeAttributeName()返回值的属性
     * 然后会调用另一个selectImports(AdviceMode adviceMode)方法,该方法被子类重写,用于根据mode值判断应该使用哪些bean
     */
    @Override
    public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //获取确定的泛型类型
        Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
        Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
        //获取该泛型注解的属性集合
        AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
        if (attributes == null) {
            throw new IllegalArgumentException(String.format(
                    "@%s is not present on importing class '%s' as expected",
                    annType.getSimpleName(), importingClassMetadata.getClassName()));
        }
        //从属性集合中根据AdviceMode属性的属性名获取属性值
        AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
        //调用selectImports(AdviceMode adviceMode)方法选择合适bean,该方法被子类重写
        String[] imports = selectImports(adviceMode);
        //不能为null
        if (imports == null) {
            throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
        }
        return imports;
    }

    /**
     * 根据给定的AdviceMode确定应导入哪些类,该方法应被子类重写
     * <p>
     * 返回null将会抛出IllegalArgumentException异常.
     */
    @Nullable
    protected abstract String[] selectImports(AdviceMode adviceMode);

}

3、AutoProxyRegistrar注册自动代理创建者

  • 这个类实现了ImportBeanDefinitionRegistrar,它的作用是向容器中注册别的BeanDefinition
  • 直接关注它的registerBeanDefinitions方法即可
// 	AnnotationMetadata,代表的是AutoProxyRegistrar的导入类的元信息
// 既包含了类元信息,也包含了注解元信息
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    boolean candidateFound = false;
    // 获取@EnableTransactionManagement所在配置类上的注解元信息
    Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
    // 遍历注解
    for (String annType : annTypes) {
        // 可以理解为将注解中的属性转换成一个map
        AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
        if (candidate == null) {
            continue;
        }
        // 直接从map中获取对应的属性
        Object mode = candidate.get("mode");
        Object proxyTargetClass = candidate.get("proxyTargetClass");

        // mode,代理模型,一般都是SpringAOP
        // proxyTargetClass,是否使用cglib代理
        if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
            Boolean.class == proxyTargetClass.getClass()) {

            // 注解中存在这两个属性,并且属性类型符合要求,表示找到了合适的注解
            candidateFound = true;

            // 实际上会往容器中注册一个InfrastructureAdvisorAutoProxyCreator
            if (mode == AdviceMode.PROXY) {
                AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                if ((Boolean) proxyTargetClass) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                    return;
                }
            }
        }
    }
    // ......
}

注册核心类的方法栈

在这里插入图片描述

@EnableTransactionManagement与@EnableAspectJAutoProxy对比

  • 查看注册AspectJ自动代理类的方法调用栈
  • 调用方法一样,只是事务注册InfrastructureAdvisorAutoProxyCreator.class,AspectJ注册AnnotationAwareAspectJAutoProxyCreator.class

在这里插入图片描述

  • AopConfigUtils类查看不同自动代理注册器下标,AnnotationAwareAspectJAutoProxyCreator最大,优先注册

在这里插入图片描述

  • InfrastructureAdvisorAutoProxyCreator只会使用容器内部定义的Advisor
  • 但是AnnotationAwareAspectJAutoProxyCreator会使用所有实现了Advisor接口的通知
  • 所有后者可以覆盖(替换)前者

4、ProxyTransactionManagementConfiguration注册事务管理bean

类图:

在这里插入图片描述

AbstractTransactionManagementConfiguration抽象父类

@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {

	@Nullable
	protected AnnotationAttributes enableTx;

	@Nullable
	protected TransactionManager txManager;

	// 这个方法就是获取@EnableTransactionManagement的属性
    // importMetadata:就是@EnableTransactionManagement这个注解所在类的元信息
	@Override
	public void setImportMetadata(AnnotationMetadata importMetadata) {
        // 将EnableTransactionManagement注解中的属性对存入到map中
        // AnnotationAttributes实际上就是个map
		this.enableTx = AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
        // 这里可以看到,限定了导入的注解必须使用@EnableTransactionManagement
        if (this.enableTx == null) {
            throw new IllegalArgumentException(
                "@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
        }
	}
	
    // 我们可以配置TransactionManagementConfigurer
    // 通过TransactionManagementConfigurer向容器中注册一个事务管理器
    // 一般不会这么使用,更多的是通过@Bean的方式直接注册
	@Autowired(required = false)
	void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
		// .....
		TransactionManagementConfigurer configurer = configurers.iterator().next();
		this.txManager = configurer.annotationDrivenTransactionManager();
	}
	
    // 向容器中注册一个TransactionalEventListenerFactory
    // 这个类用于处理@TransactionalEventListener注解
    // 可以实现对事件的监听,并且在事务的特定阶段对事件进行处理
	@Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
		return new TransactionalEventListenerFactory();
	}

}

ProxyTransactionManagementConfiguration核心配置类

// proxyBeanMethods=false,意味着不对配置类生成代理对象
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
	
    // 注册了一个BeanFactoryTransactionAttributeSourceAdvisor
    // advisor就是一个绑定了切点的通知
    // 可以看到通知就是TransactionInterceptor
    // 切点会通过TransactionAttributeSource去解析@Transacational注解
    // 只会对有这个注解的方法进行拦截
	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    // BeanDefinition的角色是一个基础设施类
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
			TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
		
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(transactionAttributeSource);
		advisor.setAdvice(transactionInterceptor);
		if (this.enableTx != null) {
			advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		}
		return advisor;
	}
	
    // 注册一个AnnotationTransactionAttributeSource
    // 这个类的主要作用是用来解析@Transacational注解
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource() {
		return new AnnotationTransactionAttributeSource();
	}
	
    // 事务是通过AOP实现的,AOP的核心就是拦截器
    // 这里就是注册了实现事务需要的拦截器
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
		TransactionInterceptor interceptor = new TransactionInterceptor();
		// 将AnnotationTransactionAttributeSource注解事务属性源set到事务拦截器对象中
		interceptor.setTransactionAttributeSource(transactionAttributeSource);
		if (this.txManager != null) {
			interceptor.setTransactionManager(this.txManager);
		}
		return interceptor;
	}
}

总结:

  • ProxyTransactionManagementConfiguration类是一个@Configuration配置类
  • 通过内部的@Bean方法向容器注入一系列与AOP事务相关的一些基础bean定义
    • BeanFactoryTransactionAttributeSourceAdvisor事务通知增强器
    • AnnotationTransactionAttributeSource注解事务属性源
    • TransactionInterceptor事务拦截器(核心内容)
    • TransactionalEventListenerFactory事务事件监听器工厂

三、创建代理对象

  • 创建代理对象的时机,以及是否提前暴露对象内容与Spring AOP切面一样Spring源码解析(九):AOP源码之@Aspect所有相关注解解析
  • 对于事务代理对象,只是在拦截器链中加上事务拦截器TransactionInterceptor

在这里插入图片描述

  • 由图可知,先执行事务拦截器,再执行切面通知的拦截器,下一章节进入事务拦截器的invoke方法

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

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

相关文章

vue3使用echarts实现地图撒点、飞线等功能

echarts地图配置参考链接 链接2 vue3使用echarts map.vue <template><div class"echart-demo" id"demo"></div> </template><script setup lang"ts"> //引入echart和json数据 import * as echarts from echarts…

ffmpeg ts 关于av_seek_frame

1 ffmpeg命令行 一般对视频文件的裁剪 我们通过一行 ffmpeg命令行即可实现&#xff0c;比如 ffmpeg -ss 0.5 - t 3 - i a.mp4 vcodec copy b.mp4 其中 -ss 放置较前 开启精准seek定位 对于mp4而言 seek将从moov中相关索引表查找 0.5s时刻附近最近的关键帧 &#xff08;此描述…

systemd服务日志重定向到文件

方式一&#xff08;centos7&#xff09; [Service] ExecStart"/usr/bin/sh test.sh >> info.log 2>&1"方式二&#xff08;centos8&#xff09; StandardOutput 和 StandardError&#xff0c;用于重定向标准输出和标准错误输出 [Service] StandardOut…

k8s containerd查看镜像

直接查看crictl image会报错&#xff1a; 1) crictl config runtime-endpoint unix:///run/containerd/containerd.sock 2) vi /etc/crictl.yaml 3) systemctl daemon-reload 此时&#xff0c;再查看image:

Kettle连接数据库[MySQL]报错

在连接数据库页面填写完成后点击“测试” 报错信息&#xff1a; 错误连接数据库 [ETLqiangzi] : org.pentaho.di.core.exception.KettleDatabaseException: Error occurred while trying to connect to the databaseDriver class org.gjt.mm.mysql.Driver could not be found…

nssm nginx window 部署和开机启动服务

部署 去到Nginx官网&#xff1a;nginx news &#xff0c;然后点击“download” 在nginx的配置文件是conf目录下的nginx.conf nginx.exe http://localhost 在cmd命令窗口里面输入nginx命令(快速停止nginx) &#xff1a; nginx -s stop 或者使用(完整有序的停止nginx)命…

elasticsearch深度分页问题

一、深度分页方式from size es 默认采用的分页方式是 from size 的形式&#xff0c;在深度分页的情况下&#xff0c;这种使用方式效率是非常低的&#xff0c;比如我们执行如下查询 1 GET /student/student/_search 2 { 3 "query":{ 4 "match_all":…

航拍飞行器经营商城小程序的作用是什么

航拍人群越来越越多&#xff0c;一款靠谱的装备往往能达到预期效果&#xff0c;随着互联网信息传播度加深&#xff0c;也吸引了大批同样的爱好者加入航拍序列。 对航拍飞行器企业/经营商来说&#xff0c;市场增幅下也带来了不少商机&#xff0c;然在实际销售及客户赋能方面还是…

必备的常见芯片封装

-网友&#xff1a;这什么破封装&#xff0c;这么难焊&#xff01; -工程师&#xff1a;你才焊过几种芯片封装呀&#xff0c;SOT封装都觉得难&#xff1f; 我们常见的芯片封装&#xff1a; 第一种&#xff0c;DIP封装&#xff0c;DIP即双列直插式封装&#xff0c;引脚从芯片两…

vue-2

一、文章内容概括 1.指令补充 指令修饰符v-bind对样式增强的操作v-model应用于其他表单元素 2.computed计算属性 基础语法计算属性vs方法计算属性的完整写法成绩案例 3.watch侦听器 基础写法完整写法 4.综合案例 &#xff08;演示&#xff09; 渲染 / 删除 / 修改数量 …

tomcat安装,创建web后端项目,部署项目过程

1&#xff0c;安装服务器&#xff0c;使用 Apache免费提供的服务器TomCat&#xff0c;注意JDK版本。 TomCat官方站点 文件解压目录。 启动服务器&#xff1a;bin目录下点击startup.bat&#xff0c;出现小黑框&#xff0c;浏览器默认访问http://127.0.0.1:8080/ 关闭服务器&…

C# 通过winmm枚举音频设备

文章目录 前言一、如何实现&#xff1f;1、DllImport接口&#xff08;1&#xff09;、方法&#xff08;2&#xff09;、结构体2、定义实体3、实现枚举 二、完整代码三、使用示例总结 前言 使用C#做音频录制时需要获取音频设备信息&#xff0c;比如使用ffmpeg进行录制需要先获取…

PMP该如何备考?

我觉得最主要的就是需要打造属于自己的学习计划&#xff0c;因为每个人的学习能力是不一样的&#xff0c;没有好的学习方法&#xff0c;就会导致学习不好&#xff0c;最终获不得成绩&#xff0c;拿不下证书。 所以接下来就说一下我自己的一些学习方法&#xff0c;如对你有用的…

家政小程序开发|家政预约维修保洁系统搭建

家政预约小程序开发&#xff0c;简单易用家政服务公司小程序&#xff0c;客户&#xff0b;员工&#xff0b;派单&#xff0b;合同&#xff0b;财务&#xff0b;营销获客一键搞定&#xff01; 那么家政小程序都有什么功能&#xff0c;今天我就给大家介绍下&#xff1b; 1、地理…

自学(黑客)技术方法 必看 ——网络安全

如果你想自学网络安全&#xff0c;首先你必须了解什么是网络安全&#xff01;&#xff0c;什么是黑客&#xff01;&#xff01; 1.无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面性&#xff0c;例如 Web 安全技术&#xff0c;既有 Web 渗透2.也有 Web 防…

智能视频监控,究竟“智”在哪里?

当人们一提到智能视频监控时&#xff0c;就会想起高清摄像头、人脸识别等技术。其实不然&#xff0c;真正智能视频监控不仅仅是这些技术算法&#xff0c;更重要的是如何将这些算法融入到应用场景中&#xff0c;更好地去服务大众、起到降本增效的作用。 首先&#xff0c;智能视…

Nginx支持SNI证书,已经ssl_server_name的使用

整理了一些网上的资料&#xff0c;这里记录一下&#xff0c;供大家参考 什么是SNI&#xff1f; 传统的应用场景中&#xff0c;一台服务器对应一个IP地址&#xff0c;一个域名&#xff0c;使用一张包含了域名信息的证书。随着云计算技术的普及&#xff0c;在云中的虚拟机有了一…

自定义无边框窗口

效果&#xff1a; 可拖动拉伸 ui&#xff1a;设计如下 样式表&#xff1a;在ui CustomDialog 里设置的 #widget_title{background: #E6F1EB;border-top-left-radius: 20px;border-top-right-radius: 20px;}#widget_client{background-color: rgb(255, 255, 255);border-bottom…

游乐园票务小程序商城的作用是什么

游乐园是众多儿童喜欢的场所&#xff0c;尤其大城市&#xff0c;场所多且规模大&#xff0c;成年人也会前往&#xff0c;对园方来说自然是好的&#xff0c;然而在实际经营中&#xff0c;也会面临一些痛点。 通过【雨科】平台制作游乐园商城&#xff0c;电脑手机端小程序端打造品…

小说推文和短剧推广的收益模式

先说授权方式&#xff0c;可以使用”巨量推文“进行授权 申请授权后怎么获取收益呢 小说推文分为cpa拉新和cps推广的形式 cpa拉新的价格大概未4-10多块钱一个固定价格&#xff0c;cps则按充值比例进行分成&#xff0c;cps的充值分成比例大概60%-90%左右 短剧推广也是一样分…