spring 事务失效的 12 种场景

news2024/11/25 0:44:25

文章目录

  • spring 事务失效的 12 种场景
  • 一、事务不生效
    • 1.访问权限问题
    • 2. 方法用 final 修饰
    • 3.方法内部调用(自己玩自己)
      • 3.1 新加一个 Service 方法
      • 3.2 在该 Service 类中注入自己
      • 3.3 通过 AopContent 类
    • 4.Bean没有纳入Spring IOC容器管理
    • 5.多线程调用(事务方法内启动新线程进行异步操作)
    • 6.表不支持事务
    • 7.未开启事务
  • 二、事务不回滚
    • 8.错误的传播特性
    • 9.自己吞了异常(捕获异常未抛出)
    • 10.异常类型不匹配(手动抛了别的异常)
    • 11.自定义了回滚异常
    • 12.嵌套事务回滚多了
  • 三、其他
    • 1、大事务问题
    • 2.编程式事务

spring 事务失效的 12 种场景

在某些业务场景下,如果一个请求中,需要同时写入多张表的数据。为了保证操作的原子性(要么同时成功,要么同时失败),避免数据不一致的情况,我们一般都会用到 spring 事务。
确实,spring 事务用起来很方便,就用一个简单的注解: @Transactional ,就能轻松搞定事务。我猜大部分小伙伴也是这样用的,而且一直用一直爽。
但如果你使用不当,它也会坑你于无形。

[图片]

一、事务不生效

1.访问权限问题

众所周知,java 的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变大。
但如果我们在开发过程中,把某些事务方法,定义了错误的访问权限,就会导致事务功能出问题,例如:

@Service
public class UserService {
    @Transactional
    private void add(UserModel userModel) {
         saveData(userModel);
         updateData(userModel);
    }
}

我们可以看到 add 方法的访问权限被定义成了 private ,这样会导致事务失效,spring 要求被代理方法必须是public的。
说白了,在 AbstractFallbackTransactionAttributeSource 类的 computeTransactionAttribute 方法中有个判断,如果目标方法不是 public,则 TransactionAttribute 返回 null,即不支持事务。

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
    }
 // The method may be on an interface, but we need attributes from the target class.
    // If the target class is null, the method will be unchanged.
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    // First try is the method in the target class.
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
      return txAttr;
    }
    // Second try is the transaction attribute on the target class.
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
      return txAttr;
    }
    if (specificMethod != method) {
      // Fallback is to look at the original method.
      txAttr = findTransactionAttribute(method);
      if (txAttr != null) {
        return txAttr;
      }
      // Last fallback is the class of the original method.
      txAttr = findTransactionAttribute(method.getDeclaringClass());
      if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
        return txAttr;
      }
    }
    return null;
  }

也就是说,如果我们自定义的事务方法(即目标方法),它的访问权限不是 public ,而是 private、default 或 protected 的话,spring 则不会提供事务功能。

2. 方法用 final 修饰

有时候,某个方法不想被子类重写,这时可以将该方法定义成 final 的。普通方法这样定义是没问题的,但如果将事务方法定义成 final,例如:

@Service
public class UserService {
    @Transactional
    public final void add(UserModel userModel){
        saveData(userModel);
        updateData(userModel);
    }
}

我们可以看到 add 方法被定义成了 final 的,这样会导致事务失效。

为什么?

如果你看过 spring 事务的源码,可能会知道 spring 事务底层使用了 aop,也就是通过 jdk 动态代理或者 cglib,帮我们生成了代理类,在代理类中实现的事务功能。

但如果某个方法用 final 修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能。

注意:如果某个方法是 static 的,同样无法通过动态代理,变成事务方法。

3.方法内部调用(自己玩自己)

有时候我们需要在某个 Service 类的某个方法中,调用另外一个事务方法,比如:

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    public void add(UserModel userModel) {
        userMapper.insertUser(userModel);
        updateStatus(userModel);
    }
    @Transactional
    public void updateStatus(UserModel userModel) {
        doSameThing();
    }
}

我们看到在事务方法 add 中,直接调用事务方法 updateStatus。从前面介绍的内容可以知道,updateStatus 方法拥有事务的能力是因为 spring aop 生成代理了对象,但是这种方法直接调用了 this 对象的方法,所以 updateStatus 方法不会生成事务。

由此可见,在同一个类中的方法直接内部调用,会导致事务失效。

那么问题来了,如果有些场景,确实想在同一个类的某个方法中,调用它自己的另外一个方法,该怎么办呢?

3.1 新加一个 Service 方法

这个方法非常简单,只需要新加一个 Service 方法,把 @ Transactional 注解加到新 Service 方法上,把需要事务执行的代码移到新方法中。具体代码如下:

@Servcie
public class ServiceA {
   @Autowired
   prvate ServiceB serviceB;
 
   public void save(User user) {
         queryData1();
         queryData2();
         serviceB.doSave(user);
   }
 }
 
 @Servcie
 public class ServiceB {
    @Transactional(rollbackFor=Exception.class)
    public void doSave(User user) {
       addData1();
       updateData2();
    }
 
 }

3.2 在该 Service 类中注入自己

如果不想再新加一个 Service 类,在该 Service 类中注入自己也是一种选择。具体代码如下:

@Servcie
public class ServiceA {
   @Autowired
   prvate ServiceA serviceA;
 
   public void save(User user) {
         queryData1();
         queryData2();
         serviceA.doSave(user);
   }
 
   @Transactional(rollbackFor=Exception.class)
   public void doSave(User user) {
       addData1();
       updateData2();
    }
 }

可能有些人会有这样的疑问:这种做法会不会出现循环依赖问题?

答案:不会。

其实 spring ioc 内部的三级缓存保证了它不会出现循环依赖问题。

3.3 通过 AopContent 类

在该 Service 类中使用 AopContext.currentProxy() 获取代理对象。

上面的方法 2 确实可以解决问题,但是代码看起来并不直观,还可以通过在该 Service 类中使用 AOPProxy 获取代理对象,实现相同的功能。具体代码如下:

@Servcie
public class ServiceA {
 
   public void save(User user) {
         queryData1();
         queryData2();
         ((ServiceA)AopContext.currentProxy()).doSave(user);
   }
 
   @Transactional(rollbackFor=Exception.class)
   public void doSave(User user) {
       addData1();
       updateData2();
    }
 }

4.Bean没有纳入Spring IOC容器管理

在我们平时开发过程中,有个细节很容易被忽略,即使用 spring 事务的前提是:对象要被 spring 管理,需要创建 bean 实例。

通常情况下,我们通过 @Controller、@Service、@Component、@Repository 等注解,可以自动实现 bean 实例化和依赖注入的功能。

如果有一天,你匆匆忙忙地开发了一个 Service 类,但忘了加 @Service 注解,比如:

//@Service
public class UserService {
    @Transactional
    public void add(UserModel userModel) {
         saveData(userModel);
         updateData(userModel);
    }    
}

从上面的例子,我们可以看到 UserService 类没有加 @Service 注解,那么该类不会交给 spring 管理,所以它的 add 方法也不会生成事务。

5.多线程调用(事务方法内启动新线程进行异步操作)

在实际项目开发中,多线程的使用场景还是挺多的。如果 spring 事务用在多线程场景中,会有问题吗?

@Slf4j
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleService roleService;
    @Transactional
    public void add(UserModel userModel) throws Exception {
        userMapper.insertUser(userModel);
        new Thread(() -> {
            roleService.doOtherThing();
        }).start();
    }
}
 
@Service
public class RoleService {
    @Transactional
    public void doOtherThing() {
        System.out.println("保存role表数据");
    }
}

从上面的例子中,我们可以看到事务方法 add 中,调用了事务方法 doOtherThing,但是事务方法 doOtherThing 是在另外一个线程中调用的。

这样会导致两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务。如果想 doOtherThing 方法中抛了异常,add 方法也回滚是不可能的。

如果看过 spring 事务源码的朋友,可能会知道 spring 的事务是通过数据库连接来实现的。当前线程中保存了一个 map,key 是数据源,value 是数据库连接。

private static final ThreadLocal<Map<Object, Object>> resources =
 
  new NamedThreadLocal<>("Transactional resources");

我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。

6.表不支持事务

众所周知,在 mysql5 之前,默认的数据库引擎是 myisam 。

它的好处就不用多说了:索引文件和数据文件是分开存储的,对于查多写少的单表操作,性能比 innodb 更好。

有些老项目中,可能还在用它。

在创建表的时候,只需要把 ENGINE 参数设置成 MyISAM 即可:

CREATE TABLE `category` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `one_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
  `two_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
  `three_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
  `four_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin

myisam 好用,但有个很致命的问题是: 不支持事务

如果只是单表操作还好,不会出现太大的问题。但如果需要跨多张表操作,由于其不支持事务,数据极有可能会出现不完整的情况。

此外,myisam 还不支持行锁和外键。

所以在实际业务场景中,myisam 使用的并不多。在 mysql5 以后,myisam 已经逐渐退出了历史的舞台,取而代之的是 innodb

有时候我们在开发的过程中,发现某张表的事务一直都没有生效,那不一定是 spring 事务的锅,最好确认一下你使用的那张表,是否支持事务。

7.未开启事务

有时候,事务没有生效的根本原因是没有开启事务。

为什么还会没有开启事务?

没错,如果项目已经搭建好了,事务功能肯定是有的。
但如果你是在搭建项目 demo 的时候,只有一张表,而这张表的事务没有生效。那么会是什么原因造成的呢?

当然原因有很多,但没有开启事务,这个原因极其容易被忽略。

如果你使用的是 springboot 项目,那么你很幸运。因为 springboot 通过 DataSourceTransactionManagerAutoConfiguration 类,已经默默地帮你开启了事务。

你所要做的事情很简单,只需要配置 spring.datasource 相关参数即可。

但如果你使用的还是传统的 spring 项目,则需要在 applicationContext.xml 文件中,手动配置事务相关参数。如果忘了配置,事务肯定是不会生效的。

具体配置信息如下:

<!-- 配置事务管理器 --> 
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> 
    <property name="dataSource" ref="dataSource"></property> 
</bean> 
<tx:advice id="advice" transaction-manager="transactionManager"> 
    <tx:attributes> 
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes> 
</tx:advice> 
<!-- 用切点把事务切进去 --> 
<aop:config> 
    <aop:pointcut expression="execution(* com.susan.*.*(..))" id="pointcut"/> 
    <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/> 
</aop:config> 

默默地说一句,如果在 pointcut 标签中的切入点匹配规则,配错了的话,有些类的事务也不会生效。

二、事务不回滚

8.错误的传播特性

其实,我们在使用 @Transactional 注解时,是可以指定 propagation 参数的。

该参数的作用是指定事务的传播特性,spring 目前支持 7 种传播特性:
在这里插入图片描述

  • REQUIRED 如果当前上下文中存在事务,则加入该事务,如果不存在事务,则创建一个事务,这是默认的传播属性值。
  • SUPPORTS 如果当前上下文中存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。
  • MANDATORY 当前上下文中必须存在事务,否则抛出异常。
  • REQUIRES_NEW 每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
  • NOT_SUPPORTED 如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。
  • NEVER 如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。
  • NESTED 如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

如果我们在手动设置 propagation 参数的时候,把传播特性设置错了,比如:

@Service
public class UserService {
    @Transactional(propagation = Propagation.NEVER)
    public void add(UserModel userModel) {
        saveData(userModel);
        updateData(userModel);
    }
}

我们可以看到 add 方法的事务传播特性定义成了 Propagation.NEVER,这种类型的传播特性不支持事务,如果有事务则会抛异常。

目前只有这三种传播特性才会创建新事务:REQUIRED,REQUIRES_NEW,NESTED

9.自己吞了异常(捕获异常未抛出)

事务不会回滚,最常见的问题是:开发者在代码中手动 try…catch 了异常。比如:

@Slf4j
@Service
public class UserService {
    @Transactional
    public void add(UserModel userModel) {
        try {
            saveData(userModel);
            updateData(userModel);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}

这种情况下 spring 事务当然不会回滚,因为开发者自己捕获了异常,又没有手动抛出,换句话说就是把异常吞掉了。

如果想要 spring 事务能够正常回滚,必须抛出它能够处理的异常。如果没有抛异常,则 spring 认为程序是正常的。

10.异常类型不匹配(手动抛了别的异常)

即使开发者没有手动捕获异常,但如果抛的异常不正确,spring 事务也不会回滚。

@Slf4j
@Service
public class UserService {
    
    @Transactional
    public void add(UserModel userModel) throws Exception {
        try {
             saveData(userModel);
             updateData(userModel);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new Exception(e);
        }
    }
}

上面的这种情况,开发人员自己捕获了异常,又手动抛出了异常:Exception,事务同样不会回滚。

@Transactional 注解默认只处理运行时异常( RuntimeException 和 error),而不会处理受检异常( Exception 的子类)。
当抛出未被捕获的运行时异常时,Spring 会触发事务回滚操作,将之前的操作撤销;而对于未被捕获的受检异常,Spring 不会触发事务回滚操作。
如果需要处理受检异常并触发事务回滚,可以通过 rollbackFor 和 noRollbackFor 属性来指定需要回滚或不需要回滚的异常类型。

11.自定义了回滚异常

在使用 @Transactional 注解声明事务时,有时我们想自定义回滚的异常,spring 也是支持的。可以通过设置 rollbackFor 参数,来完成这个功能。

但如果这个参数的值设置错了,就会引出一些莫名其妙的问题,例如:

@Slf4j
@Service
public class UserService {  
    @Transactional(rollbackFor = BusinessException.class)
    public void add(UserModel userModel) throws Exception {
       saveData(userModel);
       updateData(userModel);
    }
}

如果在执行上面这段代码,保存和更新数据时,程序报错了,抛了 SqlException、DuplicateKeyException 等异常。而 BusinessException 是我们自定义的异常,报错的异常不属于 BusinessException,所以事务也不会回滚。

即使 rollbackFor 有默认值,但阿里巴巴开发者规范中,还是要求开发者重新指定该参数。

这是为什么呢?

因为如果使用默认值,一旦程序抛出了 Exception,事务不会回滚,这会出现很大的 bug。所以,建议一般情况下,将该参数设置成:Exception 或 Throwable。

12.嵌套事务回滚多了

public class UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleService roleService;
    @Transactional
    public void add(UserModel userModel) throws Exception {
        userMapper.insertUser(userModel);
        roleService.doOtherThing();
    }
}
 
@Service
public class RoleService {
    @Transactional(propagation = Propagation.NESTED)
    public void doOtherThing() {
        System.out.println("保存role表数据");
    }
}

这种情况使用了嵌套的内部事务,原本是希望调用 roleService.doOtherThing 方法时,如果出现了异常,只回滚 doOtherThing 方法里的内容,不回滚 userMapper.insertUser 里的内容,即回滚保存点。但事实是,insertUser 也回滚了。

why?

因为 doOtherThing 方法出现了异常,没有手动捕获,会继续往上抛,到外层 add 方法的代理方法中捕获了异常。所以,这种情况是直接回滚了整个事务,不只回滚单个保存点。

怎么样才能只回滚保存点呢?

@Slf4j
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleService roleService;
    @Transactional
    public void add(UserModel userModel) throws Exception {
        userMapper.insertUser(userModel);
        try {
            roleService.doOtherThing();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}

可以将内部嵌套事务放在 try/catch 中,并且不继续往上抛异常。这样就能保证,如果内部嵌套事务中出现异常,只回滚内部事务,而不影响外部事务。

三、其他

1、大事务问题

在使用 spring 事务时,有个让人非常头疼的问题,就是大事务问题。

通常情况下,我们会在方法上加 @Transactional 注解,添加事务功能,比如:

@Service
public class UserService { 
    @Autowired 
    private RoleService roleService; 
    @Transactional
    public void add(UserModel userModel) throws Exception {
       query1();
       query2();
       query3();
       roleService.save(userModel);
       update(userModel);
    }
 
 
@Service
public class RoleService {  
    @Autowired 
    private RoleService roleService;
    @Transactional
    public void save(UserModel userModel) throws Exception {
       query4();
       query5();
       query6();
       saveData(userModel);
    }
}

但 @Transactional 注解,如果被加到方法上,有个缺点就是整个方法都包含在事务当中了。

上面的这个例子中,在 UserService 类中,其实只有这两行才需要事务:

roleService.save(userModel);
update(userModel);

在 RoleService 类中,只有这一行需要事务:

saveData(userModel);

现在的这种写法,会导致所有的 query 方法也被包含在同一个事务当中。

如果 query 方法非常多,调用层级很深,而且有部分查询方法比较耗时的话,会造成整个事务非常耗时,而从造成大事务问题。

2.编程式事务

上面聊的这些内容都是基于 @Transactional 注解的,主要说的是它的事务问题,我们把这种事务叫做: 声明式事务

其实,spring 还提供了另外一种创建事务的方式,即通过手动编写代码实现的事务,我们把这种事务叫做: 编程式事务。例如:

 @Autowired
   private TransactionTemplate transactionTemplate;
   
   ...
   
   public void save(final User user) {
         queryData1();
         queryData2();
         transactionTemplate.execute((status) => {
            addData1();
            updateData2();
            return Boolean.TRUE;
         })
   }

在 spring 中为了支持编程式事务,专门提供了一个类:TransactionTemplate,在它的 execute 方法中,就实现了事务的功能。

相较于 @Transactional 注解声明式事务,我更建议大家使用基于 TransactionTemplate 的编程式事务。主要原因如下:

  1. 避免由于 spring aop 问题导致事务失效的问题。

  2. 能够更小粒度地控制事务的范围,更直观。

建议在项目中少使用 @Transactional 注解开启事务。但并不是说一定不能用它,如果项目中有些业务逻辑比较简单,而且不经常变动,使用@Transactional 注解开启事务也无妨,因为它更简单,开发效率更高,但是千万要小心事务失效的问题。

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

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

相关文章

时间轮奇妙旅程:深度解析Netty中的时间轮机制

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 时间轮奇妙旅程&#xff1a;深度解析Netty中的时间轮机制 前言时间轮的基本概念时间轮的工作机制netty中时间轮的实现时间轮实现细节&#xff1a;简化的源码示例&#xff1a; 时间轮的应用场景时间轮的…

调研图基础模型(Graph Foundation Models)

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 图基础模型&#xff08;Graph Foundation Models&#xff0c;简称 GFMs&#xff09; 是一种经过预训练的图大模型&#xff0c;旨在处理不同领域的图数据和任务。让我们详细探讨一下这个概念。 Github …

掌握Nodejs高级图片压缩技巧提升web优化

掌握Nodejs高级图片压缩技巧提升web优化 在当今的数字时代,图像在网络开发中发挥着至关重要的作用。它们增强视觉吸引力、传达信息并吸引用户。然而,高质量的图像通常有一个显着的缺点——较大的文件大小会减慢网页加载时间。为了应对这一挑战并确保快速加载网站,掌握 Node…

SQL注入漏洞,常用注入函数及其pakachu漏洞靶场演示

目录 SQL注入漏洞概述 SQL注入的常用函数 漏洞分类与利用 1.基于联合查询的SQL注入 &#xff12;.盲注 时间盲注&#xff08;base on bool&#xff09;​编辑 &#xff13;.宽字节注入 4.inset&#xff0f;update&#xff0f;delete注入 &#xff15;.header注入 &…

【工作向】protobuf编译生成pb.cc和pb.py文件

序言 首先通过protoc --version查看protoc版本&#xff0c;避免pb文件生成方和使用方版本不一致 1. 生成pb.cc 生成命令 protoc -I${proto_file_dir} --cpp_out${pb_file_dir} *.proto参数&#xff1a; -I表示 proto 文件的路径&#xff1b; --cpp_out 表示输出路径&#xff…

关于Mybatis-Plus报错 Not Found TableInfoCache 解决办法

0. 接口结构&#xff1a;1. 方法报错&#xff1a;2. 解决方法&#xff1a;3. 原因分析&#xff1a; 0. 接口结构&#xff1a; 【接口】&#xff1a; public interface PurchaseOrderService extends IService<PurchaseOrder> {}【接口实现类】&#xff1a; public cla…

Java项目:39 springboot007大学生租房平台的设计与实现

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 系统有管理员、房东和用户 【主要功能】 1、后台&#xff1a;房源管理、信息审批管理、订单信息管理、房东管理、用户管理 2、前台&#xff1a;注册登…

PackagesNotFoundError:学习利用报错信息找到解决方法

反思&#xff1a;之前看到报错经常是直接复制报错信息去网上搜&#xff0c;但很多情况下报错信息里其实就给出了解决方案 报错信息&#xff1a; Collecting package metadata (current_repodata.json): done Solving environment: unsuccessful initial attempt using frozen …

[LeetBook]【学习日记】有效数字——状态机

题目 有效数字 有效数字&#xff08;按顺序&#xff09;可以分成以下几个部分&#xff1a; 若干空格一个小数或者整数&#xff08;可选&#xff09;一个’e’或’E’&#xff0c;后面跟着一个整数若干空格 小数&#xff08;按顺序&#xff09;可以分成以下几个部分&#xff1a…

React Vite 构建工具如何查看代码占用体积

首先安装 Vite 中的 rollup-plugin-visualizer 插件 cnpm install rollup-plugin-visualizer 接着在你的 vite.config.ts 中引入并且使用到 plugins 中 import { visualizer } from "rollup-plugin-visualizer";export default defineConfig({plugins: [react(),vi…

JWT令牌实现登陆校验

一、JWT出现的背景 jwt令牌出现的背景&#xff0c;比如我们通过一个路由访问网站的时候&#xff0c;有些游客在知道url的情况下会跳过用户登录直接访问其他网页&#xff0c;这样不仅在逻辑上说不通&#xff08;我没登陆咋就能使用其他功能&#xff1f;&#xff09;还会造成信息…

第三天 Kubernetes进阶实践

第三天 Kubernetes进阶实践 本章介绍Kubernetes的进阶内容&#xff0c;包含Kubernetes集群调度、CNI插件、认证授权安全体系、分布式存储的对接、Helm的使用等&#xff0c;让学员可以更加深入的学习Kubernetes的核心内容。 ETCD数据的访问 kube-scheduler调度策略实践 预选与…

SSD LDPC纠错算法的重要性

固态硬盘&#xff08;Solid State Drives, SSD&#xff09;作为计算机行业中最具革命性的技术之一&#xff0c;凭借其更快的读写速度、增强的耐用性和能效&#xff0c;已经成为大多数用户的首选存储方案。然而&#xff0c;如同任何其他技术一样&#xff0c;SSD也面临自身的挑战…

SpringBoot约定大于配置

什么是约定大于配置 "约定大于配置"&#xff08;Convention Over Configuration&#xff09;是一种理念&#xff0c;旨在通过默认约定和规则来减少开发人员需要做的配置工作。在Spring Boot框架中&#xff0c;这一原则得到了充分应用&#xff0c;帮助开发者更快地构…

C++之获取Windows系统信息

目录 1. 操作系统版本 2. 获取CPU信息 3. 获取内存信息 4. 获取硬盘信息 5.获取网络接口信息 6.获取计算机名称、用户名 在C中&#xff0c;你可以使用Windows API函数来获取Windows系统的各种信息。以下是一些常见的API函数和示例代码&#xff0c;用于获取Windows系统信息…

⭐每天一道leetcode:35.搜索插入位置(简单;二分速查)

⭐今日份题目 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例1 输入: nums [1,3,5,6], target 5 输出: 2 …

计算机体系结构:VLIW

原文来自知乎 计算机体系结构&#xff1a;VLIW 本文主要介绍计算机体系结构中的VLIW&#xff0c;以供读者能够理解该技术的定义、原理、应用。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;计算机杂记 &#x1f…

8套成熟在用的三级医院信息化系统源码,HIS、LIS、PACS、智慧导诊、线上预约挂号支付系统源码

8套成熟在用的二级医院、三级医院医院管理系统源码&#xff0c;均有自主知识产权&#xff0c;应用案例&#xff0c;系统稳定运行中。可直接上手项目&#xff0c;支持二次开发 ▶ 一、SaaS模式Java语言开发的云HIS系统源码 在公立二甲医院应用三年&#xff0c;融合B/S版电子病历…

[项目设计] 从零实现的高并发内存池(四)

&#x1f308; 博客个人主页&#xff1a;Chris在Coding &#x1f3a5; 本文所属专栏&#xff1a;[高并发内存池] ❤️ 前置学习专栏&#xff1a;[Linux学习] ⏰ 我们仍在旅途 ​ 目录 6.内存回收 6.1 ThreadCache回收内存 6.2 CentralCache回收内存 Rele…

Docker安装+基础命令

一、检测、配置安装环境 &#xff08;1&#xff09;查看linux版本&#xff0c;是否符合>centos 7 &#xff08;2&#xff09;查看网络是否通畅 &#xff08;3&#xff09;安装gcc&#xff0c;gcc-c编译器 &#xff08;4&#xff09;安装device-mapper-persistent-data和lvm2…