19 | 如何搞清楚事务、连接池的关系?正确配置是怎样的

事务的基本原理

在学习 Spring 的事务之前,你首先要了解数据库的事务原理,我们以 MySQL 5.7 为例,讲解一下数据库事务的基础知识。

我们都知道 当 MySQL 使用 InnoDB 数据库引擎的时候,数据库是对事务有支持的。而事务最主要的作用是保证数据 ACID 的特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),下面来一一解释。

原子性: 是指一个事务(Transaction)中的所有操作,要么全部完成,要么全部回滚,而不会有中间某个数据单独更新的操作。事务在执行过程中一旦发生错误,会被回滚(Rollback)到此次事务开始之前的状态,就像这个事务从来没有执行过一样。

一致性: 是指事务操作开始之前,和操作异常回滚以后,数据库的完整性没有被破坏。数据库事务 Commit 之后,数据也是按照我们预期正确执行的。即要通过事务保证数据的正确性。

持久性: 是指事务处理结束后,对数据的修改进行了持久化的永久保存,即便系统故障也不会丢失,其实就是保存到硬盘。

隔离性: 是指数据库允许多个连接,同时并发多个事务,又对同一个数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时,由于交叉执行而导致数据不一致的现象。而 MySQL 里面就是我们经常说的事务的四种隔离级别,即读未提交(Read Uncommitted)、读提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。

由于隔离级别是事务知识点中最基础的部分,我们就简单介绍一下四种隔离级别。但是它特别重要,你要好好掌握。

四种 MySQL 事务的隔离级别

Read Uncommitted(读取未提交内容):此隔离级别,表示所有正在进行的事务都可以看到其他未提交事务的执行结果。不同的事务之间读取到其他事务中未提交的数据,通常这种情况也被称之为脏读(Dirty Read),会造成数据的逻辑处理错误,也就是我们在多线程里面经常说的数据不安全了。在业务开发中,几乎很少见到使用的,因为它的性能也不比其他级别要好多少。

Read Committed(读取提交内容): 此隔离级别是指,在一个事务相同的两次查询可能产生的结果会不一样,也就是第二次查询能读取到其他事务已经提交的最新数据。也就是我们常说的不可重复读(Nonrepeatable Read)的事务隔离级别。因为同一事务的其他实例在该实例处理期间,可能会对其他事务进行新的 commit,所以在同一个事务中的同一 select 上,多次执行可能返回不同结果。这是大多数数据库系统的默认隔离级别(但不是 MySQL 默认的隔离级别)。

Repeatable Read(可重读): 这是 MySQL 的默认事务隔离级别,它确保同一个事务多次查询相同的数据,能读到相同的数据。即使多个事务的修改已经 commit,本事务如果没有结束,永远读到的是相同数据,要注意它与Read Committed 的隔离级别的区别,是正好相反的。这会导致另一个棘手的问题:幻读 (Phantom Read),即读到的数据可能不是最新的。这个是最常见的,我们举个例子来说明。

第一步:用工具打开一个数据库的 DB 连接,如图所示。

Drawing 0.png

查看一下数据库的事务隔离级别。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后开启一个事务,查看一下 user_info 的数据,我们在 user_info 表里面插入了三条数据,如下图所示。

Drawing 2.png

第二步:我们打开另外一个相同数据库的 DB 连接,删除一条数据,SQL 如下所示。

Drawing 3.png

当删除执行成功之后,我们可以开启第三个连接,看一下数据库里面确实少了一条 ID=1 的数据。那么这个时候我们再返回第一个连接,第二次执行 select * from user_info,如下图所示,查到的还是三条数据。这就是我们经常说的可重复读。

Drawing 4.png

Serializable(可串行化):这是最高的隔离级别,它保证了每个事务是串行执行的,即强制事务排序,所有事务之间不可能产生冲突,从而解决幻读问题。如果配置在这个级别的事务,处理时间比较长,并发比较大的时候,就会导致大量的 db 连接超时现象和锁竞争,从而降低了数据处理的吞吐量。也就是这个性能比较低,所以除了某些财务系统之外,用的人不是特别多。

数据库的隔离级别我们了解完了,并不复杂,这四种类型中,你能清楚地知道Read Uncommitted 和 Read Committed就可以了,一般这两个用得是最多的。

下面看一下数据的事务和连接是什么关系呢?

MySQL 事务与连接的关系

我们要搞清楚事务和连接池的关系,必须要先知道二者存在的前提条件。

  1. 事务必须在同一个连接里面的,离开连接没有事务可言;
  2. MySQL 数据库默认 autocommit=1,即每一条 SQL 执行完自动提交事务;
  3. 数据库里面的每一条 SQL 执行的时候必须有事务环境;
  4. MySQL 创建连接的时候默认开启事务,关闭连接的时候如果存在事务没有 commit 的情况,则自动执行 rollback 操作;
  5. 不同的 connect 之间的事务是相互隔离的。

知道了这些条件,我们就可以继续探索二者的关系了。在 connection 当中,操作事务的方式只有两种。

MySQL 事务的两种操作方式

第一种:用 BEGIN、ROLLBACK、COMMIT 来实现。

  • BEGIN开始一个事务
  • ROLLBACK事务回滚
  • COMMIT事务确认

第二种:直接用 SET 来改变 MySQL 的自动提交模式。

  • SET AUTOCOMMIT=0禁止自动提交
  • SET AUTOCOMMIT=1开启自动提交

MySQL 数据库的最大连接数是什么?

而任何数据库的连接数都是有限的,受内存和 CPU 限制,你可以通过

show variables like ‘max_connections’ 查看此数据库的最大连接数、通过 show global status like ‘Max_used_connections’ 查看正在使用的连接数,还可以通过 set global max_connections=1500 来设置数据库的最大连接数。

除此之外,你可以在观察数据库的连接数的同时,通过观察 CPU 和内存的使用,来判断你自己的数据库中 server 的连接数最佳大小是多少。而既然是连接,那么肯定会有超时时间,默认是 8 小时。

这里我只是列举了 MySQL 数据库的事务处理原理,你可以用相同的思考方式看一下你在用的数据源的事务是什么机制的。

那么学习完了数据库事务的基础知识,我们再看一下 Spring 中事务的用法和配置是什么样的。

Spring 里面事务的配置方法

由于我们使用的是 Spring Boot,所以会通过 TransactionAutoConfiguration.java 加载 @EnableTransactionManagement 注解帮我们默认开启事务,关键代码如下图所示。

Drawing 5.png

Spring 里面的事务有两种使用方式,常见的是直接通过 @Transaction 的方式进行配置,而我们打开 SimpleJpaRepository 源码类的话,会看到如下代码。

复制代码

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
...
@Transactional
@Override
public void deleteAll(Iterable<? extends T> entities) {
.....

我们仔细看源码的时候就会发现,默认情况下,所有 SimpleJpaRepository 里面的方法都是只读事务,而一些更新的方法都是读写事务。

所以每个 Respository 的方法是都是有事务的,即使我们没有使用任何加 @Transactional 注解的方法,按照上面所讲的 MySQL 的 Transactional 开启原理,也会有数据库的事务。那么我们就来看下 @Transactional 的具体用法。

默认 @Transactional 注解式事务

注解式事务又称显式事务,需要手动显式注解声明,那么我们看看如何使用。

按照惯例,我们打开 @Transactional 的源码,如下所示。

复制代码

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {@AliasFor("transactionManager")String value() default "";@AliasFor("value")String transactionManager() default "";Propagation propagation() default Propagation.REQUIRED;Isolation isolation() default Isolation.DEFAULT;int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;boolean readOnly() default false;Class<? extends Throwable>[] rollbackFor() default {};String[] rollbackForClassName() default {};Class<? extends Throwable>[] noRollbackFor() default {};String[] noRollbackForClassName() default {};
}

针对 @Transactional 注解中常用的参数,我列了一个表格方便你查看。

图片1.png

其他属性你基本上都可以知道是什么意思,下面重点说一下隔离级别和事务的传播机制。

隔离级别 Isolation isolation() default Isolation.DEFAULT:默认采用数据库的事务隔离级别。其中,Isolation 是个枚举值,基本和我们上面讲解的数据库隔离级别是一样的,如下图所示。

Drawing 6.png

propagation:代表的是事务的传播机制,这个是 Spring 事务的核心业务逻辑,是 Spring 框架独有的,它和 MySQL 数据库没有一点关系。所谓事务的传播行为是指在同一线程中,在开始当前事务之前,需要判断一下当前线程中是否有另外一个事务存在,如果存在,提供了七个选项来指定当前事务的发生行为。我们可以看 org.springframework.transaction.annotation.Propagation 这类的枚举值来确定有哪些传播行为。7 个表示传播行为的枚举值如下所示。

复制代码

public enum Propagation {REQUIRED(0),SUPPORTS(1),MANDATORY(2),REQUIRES_NEW(3),NOT_SUPPORTED(4),NEVER(5),NESTED(6);
}
  1. REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这个值是默认的。
  2. SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  3. MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  4. REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  5. NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  6. NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  7. NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED。

设置方法:通过使用 propagation 属性设置,例如下面这行代码。

复制代码

@Transactional(propagation = Propagation.REQUIRES_NEW)

虽然用法很简单,但是也有使用 @Transactional 不生效的时候,那么在哪些场景中是不可用的呢?

@Transactional 的局限性

这里列举的是一个当前对象调用对象自己里面的方法不起作用的场景。

我们在 UserInfoServiceImpl 的 save 方法中调用了带事务的 calculate 方法,代码如下。

复制代码

@Component
public class UserInfoServiceImpl implements UserInfoService {@Autowiredprivate UserInfoRepository userInfoRepository;/*** 根据UserId产生的一些业务计算逻辑*/@Override@Transactional(transactionManager = "db2TransactionManager")public UserInfo calculate(Long userId) {UserInfo userInfo = userInfoRepository.findById(userId).get();userInfo.setAges(userInfo.getAges()+1);//.....等等一些复杂事务内的操作userInfo.setTelephone(Instant.now().toString());return userInfoRepository.saveAndFlush(userInfo);}/*** 此方法调用自身对象的方法,就会发现calculate方法上面的事务是失效的*/public UserInfo save(Long userId) {return this.calculate(userId);}
}

当在 UserInfoServiceImpl 类的外部调用 save 方法的时候,此时 save 方法里面调用了自身的 calculate 方法,你就会发现 calculate 方法上面的事务是没有效果的,这个是 Spring 的代理机制的问题。那么我们应该如何解决这个问题呢?可以引入一个类 TransactionTemplate,我们看下它的用法。

TransactionTemplate 的用法

此类是通过 TransactionAutoConfiguration 加载配置进去的,如下图所示。

Drawing 7.png

我们通过源码可以看到此类提供了一个关键 execute 方法,如下图所示。

Drawing 8.png

这里面会帮我们处理事务开始、rollback、commit 的逻辑,所以我们用的时候就非常简单,把上面的方法做如下改动。

复制代码

public UserInfo save(Long userId) {return transactionTemplate.execute(status -> this.calculate(userId));
}

此时外部再调用我们的 save 方法的时候,calculate 就会进入事务管理里面去了。当然了,我这里举的例子很简单,你也可以通过下面代码中的方法设置隔离级别和传播机制,以及超时时间和是否只读。

复制代码

transactionTemplate = new TransactionTemplate(transactionManager);
//设置隔离级别
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
//设置传播机制
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
//设置超时时间
transactionTemplate.setTimeout(1000);
//设置是否只读
transactionTemplate.setReadOnly(true);

我们也可以根据 transactionTemplate 的实现原理,自己实现一个 TransactionHelper,一起来看一下。

自定义 TransactionHelper

第一步:新建一个 TransactionHelper 类,进行事务管理,代码如下。

复制代码

/*** 利用spring进行管理*/
@Component
public class TransactionHelper {/*** 利用spring 的机制和jdk8的function机制实现事务*/@Transactional(rollbackFor = Exception.class) //可以根据实际业务情况,指定明确的回滚异常public <T, R> R transactional(Function<T, R> function, T t) {return function.apply(t);}
}

第二步:直接在 service 中就可以使用了,代码如下。

复制代码

    @Autowiredprivate TransactionHelper transactionHelper;==/*** 调用外部的transactionHelper类,利用transactionHelper方法上面的@Transaction注解使事务生效*/public UserInfo save(Long userId) {return transactionHelper.transactional((uid)->this.calculate(uid),userId);}

上面我介绍了显式事务,都是围绕 @Transactional 的显式指定的事务,我们也可以利用 AspectJ 进行隐式的事务配置。

隐式事务 / AspectJ 事务配置

只需要在我们的项目中新增一个类 AspectjTransactionConfig 即可,代码如下。

复制代码

@Configuration
@EnableTransactionManagement
public class AspectjTransactionConfig {public static final String transactionExecution = "execution (* com.example..service.*.*(..))";//指定拦截器作用的包路径@Autowiredprivate PlatformTransactionManager transactionManager;@Beanpublic DefaultPointcutAdvisor defaultPointcutAdvisor() {//指定一般要拦截哪些类AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression(transactionExecution);//配置advisorDefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();advisor.setPointcut(pointcut);//根据正则表达式,指定上面的包路径里面的方法的事务策略Properties attributes = new Properties();attributes.setProperty("get*", "PROPAGATION_REQUIRED,-Exception");attributes.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");attributes.setProperty("save*", "PROPAGATION_REQUIRED,-Exception");attributes.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");attributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");//创建InterceptorTransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, attributes);advisor.setAdvice(txAdvice);return advisor;}
}

这种方式,只要符合我们上面的正则表达规则的 service 方法,就会自动添加事务了;如果我们在方法上添加 @Transactional,也可以覆盖上面的默认规则。

不过这种方法近两年使用的团队越来越少了,因为注解的方式其实很方便,并且注解 @Transactional 的方式更容易让人理解,代码也更简单,你了解一下就好了。

上面的方法介绍完了,那么一个方法经历的 SQL 和过程都有哪些呢?我们通过日志分析一下。

通过日志分析配置方法的过程

大致可以分为以下几个步骤。

第一步,我们在数据连接中加上 logger=Slf4JLogger&profileSQL=true,用来显示 MySQL 执行的 SQL 日志,如图所示。

Drawing 9.png

第二步,打开 Spring 的事务处理日志,用来观察事务的执行过程,代码如下。

复制代码

# Log Transactions Details
logging.level.org.springframework.orm.jpa=DEBUG
logging.level.org.springframework.transaction=TRACE
logging.level.org.hibernate.engine.transaction.internal.TransactionImpl=DEBUG
# 监控连接的情况
logging.level.org.hibernate.resource.jdbc=trace
logging.level.com.zaxxer.hikari=DEBUG

第三步,我们执行一个 saveOrUpdate 的操作,详细的执行日志如下所示。

Drawing 10.png

通过日志可以发现,我们执行一个 saveUserInfo 的动作,由于在其中配置了一个事务,所以可以看到 JpaTransactionManager 获得事务的过程,图上黄色的部分是同一个连接里面执行的 SQL 语句,其执行的整体过程如下所示。

  1. get connection:从事务管理里面,获得连接就 begin 开始事务了。我们没有看到显示的 begin 的 SQL,基本上可以断定它利用了 MySQL 的 connection 初始化事务的特性。
  2. set autocommit=0:关闭自动提交模式,这个时候必须要在程序里面 commit 或者 rollback。
  3. select user_info:看看 user_info 数据库里面是否存在我们要保存的数据。
  4. update user_info:发现数据库里面存在,执行更新操作。
  5. commit:执行提交事务。
  6. set autocommit=1:事务执行完,改回 autocommit 的默认值,每条 SQL 是独立的事务。

我们这里采用的是数据库默认的隔离级别,如果我们通过下面这行代码,改变默认隔离级别的话,再观察我们的日志。

复制代码

@Transactional(isolation = Isolation.READ_COMMITTED)

你会发现在开始事务之前,它会先改变默认的事务隔离级别,如图所示。

Drawing 11.png

而在事务结束之后,它还会还原此链接的事务隔离级别,又如下图所示。

Drawing 12.png

如果你明白了 MySQL 的事务原理的话,再通过日志分析可以很容易地理解 Spring 的事务原理。我们在日志里面能看到 MySQL 的事务执行过程,同样也能看到 Spring 的 TransactionImpl 的事务执行过程。这是什么原理呢?我们来详细分析一下。

Spring 事务的实现原理

这里我重点介绍一下 @Transactional 的工作机制,这个主要是利用 Spring 的 AOP 原理,在加载所有类的时候,容器就会知道某些类需要对应地进行哪些 Interceptor 的处理。

例如我们所讲的 TransactionInterceptor,在启动的时候是怎么设置事务的、是什么样的处理机制,默认的代理机制又是什么样的呢?

Spring 事务源码分析

我们在 TransactionManagementConfigurationSelector 里面设置一个断点,就会知道代理的加载类 ProxyTransactionManagementConfiguration 对事务的处理机制。关键源码如下图所示。

Drawing 13.png

而我们打开 ProxyTransactionManagementConfiguration 的话,就会加载 TransactionInterceptor 的处理类,关键源码如下图所示。

Drawing 14.png

如果继续加载的话,里面就会加载带有 @Transactional 注解的类或者方法。关键源码如下图所示。

Drawing 15.png

加载期间,通过 @Trnsactional 注解来确定哪些方法需要进行事务处理。

复制代码

o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name

而运行期间通过上面这条日志,就可以找到 JpaTransactionManager 里面通过 getTransaction 方法创建的事务,然后再通过 debuger 模式的 IDEA 线程栈进行分析,就能知道创建事务的整个过程。你可以一步一步地去断点进行查看,如下图所示。

Drawing 16.png

如上图,我们可以知道 createTransactionIfNecessary 是用来判断是否需要创建事务的,有兴趣的话你可以点击进去看看,如下图所示。

Drawing 17.png

我们继续往下面 debug 的话,就会找到创建事务的关键代码,它会通过调用 AbstractPlatformTransactionManager 里面的 startTransaction 方法开启事务,如下图所示。

Drawing 18.png

然后我们就可以继续往下断点进行分析了。断点走到最后的时候,你就可以看到开启事务的时候,必须要从我们的数据源里面获得连接。看一下断点的栈信息,这里有几个关键的 debug 点。如下图所示。

Drawing 19.png

其中,

第一处:是处理带 @Transactional 的注解的方法,利用 CGLIB 进行事务拦截处理;

第二处:是根据 Spring 的事务传播机制,来判断是用现有的事务,还是创建新的事务;

第七处:是用来判断是否现有连接,如果有直接用,如果没有就从第八处的数据源里面的连接池中获取连接,第七处的关键代码如下。

Drawing 20.png

到这里,我们介绍完了事务获得连接的关键时机,那么还需要知道它是在什么时间释放连接到连接池里面的。我们在 LogicalConnectionManagedImpl 的 releaseConnection 方法中设置一个断点,如下图所示。

Drawing 21.png

然后观察断点线性的执行方法,你会发现,在事务执行之后,它会将连接释放到连接池里面。

我们通过上面的 saveOrUpdate 的详细执行日志,可以观察出来,事务是在什么时机开启的、数据库连接是什么时机开启的、事务是在什么时机关闭的,以及数据库连接是在什么时机释放的,如果你没看出来,可以再仔细看一遍日志。

所以,Spring 中的事务和连接的关系是,开启事务的同时获取 DB 连接;事务完成的时候释放 DB 连接。通过 MySQL 的基础知识可以知道数据库连接是有限的,那么当我们给某些方法加事务的时候,都需要注意哪些内容呢?

事务和连接池在 JPA 中的注意事项

我们在“17 | DataSource 为何物?加载过程是怎样的?”中对数据源的介绍时,说过数据源的连接池不能配置过大,否则连接之前切换就会非常耗费应用内部的 CPU 和内存,从而降低应用对外提供 API 的吞吐量。

所以当我们使用事务的时候,需要注意如下 几个事项:

  1. 事务内的逻辑不能执行时间太长,否则就会导致占用 db 连接的时间过长,会造成数据库连接不够用的情况;
  2. 跨应用的操作,如 API 调用等,尽量不要在有事务的方法里面进行;
  3. 如果在真实业务场景中有耗时的操作,也需要带事务时(如扣款环节),那么请注意增加数据源配置的连接池数;
  4. 我们通过 MVC 的应用请求连接池数量,也要根据连接池的数量和事务的耗时情况灵活配置;而 tomcat 默认的请求连接池数量是 200 个,可以根据实际情况来增加或者减少请求的连接池数量,从而减少并发处理对事务的依赖。

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

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

相关文章

2018-2019 ACM-ICPC, Asia Nanjing Regional Contest G. Pyramid(组合数学 计数)

题目 t(t<1e6)组样例&#xff0c;每次给定一个n(n<1e9)&#xff0c;统计边长为n的上述三角形的等边三角形个数 其中等边三角形的三个顶点&#xff0c;可以在所有黑色三角形&白色三角形的顶点中任取&#xff0c; 答案对1e97取模 思路来源 申老师 & oeis A0003…

Unity引擎:收费模式和服务升级,为游戏开发带来更多可能性

Unity 引擎的收费模式和配套服务升级已经引起了广泛的关注和讨论。自 2024 年 1 月 1 日起&#xff0c;Unity 将根据游戏的安装量对开发者进行收费。这将会影响到很多游戏开发者和玩家。本文将探讨 Unity 引擎的收费模式和配套服务更新&#xff0c;以及对游戏开发者和玩家的影响…

报道 | 2023-2024年1月国际运筹优化会议汇总

2023年10月、11月、12月召开会议汇总&#xff1a; 2023 International Conference on Optimization and Applications (ICOA) Location: Abu Dhabi, United Arab Emirates Important dates: Conference: October 05-06, 2023 Details: https://lct.ac.ae/en/icoa/ 2023 INF…

python项目之AI动物识别工具的设计与实现(django)

项目介绍&#xff1a; &#x1f495;&#x1f495;作者&#xff1a;落落 &#x1f495;&#x1f495;个人简介&#xff1a;混迹java圈十余年&#xff0c;擅长Java、小程序、Python等。 &#x1f495;&#x1f495;各类成品java毕设 。javaweb&#xff0c;ssm&#xff0c;spring…

Java调用FFmpeg

Java调用FFmpeg 1、FFmepg基础知识1.1 下载 FFmpeg1.2 FFmpeg 工具使用 2、Java使用2.1 FFmpeg源码编译2.2 Java集成FFmpeg2.2.1 JNI2.2.2 Java调用执行 FFmpeg 工具 命令 1、FFmepg基础知识 About FFmpeg ffmpeg(计算机程序) - 百度百科 FFmpeg/FFmpeg - GitHub CSDN&#xf…

POI报表的高级应用

POI报表的高级应用 掌握基于模板打印的POI报表导出理解自定义工具类的执行流程 熟练使用SXSSFWorkbook完成百万数据报表打印理解基于事件驱动的POI报表导入 模板打印 概述 自定义生成Excel报表文件还是有很多不尽如意的地方&#xff0c;特别是针对复杂报表头&#xff0c;单…

解决react样式组合时css module动态样式失效的问题

现象&#xff1a; <button disabled{invalid} className{ "btn btn-primary btn-lg" invalid ? styles.btnDisabled : "" } > 注册 </button> 上面采用字符串拼接的方式&#xff0c;组合class&#xff0c;但是css module的动态样式style…

Bootstrap的导航菜单组件相关知识

目录 01-最基本的导航示例02-设置导航的对齐方式02-1-设置导航在水平方向上的对齐方式 02-2-设置导航在垂直方向上进行布局03-设计标签页导航04-带下拉菜单的标签页导航05-设计胶囊式导航05-1-基本的胶囊式导航05-2-带下拉菜单的胶囊式导航 06-让导航菜单项可以切换(激活导航菜…

发布npm包质量分测试

查询质量分接口 https://registry.npmjs.org/-/v1/search?textcanvas-plus v0.0.1 quality 0.2987 新建文件夹 canvas-plus 执行命令 npm init 生成package.json {"name": "3r/canvas-plus","version": "0.0.1","descript…

NewStarCTF2023week2-Unserialize?

代码审计&#xff1a; 定义了一个eval类&#xff0c;该类下有一个私有变量cmd和公有成员函数destruct()&#xff0c;该函数在对象的所有引用都被删除或类被销毁时会自动调用&#xff1b; 调用该函数则会执行一个正则表达式进行正则匹配&#xff0c;过滤掉了一些常用命令和bas…

周记之学习总结

你在人群中看到的每一个耀眼的女孩&#xff0c;都是踩着刀尖过来的。你如履平地般地舒适坦然&#xff0c;当然不配拥有任何光芒&#xff1b; 10.11-10.12 思来想去还是不舍得&#xff0c;搞了一下这个jwt&#xff0c;看了很多视频和博客&#xff0c;一直没看懂&#xff0c;两…

没有前端如何测试后端跨域问题

一、问题 前段时间对项目中的跨域做了相关的处理&#xff0c;网上有很多跨域的解决方案。前端解决&#xff0c;后端解决&#xff0c;nginx代理解决。我采用的是在后端中使用Cors来解决跨域的问题。但是前端项目还没有搭建起来&#xff0c;并不知道Cors的解决方案是否会生效&am…

Potato靶机

信息搜集 设备发现 扫描端口 综合扫描 开放了80端口的HTTP服务和7120端口的SSH服务 目录扫描 扫描目录 看看这个info.php&#xff0c;发现只有php的版本信息&#xff0c;没有可以利用的注入点 SSH突破 hydra 爆破 考虑到 7120 端口是 ssh 服务&#xff0c;尝试利用 hydra …

机器学习在工业机器人领域有哪些应用?

随着人工智能和机器学习的快速发展&#xff0c;工业机器人领域也迎来了新的机遇和挑战。本文综述了机器学习在工业机器人领域的应用&#xff0c;包括机器人视觉、运动控制、路径规划、故障诊断等方面。通过对相关研究和实际应用的分析&#xff0c;总结了机器学习在工业机器人领…

【C语言】结构体、位段、枚举、联合(共用体)

结构体 结构&#xff1a;一些值的集合&#xff0c;这些值称为成员变量。结构体的每个成员可以是不同类型的变量&#xff1b; 结构体声明&#xff1a;struct是结构体关键字&#xff0c;结构体声明不能省略struct&#xff1b; 匿名结构体&#xff1a;只能在声明结构体的时候声…

在pycharm中运行js文件,附加node.js下载步骤

文章目录 一、前言二、node.js安装和配置(如果之前就安装好了可以直接跳过)1、进入官网下载安装包2、在本地安装node.js3、环境配置4、验证是否安装成功5、修改下载位置(默认是在c盘&#xff0c;这个根据个人需求)6、设置默认模块包7、测试一下是否修改成功(要进入管理员模式的…

11 | JpaRepository 如何自定义

EntityManager 介绍 Java Persistence API 规定&#xff0c;操作数据库实体必须要通过 EntityManager 进行&#xff0c;而我们前面看到了所有的 Repository 在 JPA 里面的实现类是 SimpleJpaRepository&#xff0c;它在真正操作实体的时候都是调用 EntityManager 里面的方法。…

云上攻防-云原生篇K8s安全Config泄漏Etcd存储Dashboard鉴权Proxy暴露

文章目录 云原生-K8s安全-etcd未授权访问云原生-K8s安全-Dashboard未授权访问云原生-K8s安全-Configfile鉴权文件泄漏云原生-K8s安全-Kubectl Proxy不安全配置 云原生-K8s安全-etcd未授权访问 攻击2379端口&#xff1a;默认通过证书认证&#xff0c;主要存放节点的数据&#x…

升级包版本之后Reflections反射包在springboot jar环境下扫描不到class排查过程记录

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是「奇点」&#xff0c;江湖人称 singularity。刚工作几年&#xff0c;想和大家一同进步&#x1f91d;&#x1f91d; 一位上进心十足的【Java ToB端大厂…

卡顿分析与布局优化

卡顿分析与布局优化 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能。Android系统每隔大概16.6ms发出VSYNC信 号&#xff0c;触发对UI进行渲染&#xff0c;如果每次渲染都成功&#xff0c;这样就能够达到流畅的画面所需要的60fps&#xff0c;为了能够实现60fp…