我为了准备美团的面试,爆肝60张图画明白了Spring事务!

61张图,图解Spring事务,拆解底层源码 我们再小节一下,文章先介绍了事务的使用示例,以及事务的执行流程。先匹配出 louzai 对象所有关于事务的切面列表,并将匹配成功的事务属性保存到缓存;从缓存取出事务属性,然后创建、启动事务,执行业务逻辑,最后提交或者回滚事务。 阅读详情

点击上方“芋道源码”,选择“设为星标”

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

 

来源:楼仔

91d84e9df134a60025b10c26b9ffdc96.jpeg


本文会简单介绍一下 Spring 事务的基础知识,以及使用方法,然后直接对源码进行拆解。

不 BB,上文章目录。

924f4b6199e0da7abc138c626fc82d7c.png

1. 项目准备

需要搭建环境的同学,代码详见 :https://github.com/lml200701158/program_demo/tree/main/spring-transaction

下面是 DB 数据和 DB 操作接口:

uidunameusex
1张三女
2陈恒男
3楼仔男
// 提供的接口
public interface UserDao {
    // select * from user_test where uid = "#{uid}"
    public MyUser selectUserById(Integer uid);
    // update user_test set uname =#{uname},usex = #{usex} where uid = #{uid}
    public int updateUser(MyUser user);
}

基础测试代码,testSuccess() 是事务生效的情况:

@Service
public class Louzai {
    @Autowired
    private UserDao userDao;

    public void update(Integer id) {
        MyUser user = new MyUser();
        user.setUid(id);
        user.setUname("张三-testing");
        user.setUsex("女");
        userDao.updateUser(user);
    }

    public MyUser query(Integer id) {
        MyUser user = userDao.selectUserById(id);
        return user;
    }

    // 正常情况
    @Transactional(rollbackFor = Exception.class)
    public void testSuccess() throws Exception {
        Integer id = 1;
        MyUser user = query(id);
        System.out.println("原记录:" + user);
        update(id);
        throw new Exception("事务生效");
    }
}

执行入口:

public class SpringMyBatisTest {
    public static void main(String[] args) throws Exception {
        String xmlPath = "applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        Louzai uc = (Louzai) applicationContext.getBean("louzai");
        uc.testSuccess();
    }
}

输出:

16:44:38.267 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
        16:44:38.363 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'txManager'
        16:44:40.966 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Creating new transaction with name [com.mybatis.controller.Louzai.testSuccess]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
        16:44:40.968 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/java_study?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai]
        16:44:41.228 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@5b5caf08] for JDBC transaction
        16:44:41.231 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5b5caf08] to manual commit
        原记录:MyUser(uid=1, uname=张三, usex=女)
        16:42:59.345 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction rollback
        16:42:59.346 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@70807224]
        16:42:59.354 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@70807224] after transaction
        Exception in thread "main" java.lang.Exception: 事务生效
        at com.mybatis.controller.Louzai.testSuccess(Louzai.java:34)
// 异常日志省略...

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

2. Spring 事务工作流程

为了方便大家能更好看懂后面的源码,我先整体介绍一下源码的执行流程,让大家有一个整体的认识,否则容易被绕进去。

整个 Spring 事务源码,其实分为 2 块,我们会结合上面的示例,给大家进行讲解。

6b5b48acc0a785a22bb069d7629c2028.png

第一块是后置处理 ,我们在创建 Louzai Bean 的后置处理器中,里面会做两件事情:

获取 Louzai 的切面方法 :首先会拿到所有的切面信息,和 Louzai 的所有方法进行匹配,然后找到 Louzai 所有需要进行事务处理的方法,匹配成功的方法,还需要将事务属性保存到缓存 attributeCache 中。

创建 AOP 代理对象 :结合 Louzai 需要进行 AOP 的方法,选择 Cglib 或 JDK,创建 AOP 代理对象。

844b93b2c99a3d02a6c12e5196e3e7be.png

第二块是事务执行 ,整个逻辑比较复杂,我只选取 4 块最核心的逻辑,分别为从缓存拿到事务属性、创建并开启事务、执行业务逻辑、提交或者回滚事务。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

3. 源码解读

注意:Spring 的版本是 5.2.15.RELEASE,否则和我的代码不一样!!!

上面的知识都不难,下面才是我们的重头戏,让你跟着楼仔,走一遍代码流程。

3.1 代码入口

6175829c91ff804bed55832aa8aa2479.png 85bbda95bdda9b82ec19795eaded4652.png

这里需要多跑几次,把前面的 beanName 跳过去,只看 louzai。

225cbe3104fb9c04f8defe60142fe1bf.png 2c6b2f657c813f793c8b5e862b8a9329.png

进入 doGetBean(),进入创建 Bean 的逻辑。

067ea2225d28dcc01e8f41eab3937914.png

进入 createBean(),调用 doCreateBean()。

b03e774300116921785ffc9773b8021f.png

进入 doCreateBean(),调用 initializeBean()。

4c007bbb626754c38cb3550160f01498.png 3034fea0fa9dc544f3aa3f84e8319177.png 9f55ffe55b222b7197bf735525569efe.png e6367f137c1d95b3d0ebcafa37aca184.png

如果看过我前面几期系列源码的同学,对这个入口应该会非常熟悉,其实就是用来创建代理对象。

3.2 创建代理对象

1433f440fbf28d91862e2b31ce969672.png

这里是重点!敲黑板!!!

  1. 先获取 louzai 类的所有切面列表;

  2. 创建一个 AOP 的代理对象。

8388ef906a45e1d183ab9ec54e7895ba.png
3.2.1 获取切面列表
e03259ea7330b6ff836d45d9f28b1e0c.png

这里有 2 个重要的方法,先执行 findCandidateAdvisors(),待会我们还会再返回 findEligibleAdvisors()。

3b7bab8ecf80c0e0eaea9dc750e8e61e.png ee9b0b596913a7c0772b52481e7c8b4d.png 62d9f84340a940065d1acbbd7b88eb31.png 3e5ff7226023da79de11e59a1f2cde13.png

依次返回,重新来到 findEligibleAdvisors()。

d4b7eef5d83ff3bda355903d061b7eeb.png abd2b20ae9f486448a6ebbe53bcc1ad5.png a2606d0fe569ec2efc7250ea34d73107.png e9e47aab125c2466d3afbeb02e0e983e.png

进入 canApply(),开始匹配 louzai 的切面。

1eaf71685e1cd42ac840382e48b341ab.png

这里是重点!敲黑板!!!

这里只会匹配到 Louzai.testSuccess() 方法,我们直接进入匹配逻辑。

9c54341731c4fde9b2dddd7fb25a5a01.png

如果匹配成功,还会把事务的属性配置信息放入 attributeCache 缓存。

f6b0fa4bd2c0b54a3e4d6889d77d94d1.png 02b472a9b72e55fe33728b2e10946873.png 846bd0ad177f423d9095622d5b0e3160.png 77381b515604a0dfef2f897d9762e366.png 90bd728885279251f810805996863a63.png 4599e822e6e9ed88153032cc72276543.png

我们依次返回到 getTransactionAttribute(),再看看放入缓存中的数据。

3f73f2942d8c68301a9ca91f7caa2989.png

再回到该小节开头,我们拿到 louzai 的切面信息,去创建 AOP 代理对象。

9961aaef884eb1061c528ea2dc5c7951.png
3.2.2 创建 AOP 代理对象

创建 AOP 代理对象的逻辑,在上一篇文章(Spring AOP)讲解过,我是通过 Cglib 创建。

3.3 事务执行

回到业务逻辑,通过 louzai 的 AOP 代理对象 ,开始执行主方法。

5e320aad31cc0d7bf1b60071993c6d8c.png

因为代理对象是 Cglib 方式创建,所以通过 Cglib 来执行。

5b8229550def795d0017c5a3208a27db.png aa80d2b0056696e835e20455b94aff97.png d7fd5c2ffb2beabc574ef343e73e2891.png 9ab0f083eafa1e340e8d51445557124c.png

这里是重点!敲黑板!!!

下面的代码是事务执行的核心逻辑 invokeWithinTransaction()。

84613b1c5bb4e80bf7780f311d82e39e.png
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {

        //获取我们的事务属源对象
        TransactionAttributeSource tas = getTransactionAttributeSource();
//通过事务属性源对象获取到我们的事务属性信息
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//获取我们配置的事务管理器对象
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
//从tx属性对象中获取出标注了@Transactionl的方法描述符
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

        //处理声明式事务
        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        //有没有必要创建事务
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

        Object retVal;
        try {
        //调用钩子函数进行回调目标方法
        retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
        //抛出异常进行回滚处理
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
        }
        finally {
        //清空我们的线程变量中transactionInfo的值
        cleanupTransactionInfo(txInfo);
        }
        //提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
        }
        //编程式事务
        else {
        // 这里不是我们的重点,省略...
        }
        }
3.3.1 获取事务属性

在 invokeWithinTransaction() 中,我们找到获取事务属性的入口。

9404c017777a64b004df1402912d389e.png

从 attributeCache 获取事务的缓存数据,缓存数据是在 “2.2.1 获取切面列表” 中保存的。

1385b1bff0d0b63f3daad96a09f3ab4c.png
3.3.2 创建事务
35012d1f6323aca270092266e0deba88.png 9e69e61c487c16ccff7f88fdb9bf82fc.png 5e577386ed70eb646f4ea6c7d0b83b21.png

通过 doGetTransaction() 获取事务。

protected Object doGetTransaction() {
        //创建一个数据源事务对象
        DataSourceTransactionObject txObject = new DataSourceTransactionObject();
        //是否允许当前事务设置保持点
        txObject.setSavepointAllowed(isNestedTransactionAllowed());
        /**
         * TransactionSynchronizationManager 事务同步管理器对象(该类中都是局部线程变量)
         * 用来保存当前事务的信息,我们第一次从这里去线程变量中获取 事务连接持有器对象 通过数据源为key去获取
         * 由于第一次进来开始事务 我们的事务同步管理器中没有被存放.所以此时获取出来的conHolder为null
         */
        ConnectionHolder conHolder =
        (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
        txObject.setConnectionHolder(conHolder, false);
        //返回事务对象
        return txObject;
        }

通过 startTransaction() 开启事务。

32ca684acee14bea47a1c5b4d7e7ecc6.png

下面是开启事务的详细逻辑,了解一下即可。

protected void doBegin(Object transaction, TransactionDefinition definition) {
        //强制转化事务对象
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        Connection con = null;

        try {
        //判断事务对象没有数据库连接持有器
        if (!txObject.hasConnectionHolder() ||
        txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
        //通过数据源获取一个数据库连接对象
        Connection newCon = obtainDataSource().getConnection();
        if (logger.isDebugEnabled()) {
        logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
        }
        //把我们的数据库连接包装成一个ConnectionHolder对象 然后设置到我们的txObject对象中去
        txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }

        //标记当前的连接是一个同步事务
        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
        con = txObject.getConnectionHolder().getConnection();

        //为当前的事务设置隔离级别
        Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);

        //关闭自动提交
        if (con.getAutoCommit()) {
        txObject.setMustRestoreAutoCommit(true);
        if (logger.isDebugEnabled()) {
        logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
        }
        con.setAutoCommit(false);
        }

        //判断事务为只读事务
        prepareTransactionalConnection(con, definition);
        //设置事务激活
        txObject.getConnectionHolder().setTransactionActive(true);

        //设置事务超时时间
        int timeout = determineTimeout(definition);
        if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
        txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
        }

        // 绑定我们的数据源和连接到我们的同步管理器上   把数据源作为key,数据库连接作为value 设置到线程变量中
        if (txObject.isNewConnectionHolder()) {
        TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
        }
        }

        catch (Throwable ex) {
        if (txObject.isNewConnectionHolder()) {
        //释放数据库连接
        DataSourceUtils.releaseConnection(con, obtainDataSource());
        txObject.setConnectionHolder(null, false);
        }
        throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
        }
        }

最后返回到 invokeWithinTransaction(),得到 txInfo 对象。

48e3e9e44610b51eed2925c9c6325dfe.png
3.3.3 执行逻辑

还是在 invokeWithinTransaction() 中,开始执行业务逻辑。

407ffcb3a7b0cc97a33fbb64e49404eb.png 7494b815fb19b4bdd3e9946f5cc61d4f.png 018d1a322c990f4044d7560872ec0e76.png f9a28692acf7b69ff2183048fdffe73b.png c36aa588886e3587915a5deca36c5411.png

进入到真正的业务逻辑。

51058efa2562cf25c6ae46ee61d5c28c.png

执行完毕后抛出异常,依次返回,走后续的回滚事务逻辑。

3.3.4 回滚事务

还是在 invokeWithinTransaction() 中,进入回滚事务的逻辑。

26d1dac73f476df645f26e45dce397fa.png。

b7819652b5832523adc9d164ba119828.png

执行回滚逻辑很简单,我们只看如何判断是否回滚。

0a79fb5c24dd16f65949e9da9cf8326b.png 2a88a39849d747406dc3edb1bbad7e7b.png c149946f254a75735231ceff0fdfdbd1.png

如果抛出的异常类型,和事务定义的异常类型匹配,证明该异常需要捕获。

之所以用递归,不仅需要判断抛出异常的本身,还需要判断它继承的父类异常,满足任意一个即可捕获。

5ebc355304f05d782e29d2f7f554909b.png

到这里,所有的流程结束。

4. 结语

我们再小结一下,文章先介绍了事务的使用示例,以及事务的执行流程。

之后再剖析了事务的源码,分为 2 块:

  • 先匹配出 louzai 对象所有关于事务的切面列表,并将匹配成功的事务属性保存到缓存;

  • 从缓存取出事务属性,然后创建、启动事务,执行业务逻辑,最后提交或者回滚事务。



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢:

261fdec2b62c6e0d96bc89330c4494d3.png

已在知识星球更新源码解析如下:

af88774e69263f89a787e5cfef796fed.jpeg

f1457def5b5afd7d68780c22530e4d3f.jpeg

3b6d93a8f1fd0598d2e19c46bc91f46c.jpeg

b9dbc1f10bc8ae7446a2c171dae18fce.jpeg

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
从空调温控到信号降噪:一阶RC低通滤波器在Arduino和STM32上的C语言实现指南 本文详细介绍了如何在Arduino和STM32平台上使用C语言实现一阶RC低通滤波器,涵盖从基础理论到实际应用的完整流程。通过具体代码示例和优化技巧,帮助开发者有效处理传感器噪声、信号平滑等问题,提升嵌入式系统的信号处理能力。 阅读详情

相关推荐

从零开始学嵌入式之STM32——1.什么是STM32

它是ST公司推出的一系列单片机的统称。ST----指的是ST公司----意法半导体。意法半导体,成立于1987年,是由意大利的SGS微电子公司和法国的Thomson半导体公司合并而成。ARM目前主要是ARM架构处理器及外围组件的电路设计方案,知识产权授权的形式收取费用,其他公司根据ARM的方案设计出CPU产品。,它以应用为核心,软硬件可裁剪,旨在实现对设备的控制、监测或辅助功能,区别于通用计算机(如 PC、服务器)的 “一机多用” 属性。在嵌入式领域,目前主流的是cortex-M3内核推出的芯片。

qshdyy的博客 1852

一图了解Spring框架

一图了解Spring框架

DN金猿的博客 336

PCB布局规范及其注意事项(纯干货)

这样做的好处是信号1,3层不会有串扰,4和5作为电源有紧密的耦合,3个信号层都有GND平面可以参考,且不会互相产生串扰,唯一的缺点是布线的层面只有3个信号层(信号03的是最适合来传输高速信号,因为该层的二面都有屏蔽,防止干扰传出,也防止干扰传入)33、影响阻抗匹配的参数(ER介电常数(1GHZ以下默认为4.2,以上的话问板厂),H介质厚度(就是内层压合的PP片厚度),W走线的线宽,T走线的厚度(铜厚/减小线的厚度可以增大阻抗))我这里使用的是CITS25来计算阻抗。

qq_42379138的博客 8839

16张图解锁Spring的整体脉络

图解锁Spring的整体脉络

qq_36405748的博客 880

修改串口服务器,串口虚拟化 | 串口服务器Nport 5630 设置

原标题:串口虚拟化 | 串口服务器Nport 5630 设置JZGKCHINA项目改造,客户需要对IFix4.5进行升级,并优化通讯,在不改变原来配置的基础上逐步进行替换。需要将IFix升级到IFix5.9,数据库升级为SQL Server 2016,客户现场使用的智能仪表都是支持Modbus RTU协议的,因原来的手拉手的通讯方式的,当一台仪表掉线后,会影响到其他仪表的通讯,所以此次在不改变原有...

weixin_31688465的博客 2407

双系统引导修复:GRUB消失后恢复Windows与Ubuntu启动菜单

在UEFI引导架构下,操作系统启动依赖于EFI系统分区(ESP)中存储的引导加载程序。当多个系统共存时,引导管理器负责识别并加载不同操作系统的引导文件。然而,Windows更新或系统调整时常会重置UEFI启动顺序,导致GRUB引导菜单消失,直接进入Windows。这一问题的技术价值在于理解UEFI启动流程与多系统引导管理机制,掌握ESP分区结构与引导文件修复方法。常见的应用场景包括双系统安装后的引导修复、系统更新后的启动项丢失以及磁盘分区调整导致的引导故障。本文针对**GRUB引导菜单消失**和**UEFI

weixin_29895109的博客 292

(Spring学习11)Spring之事务底层源码解析

Spring之事务底层源码解析

a250029634的博客 275

李宏毅机器学习hw3

Homework 3 - Convolutional Neural Network 1作业概述 在food_11文件中有三个文件分别是training、validation、testing,打开training和validation可以看见总共有11种食物(0-10的类别编号),而testing是没有类别编号的。 然后用卷积神经网络对食物图片进行分类。 2导入用到的库 torch库 这个我弄了半天,直接pip总是timeout,用镜像源下下来又出现OSError: [WinError..

老问题的博客 4187

Spring理解,重要概念及图解,2023秋招spring常见八股文

pring的两大核心:IoC和AOP;IOC容器:BeanFactory和ApplicationContext;DI依赖注入;AOP和动态代理关联;spring中bean的安全问题;事务;Spring事务失效的场景;spring中bean的生命周期

zj5566778899的博客 1214

Claude Code长期记忆:优雅的上下文管理

ClaudeCode的上下文管理系统采用三层架构设计,通过智能压缩和优化策略在记忆容量与性能间取得平衡。系统包含短期记忆(高速工作区)、中期记忆(智能蒸馏器)和长期记忆(持久化知识库)。当Token使用率达到92%阈值时启动wU2压缩器,采用8段式结构化摘要保留关键信息,并配合智能文件恢复和渐进式预警机制。该系统通过分层处理、优雅降级和透明化交互,为AI应用提供了高效可靠的上下文管理方案,展现了在有限资源下实现智能记忆的艺术。

m0_53117338的博客 3812

芒果YOLOv5改进55:损失函数篇之KLD:即插即用 芒果首发原创新颖|全网唯一更新KLD改进博客,通过 Kullback-Leibler 散度学习用于目标检测的高精度边界框,提升YOLOv5小目标

YOLOv5改进KLD损失函数:即插即用 原创新颖|芒果专栏独家首发

包括YOLOv5、YOLOv7、YOLOv8等模型改进 661

美团四轮面试全流程解析:从技术面到HR面的准备策略

面试是对候选人基本功、项目深度与思维方式的系统考察,技术面试中常涉及数据结构、算法、数据库、缓存等基础概念,理解其底层原理是应对代码题与场景题的前提。项目经验则是综合能力的直接体现,通过结构化复盘梳理难点、解法与量化成果,能有效支撑面试官的层层追问。在此基础上,掌握分轮次的答题策略,如基础问答、项目深挖、开放式设计题及HR沟通,能显著提升通过率。无论目标是技术岗、产品岗还是运营岗,均可从面试的通用逻辑中获益。本文结合美团四轮面试的真实经验,系统拆解每轮考察重点、高频问题与避坑技巧,为准备互联网大厂面试的候选

weixin_33788244的博客 478

华为OD机试真题2025双机位A卷 Java&Go 实现【采购订单】

在一个采购系统中,采购申请(PR)需要经过审批后才能生成采购订单(PO)。每个PR包含商品的单价(假设相同商品的单价一定是一样的)及数量信息。系统要求对商品进行分类处理:单价高于100元的商品需要单独处理,单价低于或等于100元的相同商品可以合并到同一采购订单PO中。针对单价低于100的小额订单,如果量大可以打折购买。

MISAYAONE的博客 1256

Java大厂面试深度解析:JVM、集合框架与Spring实战

Java虚拟机(JVM)作为Java生态的核心基石,其内存模型、类加载机制和垃圾回收原理是构建高并发应用的关键。以HashMap为代表的集合框架通过哈希算法和红黑树实现高效数据存取,而ConcurrentHashMap则采用CAS+synchronized优化并发性能。Spring框架通过三级缓存解决循环依赖问题,其事务传播机制在实际开发中需要特别注意边界条件。在分布式系统中,Redis分布式锁的实现需要考虑锁标识、可重入性和自动续期等进阶特性。理解这些核心技术原理,能够帮助开发者应对电商秒杀、实时数据处理

chuange6363的博客 384

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

最新版校园招聘进大厂系列----------(2)美团篇 -----未完待续

由于大家相对来说面试经验相对较少,博主这里暂时就简单给大家提几个意见,简历要做的简而精,毕竟每一个面试官的时间和精力都有限,(这里说的是技术面试官)一般都是忙里偷闲进行面试,平时还会有迭代需求要做,同时还会有bug开各种无聊的会议,所以简历推荐大家写的简而精不要写一些无关紧要的东西。...

奇点 1818

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

双非读者,一举拿下阿里、字节、美团、京东、虾皮offer

IT行业的高薪资吸引了很多人,竞争也日益激烈。要想在这场激烈的角逐中胜出,首先得保证非常充足的学习时间,后端要学的东西非常非常之多,光算法就可以让你学好几个月,更何况还有那么多的专业知识和语言方面的知识。如此庞大的知识体系,没个一年半载是啃不下来的,所以一定要以高三或者考研的状态来要求自己。只有学习强度上去了,才能更好地理解消化知识,三天打鱼两天晒网是不可能学到东西的。一定要保证时间的投入,这是秋招成功的基本前提!其次,秋招一定是越早准备,越早投递,越早面试越好,很多人都认为自己没有准备好,不敢投递。...

weixin_70730532的博客 465

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Java高级:35岁程序员半月4轮面试,给大家安排上!

你真的需要分布式锁吗? 用到分布式锁说明遇到了多个进程共同访问同一个资源的问题。一般是在两个场景下会防止对同一个资源的重复访问: **提高效率。**比如多个节点计算同一批任务,如果某个任务已经有节点在计算了,那其他节点就不用重复计算了,以免浪费计算资源。不过重复计算也没事,不会造成其他更大的损失。也就是允许偶尔的失败。 **保证正确性。**这种情况对锁的要求就很高了,如果重复计算,会对正确性造成影响。这种不允许失败。 引入分布式锁势必要引入一个第三方的基础设施,比如 MySQL,Redis,Zo

m0_56712948的博客 216

深入分布式缓存从原理到实践技术分享,看完这篇彻底明白了

1. 前言 相信大家对 ZooKeeper 应该不算陌生。但是你真的了解 ZooKeeper 到底有啥用不?如果别人/面试官让你给他讲讲对于 ZooKeeper 的认识,你能回答到什么地步呢? 拿我自己来说吧!我本人曾经使用 Dubbo 来做分布式项目的时候,使用了 ZooKeeper 作为注册中心。为了保证分布式系统能够同步访问某个资源,我还使用 ZooKeeper 做过分布式锁。另外,我在学习 Kafka 的时候,知道 Kafka 很多功能的实现依赖了 ZooKeeper。 前几天,总结项目经验的时候,

249

Java全栈开发面试要点与实战解析

Java全栈开发涉及前后端技术栈的深度融合,要求开发者具备从基础语言特性到分布式系统设计的全方位能力。在JVM层面,理解内存模型与GC机制是诊断OOM问题的关键,比如通过heapdump分析内存泄漏。并发编程中,掌握AQS原理和线程池优化能有效提升系统性能。Spring框架的IOC容器和事务传播机制是面试高频考点,需要深入理解Bean生命周期和PROPAGATION_REQUIRES_NEW等事务行为。分布式场景下,缓存一致性方案如延迟双删和BloomFilter应用,以及Seata的TCC模式实践,都是解

weixin_30889885的博客 535

美团java研发岗二面:北京java开发薪资水平

Java面试指南 今天给大家分享一份【Java 面试 + Java 后端技术学习指南】:一份通向理想互联网公司的面试指南,包括 Java,技术面试必备基础知识、Leetcode、计算机操作系统、计算机网络、系统设计、分布式、数据库(MySQL、Redis)、Java 项目实战等, 新鲜出炉! 此手册内容专注 Java面试 ,这是本人花费了半年的时间总结的【Java 面试 + Java 后端技术学习指南】,目前本人已经拿到了腾讯等大厂offer,只要大家把这上面的知识点都搞明白,进入前 5 的互联网公司是绝对

m0_57983911的博客 205

Java面试全攻略:从基础到微服务架构深度解析

Java技术栈作为企业级开发的核心,其知识体系涵盖从JVM内存模型到微服务架构的广泛领域。理解JVM内存模型中的happens-before原则和volatile关键字实现原理,是解决并发编程问题的关键基础。在分布式系统中,Spring事务传播行为与分布式事务选型策略直接影响系统一致性保障。通过分析HashMap扩容机制对分布式锁的影响等实际案例,可以建立技术模块间的关联认知。本文结合大厂面试真题,系统梳理Java工程师必备的核心技术栈,包括JVM并发控制、Spring循环依赖处理、Redis缓存一致性等高

weixin_33906657的博客 454

美团后台开发笔试真题复盘:考点拆解与编程题解

动态规划是后台开发笔试中的高频考点,其核心思想是通过状态定义与转移方程将复杂问题拆解为可递推的子问题。掌握完全背包、最大连续子数组等经典模型,不仅有助于应对在线笔试,更能提升工程中的算法设计与优化能力。在计算机基础层面,TCP连接管理、数据库索引、进程线程模型等知识同样决定了一名后端工程师能否高效排查线上故障。无论是支付场景中的一致性设计,还是高并发下的缓存策略,这些技术原理都贯穿于实际业务系统。以美团校招后台开发笔试真题为例,完整复盘当年的题型构成、高频考点与两道编程题的思考过程,为准备互联网大厂校招的开

weixin_30314793的博客 425

01 Java 程序员为什么要聊 AIGC?现在面试到底卷成什么样了

本文从Java后端工程师的视角,深入浅出地解释了AIGC和RAG的核心概念,通过生动的比喻让读者快速理解这些技术。文章分析了为什么AIGC/RAG成为Java面试新热点,并提供了从入门到进阶的面试准备路线图,最后给出了面试官视角的标准回答模板,帮助Java开发者快速掌握AI时代必备技能。

weixin_42023666的博客 610

美团研发工程师模拟笔试题复盘:从数据结构到高并发系统设计

数据结构与算法是研发岗位笔试的第一道门槛,也是大厂技术面试中区分度的核心来源。从动态规划、链表反转到滑动窗口,每一类题型都在考察候选人能否将基础原理灵活迁移到真实业务场景中。操作系统中的进程线程模型、死锁条件,网络中的TCP三次握手与HTTP状态码,以及数据库索引失效与订单表设计,共同构成了完整的研发知识体系。在交易系统、外卖配送等高并发场景下,如何通过乐观锁、缓存和消息队列解决库存扣减与流量冲击,更是工程能力的直接体现。这套美团2016年研发工程师模拟笔试题虽然年代较早,但其覆盖的基础知识与能力模型至今依

weixin_34343308的博客 419

Java面试从背诵到理解:7天构建后端知识体系与场景题拆解

最近和几位刚经历秋招的朋友聊天,发现一个普遍现象:很多人刷了上百道八股文,面试时依然答不到点上。面试官问“HashMap的扩容机制”,他们能背出“默认容量16,负载因子0.75,扩容2倍”,但被追问“为什么是2的幂次方?”、“头插法改尾插法解决了什么问题?”时,就卡壳了。更别提那些结合真实业务场景的“场景题”,比如“如何设计一个高并发的秒杀系统?”、“线上CPU飙升100%如何排查?”,更是无从下手。 问题出在哪里? 面试八股文,从来不是“背多分”的考试,而是一场“理解力”和“工程思维”的较量。 面试官抛出

a2850363633的博客 429

AI正在重塑面试流程:2026年程序员求职形式全解析,从机试到终面的完整通关指南

本文从简历筛选、笔试机试、技术面试、HR面试到评估标准,全面拆解AI时代程序员面试流程的形式变化,附面试准备清单和不同规模公司面试差异分析。

热爱技术与前沿创新,深耕科技领域,在科创中精进自我;探索技术乐趣,分享技术干货与成长心得,以技术为伴,热爱生活,在科创与生活中双向成长。 232
上一篇: GitHub 前 CTO:全面微服务是最大的架构错误!网友:这不是刚改完吗...
下一篇: 5款程序员必备的免费在线画图工具,超级好用!
公众号-芋道源码
博客等级 码龄10年 1625粉丝 377原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值