Spring事务和事务的传播机制

目录

Spring中事务的实现

MySQL中的事务使用

Spring 编程式事务

TransactionTemplate 编程式事务

TransactionManager编程式事务

 Spring声明式事务

@Transactional 参数说明

事务因为程序异常捕获不会自动回滚的解决方案

@Transactional 原理

 Spring 事务隔离级别

Spring事务失效的场景

非public修饰的方法

为什么非public修饰的方法使用@Transactional会失效?

timeout超时

代码中有try/catch

调用类内部@Transactional方法

数据库不支持事务

Spring 事务传播机制

事务隔离级别和传播机制有什么区别?

① REQUIRES_NEW: 新建事务执行,如果当前存在事务,把当前事务挂起。

② REQUIRED (默认值):如果当前方法没有事务,新建一个事务,如果已经存在一个事务中,则加入到这个事务中。

 ③ NESTED(嵌套事务):如果当前存在事务,则在嵌套事务内执行。如果不存在,则执行与PROPAGATION_REQUIRED 类似的操作。

加入事务和嵌套事务有什么区别?

嵌套事务的实现原理:


Spring中事务的实现

Spring中的事务操作分为两类:

  • 编程式事务 (手动写代码操作事务)
  • 声明式事务(利用注解自动开启和提交事务)

MySQL中的事务使用

事务在MySQL有3个重要的操作:开启事务、提交事务、回滚事务,它们对应的操作命令如下:

-- 开启事务
start transaction;
-- 业务执行

-- 提交事务
commit;

-- 回滚事务
rollback;

Spring 编程式事务

Spring 手动操作事务和上面MySQL操作事务类似,他也是有3个重要操作步骤:

  • 开启事务(获取事务)
  • 提交事务
  • 回滚事务

编程式事务有两种实现方式

  • 使用 TransactionTemplate 对象实现编程式事务
  • 使用更加底层的 TransactionManager 对象实现编程式事务

TransactionTemplate 编程式事务

要使用 TransactionTemplate 对象需要先将 TransactionTemplate 注入到当前类中,然后再使用它提供的 execute 方法执行事务并返回相应的执行结果,如果程序在执行途中出现了异常,那么就可以使用代码手动回滚事务,具体实现代码如下:

TransactionManager编程式事务

TransactionManager 实现编程式事务相对麻烦一点,它需要使用两个对象TransactionManager 的子类,加上 TransactionDefinition 事务定义对象,再通过调用TransactionManager的 getTransaction 获取并开启事务,然后调用 TransactionManager 提供的 commit 方法提交事务,或使用它的另一个方法 rollback 回滚事务,它的具体实现代码如下:

SpringBoot内置了两个对象:

  • DataSourceTransactionManager: 事务的管理器, 是用来获取事务(开启事务)、提交或回滚事务的。
  • TransactionDefinition :事务的属性,在获取事务的时候需要将TransactionDefinition传递进去从而获得一个事务 TransactionStatus。

实现代码如下:

package com.example.demo.controller;


import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController // 这是@Controller + @ResponseBody 的结果
//目的是返回非页面数据 
@RequestMapping("/user")
public class UserController {
    //编程式事务
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    @Autowired
    private TransactionDefinition transactionDefinition;

    @Autowired
    private UserService userService;

    @RequestMapping("/del")
    public int del(Integer id) {
        if (id == null || id <= 0) return 0;
        TransactionStatus transactionStatus = null;
        int result = 0;
        try {
            // 1. 开启事务
            transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
            // 业务操作 删除用户
            result = userService.del(id);
            System.out.println("删除:" + result);
            dataSourceTransactionManager.commit(transactionStatus); // 提交事务

        } catch (Exception e) {
            if (transactionStatus != null) {
                dataSourceTransactionManager.rollback(transactionStatus); // 回滚事务
            }
        }
        return result;
    }
}

 Spring声明式事务

声明式事务:只需要事务的⽅法上添加@Transactional 注解就可以实现了
⽆需⼿动开启事务和提交事务,添加该注解后实现的效果如下:

  1. 进⼊⽅法时自动开启事务。
  2. 方法执行完会自动提交事务。
  3. 如果中途发⽣了没有处理的异常会自动回滚事务。
@RestController
public class UserController2 {
    @Autowired
    private UserService userService;

    @RequestMapping("/del")
    @Transactional
    public int del(Integer id) {
        if (id == null || id <= 0) return 0;
        int result = userService.del(id);
        System.out.println("删除: " + result);
     // int num = 10 / 0; 
        return result;
    }
}

 如果上面的异常触发,就会导致事务回滚,如果没有,事务则正常提交。

需要注意的一点是:这里的@Transactional 与单元测试中的@Transactional不一样。

单元测试中的@Transactional 是: 无论方法是执行完方法后,一定回滚事务。

@Transactional的作用

@Transactional 可以⽤来修饰方法或类:

  • 修饰⽅法时: 只有修饰public⽅法时才⽣效(修饰其他⽅法时不会报错,也不生效)[推荐]
  • 修饰类时: 对 @Transactional 修饰的类中所有的public方法都生效.

@Transactional 参数说明

 需要注意的是:

@Transactional 在异常被捕获的情况下,不会进行事务自动回滚:

package com.example.demo.controller;

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user2")
public class UserController2 {
    @Autowired
    private UserService userService;

    @RequestMapping("/del")
    @Transactional()
    public int del(Integer id) {
        if (id == null || id <= 0) return 0;
        int result = userService.del(id);
        System.out.println("删除: " + result);
        try {
            int num = 10 / 0;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

观察代码运行结果,可以发现:由于异常被程序捕获(try-catch),事务不会进行回滚,数据被删除:

其实这是因为 @Transactional 在底层实现的时候是通过代理类来完成的,代理类会通过反射拿到目标方法,如果目标方法出现异常会进行回滚操作,否则就提交当前事务。

因此,如果我们在程序中显示的捕获异常,那么 @Transactional 里面的代理类就无法捕获到异常,于是就提交了事务。

事务因为程序异常捕获不会自动回滚的解决方案

方案①:将异常重新抛出

对于捕获的异常,事务是会自动回滚的,因此解决方案就是将异常重新抛出:

package com.example.demo.controller;

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user2")
public class UserController2 {
    @Autowired
    private UserService userService;

    @RequestMapping("/del")
    @Transactional()
    public int del(Integer id) {
        if (id == null || id <= 0) return 0;
        int result = userService.del(id);
        System.out.println("删除: " + result);
        try {
            int num = 10 / 0;
        } catch (Exception e) {
            throw e;
        }
        return result;
    }
}

运行结果:

方案②:手动回滚事务

⼿动回滚事务,在⽅法中使⽤ TransactionAspectSupport.currentTransactionStatus() 可
以得到当前的事务,然后设置回滚⽅法 setRollbackOnly 就可以实现回滚了:

@Transactional 原理

@Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。如果⽬标对象实现了接⼝,默认情况下会采⽤ JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使⽤ CGLIB 动态代理。
@Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇到的异常,则回滚事务:

 实现细节:

 Spring 事务隔离级别

Spring 中事务隔离级别包含以下 5 种:

  • Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
  • Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
  • Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。
  • Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级别)。
  • Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。

相比于 MySQL 的事务隔离级别,Spring 的事务隔离级别只是多了⼀个 Isolation.DEFAULT(以数据库的全局事务隔离级别为主)。
Spring 中事务隔离级别只需要设置 @Transactional ⾥的 isolation 属性即可,具体实现代码如下:

Spring事务失效的场景

在开始之前,我们先要明确一个定义,什么叫做“失效”?

本文中的“失效”指的是“失去(它的)功效”,也就是当 @Transactional 不符合我们预期的结果时,我们就可以说 @Transactional 失效了。

那 @Transactional 失效的场景有哪些呢?接下来我们一一来看。

非public修饰的方法

当 @Transactional 修饰的方法为非 public 时,事务就失效了,比如以下代码当遇到异常之后,不能自动实现回滚:

@Transactional 
@RequestMapping("/save")
int save(UserInfo userInfo) {
    // 非空效验
    if (userInfo == null ||
        !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword()))
        return 0;
    // 执行添加操作
    int result = userService.save(userInfo);
    System.out.println("add 受影响的行数:" + result);
    int num = 10 / 0; // 此处设置一个异常
    return result;
}
为什么非public修饰的方法使用@Transactional会失效?

答: 这分为两层原因: 浅层原因 和 深层次原因.
1. 浅层原因
浅层原因是 @Transactional 源码限制了必须是 public 才能执行后续的代理流程,它的部分实现源码如下:

protected TransactionAttribute computeTransactionAttribute(Method method, Class<?>
targetClass) {
    // Don't allow no-public methods as required.
    // 非 public 方法,设置为 nul1
    if(allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }
    // 后面代码省略....
}

2. 深层次原因
深层次的原因 Spring Boot 的动态代理只能代理公共方法,而不能代理私有方法或受保护的方法。

这是因为 Spring 的动态代理是基于 Java 的接口代理机制或者基于 CGLib 库来实现的,而这两种代理方式都只能代理公共方法。

  • 接口代理: 当目标类实现了接口时,Spring 使用JDK 动态代理来生成代理对象。JDK 动态代理是通过生成实现目标类接口的匿名类,并将方法调用委托给目标类的实例来实现的。由于接口中的方法都是公共的,所以JDK 动态代理只能代理公共方法。
  • CGLib 代理: 当目标类没有实现接门时,Spring 使用 CGLib 动态代理来生成代理对象CGLib 动态代理是通过生成目标类的子类,并将方法调用委托给子类的实例来实现的。然而Java 中的继承要求子类能够继承父类的方法,因此 CGLib 动态代理也只能代理目标类中的公共方法。

timeout超时

当在 @Transactional 上,设置了一个较小的超时时间时,如果方法本身的执行时间超过了设置的 timeout 超时时间,那么就会导致本来应该正常插入数据的方法执行失败,示例代码如下:

@Transactional(timeout = 3) // 超时时间为 3s
@RequestMapping("/save")
int save(UserInfo userInfo) throws InterruptedException {
    // 非空效验
    if (userInfo == null ||
        !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword()))
        return 0;
    int result = userService.save(userInfo);
    return result;
}

 UserService 的 save 方法实现如下:

public int save(UserInfo userInfo) throws InterruptedException {
    // 休眠 5s
    TimeUnit.SECONDS.sleep(5);
    int result = userMapper.add(userInfo);
    return result;
}

代码中有try/catch

在前面 @Transactional 的执行流程中,我们提到:当方法中出现了异常之后,事务会自动回滚。然而,如果在程序中加了 try/catch 之后,@Transactional 就不会自动回滚事务了,示例代码如下:

@Transactional
@RequestMapping("/save")
public int save(UserInfo userInfo) throws InterruptedException {
    // 非空效验
    if (userInfo == null ||
        !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword()))
        return 0;
    int result = userService.save(userInfo);
    try {
        int num = 10 / 0; // 此处设置一个异常
    } catch (Exception e) {
    }
    return result;
}

调用类内部@Transactional方法

当调用类内部的 @Transactional 修饰的方法时,事务是不会生效的,示例代码如下:

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MyService {
    
    // 这个outerMethod方法的@Transactional无所谓有没有标注
    @Transactional
    public void outerMethod() {
        // 调用内部方法
        innerMethod();
    }

    @Transactional
    public void innerMethod() {
        // 这里的事务可能不会生效
        // ...
    }
}

说明:因为 @Transactional 是基于动态代理实现的,而当调用类内部的方法时,不是通过代理对象完成的,而是通过 this 对象实现的,这样就绕过了代理对象,从而事务就失效了。

数据库不支持事务

我们程序中的 @Transactional 只是给调用的数据库发送了:开始事务、提交事务、回滚事务的指令,但是如果数据库本身不支持事务,比如 MySQL 中设置了使用 MyISAM 引擎,那么它本身是不支持事务的,这种情况下,即使在程序中添加了 @Transactional 注解,那么依然不会有事务的行为,这就是巧妇也难为无米之炊吧。

Spring 事务传播机制

Spring 事务传播机制定义了多个包含了事务的⽅法: 相互调用时,事务是如何在这些⽅法间进⾏传递的。

比如有两个⽅法A,B都被 @Transactional 修饰,A⽅法调⽤B⽅法
A方法运行时,会开启⼀个事务.当A调⽤B时,B⽅法本⾝也有事务,此时B⽅法运⾏时,是加⼊A的事务,还是创建⼀个新的事务呢?这个就涉及到了事务的传播机制.

比如公司流程管理
执行任务之前,需要先写执行⽂档,任务执行结束,再写总结汇报。


此时A部门有⼀项⼯作,需要B部门的⽀援,此时B部门是直接使⽤A部门的⽂档,还是新建⼀个⽂档呢?
 

事务隔离级别和传播机制有什么区别?

事务的隔离级别是保证多个并发事务执行的可控性(稳定性),而事务传播机制是保证一个事务在多个调用方法间的可控性(稳定性)。

事务隔离级别描述的是多个事务同时执行时的某种行为;

而事务传播机制是描述包含了多个事务的方法在相互调用时事务的传播行为。

所以事务隔离级别描述的是纵向事务并发调用时的行为模式,而事务传播机制描述的是横向事务传递时的行为模式,如下图所示:

Spring 事务传播机制包含以下 7 种:

  1. Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
  2. Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
  3. Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。
  4. Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
  5. Propagation.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
  6. Propagation.NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
  7. Propagation.NESTED:如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。

以上 7 种传播⾏为,可以根据是否⽀持当前事务分为以下 3 类:

为了加深印象,我们这里就演示其中的三种传播机制:

调用链关系:Controller --> UserService -> LogService

Controller:

UserService: 

LogService: 

 方便演示,这里更改传播机制时候是整个调用链一起修改。

① REQUIRES_NEW: 新建事务执行,如果当前存在事务,把当前事务挂起。

预期运行结果:用户添加成功,日志添加失败。 

实际运行结果:没有添加任何记录。

 

分析:因为没有将程序异常进行处理,导致整条调用链上的事务感知到,从而全部回滚。 

改进方法,将异常捕获,进行回滚:

运行结果:符合预期,日志出现回滚,用户加入操作没有回滚。 

② REQUIRED (默认值)
:如果当前方法没有事务,新建一个事务,如果已经存在一个事务中,则加入到这个事务中。

预期运行结果:全部事务都进行回滚。

运行结果:全部事务进行回滚。

这里即使我们进行异常处理,还是出现报错的原因是因为:这里使用REQUIRED 是把这些事务看成一个整体,而事务外部觉得不应该回滚,因为没有出现异常,而事务内部进行回滚了,所以这里程序不知道什么情况,所以报500了。

但是如果是外部事务进行回滚,那么内部事务也会进行回滚。(这时候不会报异常)

 ③ NESTED(嵌套事务):如果当前存在事务,则在嵌套事务内执行。如果不存在,则执行与PROPAGATION_REQUIRED 类似的操作。

 执行结果,日志添加失败,用户添加成功。

这里的效果跟REQUIRES_NEW是很像的。

看到这里,可能有人会问:

加入事务和嵌套事务有什么区别?

加入事务 (REQUIRED) 和嵌套事务(NESTED) 都是事务传播机制中的两种传播级别,如果当前不存在事务,那么二者的行为是一致的。但如果当前存在事务:

  1. 加入事务: 遇到异常时,会回滚全部事务
  2. 嵌套事务:遇到异常时,回滚部分事务

嵌套事务之所以能回滚部分事务,是因为数据库中存在一个保存点的概念,嵌套事务相对于新建了一个保存点,如果出现异常了,那么只需要回滚到保存点即可,这样就实现了部分事务的回滚。

嵌套事务的实现原理:

嵌套事务之所以能实现部分事务的回滚,是因为在数据库中存在一个保存点(savepoint)的概念,以 MySQL 为例,嵌套事务相当于新建了一个保存点,而滚回时只回滚到当前保存点,因此之前的事务是不受影响的,这一点可以在 MySQL 的官方文档汇总找到相应的资料: https://dev.mysgl.com/doc/refman/5.7/en/savepoint.html

而 REQUIRED 是加入到当前事务中,并没有创建事务的保存点,因此出现了回滚就是整个事务回滚,这就是嵌套事务和加入事务的区别。
保存点就像玩通关游戏时的“游戏存档”一样,如果设置了游戏存档,那么即使当前关卡失败了,也能继续上一个存档点继续玩,而不是从头开始玩游戏。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Apipost 推出IDEA插件一键生成API文档

今天给大家推荐一款IDEA插件&#xff1a;Apipost-Helper-2.0&#xff0c;写完代码IDEA内一键生成API文档&#xff0c;无需安装、打开任何其他软件&#xff1b;写完代码IDEA内一键调试&#xff0c;无需安装、打开任何其他软件&#xff1b;生成API目录树&#xff0c;双击即可快速…

单片机编程原则

多任务编程的概念 方式一&#xff1a;实时操作系统&#xff08;不建议新手使用&#xff09; 方式二 &#xff1a;裸机多任务模型 逻辑多任务的基本原理 把三个任务分别分为一个一个的片段 然后先执行任务一的第一个切片 执行第二个任务的第一个片段 执行第三个任务的第一个片…

How to design a database storage model for water network information system

How to design a database storage model for water network information system 1、领域划分2、基础域2.1、概述2.2、E-R图2.3、SQL脚本 1、领域划分 序号中文名称英文名称代号备注1基础域basea012资产域assertsa023监测域monitora034水权域quotaa045灌溉域irrigationa056排涝…

JVM bash:jmap:未找到命令 解决

如果我们在使用JVM的jmap命令时遇到了"bash: jmap: 未找到命令"的错误&#xff0c;这可能是因为jmap命令没有在系统的可执行路径中。 要解决这个问题&#xff0c;可以尝试以下几种方法&#xff1a; 1. 检查Java安装&#xff1a;确保您已正确安装了Java Development …

数字孪生技术能够为旅游业带来什么新变化?

随着科技的不断发展&#xff0c;数字孪生技术逐渐融入各个行业&#xff0c;为旅游业的高质量发展提供了新的动力和可能性。数字孪生&#xff0c;即对现实世界的精确数字建模&#xff0c;通过实时数据同步、模拟分析&#xff0c;为旅游业带来了诸多创新和改变。 在数字孪生的理…

ES Kibana 简介

ES & Kibana 简介 来源&#xff1a;微信公众号《鲜枣课堂》 1998年9月4日&#xff0c;Google公司在美国硅谷成立。正如大家所知&#xff0c;它是一家做搜索引擎起家的公司。 无独有偶&#xff0c;一位名叫Doug?Cutting的美国工程师&#xff0c;也迷上了搜索引擎。他做了…

3. hdfs概述与高可用原理

简述 HDFS&#xff08;Hadoop Distributed File System&#xff09;是一种Hadoop分布式文件系统&#xff0c;具备高度容错特性&#xff0c;支持高吞吐量数据访问&#xff0c;可以在处理海量数据&#xff08;TB或PB级别以上&#xff09;的同时最大可能的降低成本。 HDFS适用于…

0门槛!用ChatGPT只花1天批量生成300个爆火TikTok视频

目录 1 2让天下没有难做的开发和运维&#xff0c;DevOps终于有了专属大模型 3关于 DevOpsGPT 1 不露脸&#xff0c;不怕视频也能从TikTok赚到钱?在过去30天&#xff0c;有人通过这个方式预计至少获得了超过1万美金的广告分成收益。这些成功账号的共同特点是&#xff0c;所有…

探究多态的原理与实现:虚函数表、动态绑定与抽象类

文章目录 一、多态概念二、多态实现&#xff08;具体&#xff09;2.1 虚函数2.2 虚函数 重写2.3 override关键字 与 final关键字2.4 重载、重写&#xff08;覆盖&#xff09;、重定义&#xff08;隐藏&#xff09;的区别 三、抽象类3.1 概念3.2 实现继承 && 接口继承 四…

通过使用阿里云服务器,搭建Java程序的运行环境

&#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Linux》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一…

ACM练习——第四天

ok&#xff0c;今天又是三节课的一天。 刚刚吃完饭的我一刻也不敢耽误&#xff0c;直接开干&#xff01;&#xff01;&#xff01; 这些题和力扣上的&#xff0c;为了练习数据结构的题是不一样的。 直接上嘴脸 题目 第一次放学 (nowcoder.com)来自牛客网 思路 先看他的输入…

8086与8088

一、8086与8088概述 8088/8086都是16位微处理器&#xff0c;内部运算器和寄存器都是16位的&#xff0c;同样具有20位地址线8088/8086都是由执行单元(EU)和总线接口部件(BIU)两大部分构成指令系统和寻址能力都相同&#xff0c;两种CPU是兼容的8088被称作准十六位的、是紧继8086…

【文件上传】upload-labs 通关

环境准备 靶场upload-labs-env-win-0.1环境windows 靶场描述 靶场攻略 Pass-01 1.上传一句话木马 发现有类型限制 2.上传图片木马 抓包 3.修改后缀 将png修改为php. 成功上传&#xff0c; 4.查找文件地址 5.中国蚁剑连接 6.成功 7.源码分析 function checkFile() {var fi…

深入理解锁

目录 常用锁策略 1.乐观锁 VS 悲观锁 2.轻量级锁 VS 重量级锁 3.自旋锁 VS 挂起等待锁 4.互斥锁 VS 读写锁 5.公平锁 VS 非公平锁 6.可重入锁 VS 可重入锁 CAS ABA问题 Synchronized原理 1. 锁升级/锁膨胀 2.锁消除 3.锁粗化 常用锁策略 1.乐观锁 VS 悲观锁 站在…

自建es数据迁移阿里云方案

一、ElasticSearch数据迁移方法介绍 https://help.aliyun.com/document_detail/170095.html?spma2c4g.26937906.0.0.429240c9ymiXGm 可以通过Logstash、reindex和OSS等多种方式完成阿里云Elasticsearch间数据迁移、Elasticsearch数据迁移至Openstore存储中、自建Elasticsear…

教你轻松保存视频号里的视频到相册

在今天的数字化社会&#xff0c;人们在各种社交平台上分享视频已经成为一种习惯。而在短视频平台上&#xff0c;用户可以轻松地上传、分享和观看各种内容丰富的视频。然而&#xff0c;很多用户在观看完善了的视频后&#xff0c;希望将喜欢的视频保存到手机相册中&#xff0c;以…

yyds!这个写文案神器爱了爱了

每次写文案都绞尽脑汁&#xff0c;不知道怎么写&#xff0c;想了很久&#xff0c;好不容易写完了&#xff0c;数据真的很差&#xff0c; 心累啊&#xff0c;家人们&#xff01;&#xff01; 只要输入你想写的标题&#xff0c;马上就能得到一篇&#xff0c;不满意就重写&#…

力扣-路径总和问题

路径总和 --简单 112. 路径总和 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 f…

MATLAB中zticks函数用法

目录 语法 说明 示例 指定 z 轴刻度值和标签 指定非均匀 z 轴刻度值 以 2 为增量递增 z 轴刻度值 将 z 轴刻度值设置回默认值 指定特定坐标区的 z 轴刻度值 删除 z 轴刻度线 zticks函数的功能是设置或查询 z 轴刻度值。 语法 zticks(ticks) zt zticks zticks(auto)…

猫罐头哪个牌子好性价比高?5款良心性价比的猫罐头推荐给新手养猫!

养猫新手很容易陷入疯狂购买的模式&#xff0c;但有些品牌真的不能乱买&#xff01;现在的市场环境不太好&#xff0c;我们需要学会控制自己的消费欲望&#xff0c;把钱花在刀刃上&#xff01;宠物市场真的很内卷&#xff0c;很多品牌都在比拼产品的数据和营养成分。很多铲屎官…