目录

1.事务的基本概念

2.Spring事务的实现

3.事务隔离级别

4.事务传播机制


1.事务的基本概念

关于事务的一些基础概念我已经在MYSQL中讲解过了,有不了解的可以移步至此篇文章:
MySQL基础——数据库索引与事务_invictusQAQ的博客-CSDN博客

2.Spring事务的实现

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

1. 编程式事务(⼿动写代码操作事务)。

2. 声明式事务(利⽤注解⾃动开启和提交事务)。

在我们讲解他们如何使用之前,我们先来回顾一下MYSQL中事务的使用

2.1 回顾MYSQL中事务的使用

-- 开启事务
start transaction;
-- 业务执⾏
-- 提交事务
commit;
-- 回滚事务
rollback;

2.2 Spring 编程式事务

当然此种方法较为麻烦,而且实际也很少使用,所以我们仅做了解

Spring ⼿动操作事务和上⾯ MySQL 操作事务类似,它也是有 3 个重要操作步骤:

1.开启事务(获取事务)。

2.提交事务。

3.回滚事务。

SpringBoot 内置了两个对象,DataSourceTransactionManager ⽤来获取事务(开启事务)、提交或 回滚事务的,而TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从而获得⼀个事务 TransactionStatus,实现代码如下:

    @Autowiredprivate DataSourceTransactionManager dataSourceTransactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@Autowiredprivate UserService userService;@RequestMapping("/add")public int add(UserInfo userInfo){//参数合法性判断if(userInfo==null||!StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) return 0;// 开启事务(获取事务)TransactionStatus transactionStatus=dataSourceTransactionManager.getTransaction(transactionDefinition);int result= userService.add(userInfo);System.out.println("add 受影响的行数:" + result);//回滚事务dataSourceTransactionManager.rollback(transactionStatus);//或者提交事务//dataSourceTransactionManager.commit(transactionStatus);return result;}

2.3 Spring 声明式事务(自动)

声明式事务的实现很简单,只需要在需要的⽅法上添加 @Transactional 注解就可以实现了,无需手动 开启事务和提交事务,进入方法时⾃动开启事务,方法执行完会⾃动提交事务,如果中途发生了没有处 理的异常会自动回滚事务,具体实现代码如下:

  // 使用声明式事务@Transactional//@Transactional内部可以设置隔离级别等其他属性@RequestMapping("/add2")public int add2(UserInfo userInfo){if(userInfo==null||!StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) return 0;// 开启事务(获取事务)int result= userService.add(userInfo);System.out.println("add 受影响的行数:" + result);return result;}

还是之前的实现添加用户的功能,但是代码却简洁了不少

2.3.1 @Transactional 作用范围

@Transactional 可以用来修饰方法或类

修饰方法时:需要注意只能应用到 public ⽅法上,否则不⽣效。推荐此种⽤法。

修饰类时:表明该注解对该类中所有的 public ⽅法都⽣效

2.3.2 @Transactional 参数说明

前面我们提到@Transactional是可以设置多种参数的,比如下面:

而它们的具体含义如下,大家可以根据实际情况灵活去选择

2.3.3 注意事项

前面我们提到了@Transactional在事务内部出现异常时是会自动回滚的,这个也可以配合我们前面提到了@Transactional的rollbackFor等和异常处理相关的参数使用。但是假如我们使用了try-catch语句去处理了可能出现异常的代码段,那么此时即使发生了异常事务也不会回滚。

例如下面的代码,它由于使用了try-catch语句捕获异常,所以此时@Transactional就不会再去处理该异常,自然也就不会对事务进行回滚。因为Spring认为你已经使用了try-catch使用他就不会再去干涉了。

@RestController
public class UserController {@Resourceprivate UserService userService;@RequestMapping("/save")@Transactionalpublic Object save(User user) {// 插⼊数据库int result = userService.save(user);try {// 执⾏了异常代码(0不能做除数)int i = 10 / 0;} catch (Exception e) {System.out.println(e.getMessage());}return result;}
}

那么我们如何让事务在这种情况下依然能够实现自动回滚呢? 

解决方案1:对于捕获的异常,事务是会⾃动回滚的,因此解决方案1就是可以将异常重新抛出,具体实现如下:

    @RequestMapping("/save")@Transactional(isolation = Isolation.SERIALIZABLE)public Object save(User user) {// 插⼊数据库int result = userService.save(user);try {// 执⾏了异常代码(0不能做除数)int i = 10 / 0;} catch (Exception e) {System.out.println(e.getMessage());// 将异常重新抛出去throw e;}return result;}

当然此种方法将异常捕获后又重新抛出,似乎很奇怪,所以还有解决方法2。

解决方案2:⼿动回滚事务,在方法中使用 TransactionAspectSupport.currentTransactionStatus() 可以得到当前的事务,然后设置回滚方法 setRollbackOnly 就可以实现回滚了,具体实现代码如下:

    @RequestMapping("/save")@Transactional(isolation = Isolation.SERIALIZABLE)public Object save(User user) {// 插⼊数据库int result = userService.save(user);try {// 执⾏了异常代码(0不能做除数)int i = 10 / 0;} catch (Exception e) {System.out.println(e.getMessage());// ⼿动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}

2.3.4 @Transactional 工作原理

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

@Transactional 具体执行细节如下图所示:

3.事务隔离级别

3.1 事务特性回顾

1.原子性:最核心的特性,即逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败

2.一致性:保证数据一致,没有纰漏

3.持久性:只要事务执行成功,造成的修改就是可持久化保存的(保存在磁盘/硬盘中)

4.隔离性:描述多个事务并行执行所发生的情况

而这 4 种特性中,只有隔离性(隔离级别)是可以设置的

为什么要设置事务的隔离级别?

设置事务的隔离级别是⽤来保障多个并发事务执⾏更可控,更符合操作者预期的。

其实再说得通俗一点就是我们可以通过设置事务的隔离级别来控制脏读,不可重复读,幻读的现象发生与否

3.2 Spring 中设置事务隔离级别

Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进⾏设置,具体操作如下图所 示:

3.2.1 MySQL 事务隔离级别

在讲解Spring中的事务隔离级别之前我们先来回顾一下MYSQL中的事务隔离级别。

1. READ UNCOMMITTED:读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提 交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,⽽未提交的数据可能会发⽣回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。

2. READ COMMITTED:读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据, 因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间 的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。

3. REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询 的结果⼀致。但也会有新的问题,比如此级别的事务正在执⾏时,另⼀个事务成功的插⼊或者删除了某条数据,此时再去查询数据会发现突然多了或者少了部分数据,好像出现了幻觉,这就叫幻读 (Phantom Read)。

4. SERIALIZABLE:序列化,事务最⾼隔离级别,它会强制事务排序,使之不会发⽣冲突,从而解决 了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使⽤的场景并不多。

 这里我们重点区分一下不可重复读与幻读的区别:

不可重复读强调的重点在于修改(update)而幻读强调的重点在于插入和删除(insert/delete)

3.2.2 Spring 事务隔离级别

Spring 中事务隔离级别包含以下 5 种,仅仅只比MYSQL多了一种DEFAULT级别:

1. Isolation.DEFAULT:以连接的数据库的事务隔离级别为主

2. Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读

3. Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重 复读

4. Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级 别)

5. Isolation.SERIALIZABLE:串行化,可以解决所有并发问题,但性能太低

注意事项:

1.当Spring设置了事务隔离级别和连接的数据库(MYSQL)隔离级别冲突时,以Spring的隔离级别为准

2.Spring事务中的事务隔离级别机制的实现是依靠连接数据库支持的隔离级别为基础

4.Spring事务传播机制

4.1 什么是事务传播机制

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

4.2 为什么需要事务传播机制

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

直观的去理解就是事务隔离级别是保证事务并行可控性,类似于下图:

而事务传播机制是解决了事务调用链的可控性,类似于下图:

4.3 事务的传播机制有哪些

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。


我们可以通过将其分类来帮助我们记忆

4.4 Spring 事务传播机制使用和各种场景演示

首先我们搭建测试所需环境,此处我们依然选择模拟ssm项目结构,结构如下:

我们程序的执行逻辑大概如下:

UserMapper:

package com.example.springtransactiondemo.mapper;import com.example.springtransactiondemo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper {public int add(UserInfo userInfo);
}

LogMapper:

package com.example.springtransactiondemo.mapper;import com.example.springtransactiondemo.model.LogInfo;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface LogMapper {public int add(LogInfo logInfo);
}

UserService:

package com.example.springtransactiondemo.service;import com.example.springtransactiondemo.mapper.UserMapper;
import com.example.springtransactiondemo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactionalpublic int add(UserInfo userInfo){int result= userMapper.add(userInfo);return result;}
}

LogService:

package com.example.springtransactiondemo.service;import com.example.springtransactiondemo.mapper.LogMapper;
import com.example.springtransactiondemo.model.LogInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;@Service
public class LogService {@Autowiredprivate LogMapper logMapper;@Transactionalpublic int add(LogInfo logInfo) {int result = logMapper.add(logInfo);System.out.println("添加日志结果:" + result);int num=1/0;//模拟发生异常的情况return result;}
}

而为了测试我们spring的事务传播机制,最终我们UserController的执行逻辑如下:

所以我们得到了我们测试所需的UserController代码如下:

package com.example.springtransactiondemo.controller;import com.example.springtransactiondemo.model.LogInfo;
import com.example.springtransactiondemo.model.UserInfo;
import com.example.springtransactiondemo.service.LogService;
import com.example.springtransactiondemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.sql.DataSourceDefinition;@RestController
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate LogService logService;@Transactional@RequestMapping("/add")public int add3(UserInfo userInfo){//参数合法性校验if (userInfo == null ||!StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}int userResult=userService.add(userInfo);System.out.println("添加用户:" + userResult);LogInfo logInfo=new LogInfo();logInfo.setName("添加用户");logInfo.setDesc("添加用户结果:" + userResult);int logResult= logService.add(logInfo);return userResult;}
}

4.4.1 支持当前事务(REQUIRED)

以下代码实现中,先开启事务先成功插⼊⼀条⽤户数据,然后再执行日志报错,而在日志报错是发⽣了 异常,观察 propagation = Propagation.REQUIRED 的执行结果。

这里的测试代码我们在上面已经放出来了,就不在赘述。而得到的结果是程序报错,数据库没有插入任何数据。

执行流程描述:

1. UserService 中的保存⽅法正常执⾏完成。

2. LogService 保存日志程序报错,因为使⽤的是 Controller 中的事务,所以整个事务回滚。 3. 数据库中没有插入任何数据,也就是步骤 1 中的用户插⼊⽅法也回滚了。

4.4.2 不支持当前事务(REQUIRES_NEW)

UserController 类中的代码不变,将添加用户和添加日志的方法修改为 REQUIRES_NEW 不⽀持当前事务,重新创建事务,观察执行结果:

此处我们的LogService依然模拟抛出异常,但此次用户数据插入成功,日志数据插入失败被回滚了。说明REQUIRES_NEW创建的两个新事务是独立的,不会互相影响。

4.4.3 NESTED 嵌套事务

UserController 类中的代码不变,将添加用户和添加日志的方法修改为 Propagation.NESTED 嵌套事务,重新创建事务,观察执行结果:

此处我们的LogService依然模拟抛出异常,此次用户数据插入成功,日志数据插入失败被回滚了。说明嵌套事务是可以部分回滚的,没有因为嵌套事务抛出异常而导致同一层的嵌套事务以及外层的事务报错回滚。

4.5 事务传播机制的相关注意事项

4.5.1 REQUIRED / REQUIRES_NEW / NESTED的区别

REQUIRED会加入当前的事务,此时可以看作两个事务融为一体,其中一个事务报错另一个也会一起陪它回滚。而REQUIRES_NEW则是创建了新的事务,和别的事务没有关系,所以同层次的另一个事务报错不影响该事务继续执行。而NESTED嵌套事务 进入之后相当于新建了⼀个保存点,滚回时只回滚到当前保存点,因此之前的事务是不受影响的,所以可以实现部分回滚。

4.5.2 有关事务自调用问题

以下部分内容来自一个@Transactional哪里来这么多坑?_Foo.的博客-CSDN博客

部分内容根据自己的理解进行了修改

@Service
public class DmzService {public void saveAB(A a, B b) {saveA(a);saveB(b);}@Transactionalpublic void saveA(A a) {dao.saveA(a);}@Transactionalpublic void saveB(B b){dao.saveB(a);}
}

上面三个方法都在同一个类DmzService中,其中saveAB方法中调用了本类中的saveAsaveB方法,这就是自调用。在上面的例子中saveAsaveB上的事务会失效

那么自调用为什么会导致事务失效呢?我们知道Spring中事务的实现是依赖于AOP的,当容器在创建dmzService这个Bean时,发现这个类中存在了被@Transactional标注的方法(修饰符为public)那么就需要为这个类创建一个代理对象并放入到容器中,创建的代理对象等价于下面这个类:

public class DmzServiceProxy {private DmzService dmzService;public DmzServiceProxy(DmzService dmzService) {this.dmzService = dmzService;}public void saveAB(A a, B b) {dmzService.saveAB(a, b);}public void saveA(A a) {try {// 开启事务startTransaction();dmzService.saveA(a);} catch (Exception e) {// 出现异常回滚事务rollbackTransaction();}// 提交事务commitTransaction();}public void saveB(B b) {try {// 开启事务startTransaction();dmzService.saveB(b);} catch (Exception e) {// 出现异常回滚事务rollbackTransaction();}// 提交事务commitTransaction();}
}

简单的说就是我们理想情况下应该是先通过代理类然后采取调用目标方法,但实际上我们会通过this来直接调用目标方法,也就是说此时我们没有经过代理类而是直接通过this来调用,此时就导致了代理失效的问题。

常见的自调用导致的事务失效还有一个例子,如下:

@Service
public class DmzService {@Transactionalpublic void save(A a, B b) {saveB(b);}@Transactional(propagation = Propagation.REQUIRES_NEW)public void saveB(B b){dao.saveB(a);}
}

当我们调用save方法时,我们预期的执行流程是这样的:

但还是由于this的存在,所以我们实际上还是会不经过代理去直接通过this调用目标方法导致事务失效。

4.5.3 关于REQUIRES_NEW的补充

REQUIRES_NEW官方文档解释:
Create a new transaction, and suspend the current transaction if one exists.
意思是,创建一个新事务,如果当前存在事务,将这个事务挂起。也就是说如果当前存在事务,那么将当前的事务挂起,并开启一个新事务去执行REQUIRES_NEW标志的方法。

但是当它在嵌套调用的时候有几点我们是需要注意的:

1.标志REQUIRES_NEW会新开启事务,外层事务不会影响内部事务的提交/回滚
2.标志REQUIRES_NEW的内部事务的异常,会影响外部事务的回滚

我们也可以结合NESTED一起来记忆

REQUIRES_NEW 和 NESTED ,前者是内层异常影响外层,外层不影响内层;后者正好相反,内层加try catch后 异常不影响外层,外层会影响内层。

4.5.4 REQUIRES与REQUIRES_NEW嵌套场景简单总结

1.同类中:无事务方法 嵌套 事务方法 ,事务不生效,因spring 中事务基于代理实现。详细原理请见4.5.2

2.同类中:REQUIRES 嵌套 REQUIRES_NEW ,REQUIRES_NEW不生效,加入到REQUIRES事务中,原理同上。

3.同类中:REQUIRES(1) 嵌套 REQUIRES (2),其实REQUIRES(2)事务注解也是不生效的,只是加入到REQUIRES(1)事务中,看起来REQUIRES(2)事务注解是生效的,原理同上。

4.同类中:REQUIRES(1) 嵌套 REQUIRES或REQUIRES_NEW (2),(1) 中 try {(2)}catch{e.printStackTrace();} , 2异常,1与2都不会回滚,因为2实际无事务,异常也被1 catch,故都不回滚。

5.不同类中:REQUIRES 嵌套 REQUIRES_NEW,情况1:REQUIRES无异常,REQUIRES_NEW发生异常,REQUIRES与REQUIRES_NEW都回滚。

6.不同类中:REQUIRES 嵌套 REQUIRES_NEW,情况2:REQUIRES(1)发生异常,REQUIRES_NEW(2)无异常,1的事务回滚,2的事务正常提交。这种情况在4.5.3提及过。

7.不同类中:REQUIRES(1)嵌套 REQUIRES(2),(1) 中 try {(2)}catch{e.printStackTrace();} , 2异常 , 1与2都会回滚(尽管catch了),因为1,2在同一事务中,发生异常Rolling back,故都回滚(同一事务中,要么都提交,要么都不提交)

8.不同类中:REQUIRES(1)嵌套 REQUIRES_NEW(2),(1) 中 try {(2)}catch{e.printStackTrace();} , 2异常, 1不回滚,2回滚,因为2在新事务中,发生异常Rolling back,且异常被1catch不被1感知,故1不回滚,2回滚。

Spring事务与事务传播机制相关推荐

  1. Spring事务传播机制大白话(使用springboot,注解演示)

    1. 我对传播机制的理解 为什么需要传播机制? 因为事务之间可能存在相互调用,例如service业务层的方法存在相互调用,如果相互调用的方法都开启了事务(对应到springboot就是在方法上要添加@ ...

  2. java spring 事务传播_spring事务传播机制实例讲解

    天温习spring的事务处理机制,总结如下 对于SQL事务的概念以及ACID性质,可以参见我的另一篇博文 http://kingj.iteye.com/admin/blogs/1675011 spri ...

  3. Spring 事务传播机制 实例讲解

    事务传播机制 对于SQL事务的概念以及ACID性质,可以参见我的另一篇博文 http://kingj.iteye.com/admin/blogs/1675011 spring的管理的事务可以分为如下2 ...

  4. java事务传播机制事例_Spring事务传播机制

    addBook()代码 Java代码  收藏代码 public void addBook() throws Exception{ this.jdbcTemplate.execute(ADD_BOOK) ...

  5. 第09篇:Spring声明式事务的实现方式

    作者: 西魏陶渊明 博客: https://blog.springlearn.cn/ 西魏陶渊明 本篇文章是对Mybatis知识点的一个扩展,主要一起来研究下Spring是如何来管理事务的.顺便再多聊 ...

  6. Spring事务及事务不生效的原因

    目录 注解 `@Transactional` 的属性参数 `Spring` 中事务的传播机制 `Spring` 中事务的隔离级别 常用数据库的隔离级别 `readOnly` 事务的读写性 事务的只读性 ...

  7. spring中的事务到底是什么

    spring事务 事务在代码里或者数据库中都可以配置. 其含义理解为 一系列的数据操作,要么全部执行完成.要么都不执行.归纳为 1.原子性:事务是一个原子操作,由一系列动作组成.事务的原子性确保动作要 ...

  8. spring上下文是什么意思_Java程序员只会CRUD连Spring事务传播机制都不懂?

    AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东西,面试完就 ...

  9. 原创 | CRUD更要知道的Spring事务传播机制

    来自:肥朝 AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东 ...

  10. Spring事务传播机制和隔离级别

    Spring有5种隔离级别,7种传播行为.这是面试常问的内容,也是代码中经常碰到的知识点.这些知识枯燥而且乏味,其中有些非常的绕.如果栽在这上面,就实在是太可惜了. @Transactional(is ...

最新文章

  1. MySQL优化篇:锁机制
  2. Mysql 索引优化分析_如何优化MySQL的性能?从索引方面优化案例分析
  3. Ubuntu安装搜狗输入法
  4. python学习-知识点进阶使用(end、while else、range、iter、list的多种遍历方式)
  5. 第5章 Python 数字图像处理(DIP) - 图像复原与重建10 - 空间滤波 - 统计排序滤波器 - 中值、最大值、最小值、中点、修正阿尔法均值滤波器
  6. 数据结构之平衡树:2-3查找树的介绍——16
  7. SpringBoot2基础,进阶,数据库,中间件等系列文章目录分类
  8. 微信小程序入门二:底部导航tabBar
  9. django 如何接收bootstrap-table传送的 ajax数组
  10. 开发者论坛一周精粹(第六期):阿里B2B研发管理难题如何应对?打造强有力的技术中台...
  11. 使用R语言中的GWmodel进行GWR模型的运算
  12. 家用计算机常见故障及解决方式,电脑常见故障及处理方法汇总
  13. anaconda base环境的地址_Anaconda安装及环境管理
  14. 路由来源、优先级和度量值
  15. LeoCAD:用于创建虚拟乐高模型的开源CAD程序
  16. 武林外传之勇夺金掌柜 【安卓游戏】
  17. 【IPS安全策略配置】
  18. Win10下kafka简单安装及使用
  19. 沪深A股分析数据机构持股信息API接口(JSON标准格式,Get请求方式)
  20. Reflex WMS入门系列二十三:几个库存相关的报表

热门文章

  1. 【验证码逆向专栏】某验三代滑块验证码逆向分析
  2. 【mycat】mycat安装
  3. 递归求阶乘和--PTA
  4. ICS中的6种知名攻击
  5. 虚拟机怎么安装gho系统
  6. 【微信小程序】提示框、选择框、选择菜单
  7. 角形级联h桥,不平衡电网,负序抑制,角形级联h桥statcom
  8. 班级纪念册php源码,6篇发自内心的同学班级纪念册后记,毕业同学录纪念册设计卷尾语...
  9. layui框架是什么?
  10. 图片批量重命名编号,一键处理的方法!