10 分钟吃透 MyBatis 核心|从底层原理到实战技巧,Java 开发者必藏(无废话干货)

作为 Java 开发者,你一定被 JDBC 折磨过:写一堆重复的连接、关闭代码,SQL 硬编码在 Java 里,参数手动 set、结果手动映射,折腾半天,真正的业务逻辑没写几行

直到 MyBatis 出现,彻底拯救了被 JDBC 折磨的程序员 ------ 它不是替代 JDBC,而是站在 JDBC 的肩膀上,用 "SQL 与代码分离" 的思想,把繁琐的样板代码全部封装,让你只专注 SQL 和结果映射,开发效率直接翻倍。

这篇文章,不跳步、不玄学、不堆砌概念,用 "道法术器" 四层逻辑,从 "为什么要有 MyBatis" 到 "生态工具怎么用",把 MyBatis 彻底讲透。全程专业准确,新手能快速上手,老手能查漏补缺,建议立刻收藏,面试、开发、复盘都能直接翻。


第一步:问题的原点 ------ 为什么要有 MyBatis?(先搞懂 "痛点",再学 "用法")

在 MyBatis 出现之前,用 JDBC 操作数据库,堪称 "体力活天花板",全程踩坑不重样,我们一步步还原当时的绝望:

痛点 1:样板代码多到窒息,业务逻辑被淹没

开发一个简单的 "根据 ID 查用户" 功能,JDBC 要写几十行代码,真正的业务逻辑(SQL 查询)只占 1 行,剩下全是重复的样板代码:

java

运行

ini 复制代码
// 传统JDBC的噩梦:重复代码占90%
public User findUserById(Long id) {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    User user = null;
    
    try {
        // 1. 加载驱动(重复)
        Class.forName("com.mysql.jdbc.Driver");
        
        // 2. 获取连接(重复)
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
        
        // 3. 创建语句(重复)
        String sql = "SELECT * FROM user WHERE id = ?";
        stmt = conn.prepareStatement(sql);
        stmt.setLong(1, id);
        
        // 4. 执行查询(核心逻辑,仅1行)
        rs = stmt.executeQuery();
        
        // 5. 处理结果集(重复+枯燥)
        if (rs.next()) {
            user = new User();
            user.setId(rs.getLong("id"));
            user.setName(rs.getString("name"));
            user.setAge(rs.getInt("age"));
        }
        
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 6. 关闭资源(重复+易错)
        if (rs != null) try { rs.close(); } catch (Exception e) {}
        if (stmt != null) try { stmt.close(); } catch (Exception e) {}
        if (conn != null) try { conn.close(); } catch (Exception e) {}
    }
    
    return user;
}

痛点 2:参数设置麻烦,类型错了就报错

每个 SQL 占位符都要手动setXXX,参数多了容易数错位置,类型不匹配直接抛异常,调试半天找不到问题。

痛点 3:结果集映射枯燥,表字段多了写到手软

数据库字段和 Java 对象属性要手动映射,表有 10 个字段就要写 10 行getXXX+setXXX,字段名不一致(如create_time vs createTime)还要额外处理,纯纯的体力活。

痛点 4:SQL 与代码耦合,改 SQL 要重新编译

SQL 硬编码在 Java 字符串里,没有语法高亮、没有自动补全,改个字段都要重新编译、打包、部署,效率极低,DBA 想优化 SQL 都无从下手。

痛点 5:没有缓存,重复查询浪费数据库资源

每次查询都要走数据库,哪怕是相同的查询条件,重复请求会给数据库带来不必要的压力,性能拉胯。

核心结论

MyBatis 存在的唯一理由:解决 JDBC"太底层、太繁琐" 的痛点 ------ 把连接管理、参数设置、结果映射、资源关闭等重复样板代码封装起来,让开发者只专注核心的 SQL 和业务逻辑,同时保留对 SQL 的完全控制权


第二步:道的层面 ------MyBatis 的根本思想(懂道,才懂它的设计精髓)

"道" 是 MyBatis 的灵魂,回答 "为什么这么设计",搞懂这 3 个核心思想,就抓住了 MyBatis 的本质,面试被问也能对答如流。

道 1:SQL 与代码分离(MyBatis 的核心中的核心)

这是 MyBatis 最核心的设计思想,也是它能解决 "SQL 与代码耦合" 的根本。

  • 传统 JDBC:SQL 硬编码在 Java 代码里,改 SQL = 改代码 = 重新编译;
  • MyBatis:将 SQL 从 Java 代码中剥离,放到 XML 配置文件(或注解)中,SQL 变更无需修改 Java 代码,也不用重新编译。

✅ 实战示例(一看就懂):

xml

xml 复制代码
<!-- XML中写SQL,脱离Java代码,支持语法高亮、自动补全 -->
<mapper namespace="com.example.mapper.UserMapper">
    <select id="selectUserById" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

java

运行

ini 复制代码
// Java代码中只剩接口调用,简洁明了
User user = userMapper.selectUserById(1L);

✅ 核心价值:

  • 解耦:SQL 归 DBA 管,代码归开发管,分工明确;
  • 可维护:SQL 集中管理,便于优化、审查;
  • 灵活:支持动态 SQL,可根据条件拼接不同 SQL。

道 2:ORM 映射 ------ 对象与数据库的桥梁

MyBatis 的本质是轻量级 ORM(对象关系映射)框架,解决 "Java 对象和数据库记录的阻抗不匹配" 问题。

✅ 常见的 "阻抗不匹配" 场景:

  • 命名不一致:数据库字段create_time vs Java 属性createTime
  • 类型不一致:数据库datetime vs Java Date
  • 关系不一致:数据库一对多 vs Java 对象嵌套。

✅ MyBatis 的解决方案(结果映射):

xml

xml 复制代码
<!-- 自定义结果映射,解决字段与属性不匹配问题 -->
<resultMap id="userResultMap" type="User">
    <id property="id" column="id"/>          <!-- 主键映射 -->
    <result property="name" column="name"/>  <!-- 普通字段映射 -->
    <result property="age" column="age"/>
    <result property="createTime" column="create_time"/> <!-- 命名不一致映射 -->
</resultMap>

道 3:封装不变,变化定制(MyBatis 的 "轻量级" 精髓)

MyBatis 的核心理念是:把不变的、重复的 JDBC 样板代码封装,把变化的、业务相关的部分留给开发者定制------ 这也是它区别于 Hibernate(全自动化 ORM)的关键。

表格

不变的部分(框架封装) 变化的部分(开发者定制)
连接获取与关闭 SQL 语句(核心业务逻辑)
参数自动设置 参数类型 / 映射规则
结果集自动遍历 结果映射方式
异常统一处理 动态 SQL 条件
事务管理 返回值类型

✅ 核心价值:

  • 不剥夺控制权:开发者完全掌控 SQL,适合复杂查询、性能优化;
  • 不重复造轮子:框架搞定体力活,开发者专注业务,效率翻倍。

第三步:法的层面 ------MyBatis 的核心方法论(懂法,才会用对 MyBatis)

"法" 是实现 "道" 的路径和方法论,回答 "怎么做",这 4 个方法论,是 MyBatis 简化开发的核心,也是面试高频考点。

法 1:三层架构 ------ 各司其职,分工明确

MyBatis 的整体架构分为三层,每一层有明确的职责,保证框架的清晰性和可维护性:

plaintext

sql 复制代码
┌─────────────────────────────────────┐
│           API接口层                  │
│  SqlSession、Mapper接口代理(开发者直接用) │
├─────────────────────────────────────┤
│           数据处理层                  │
│  SQL解析、SQL执行、结果映射(核心逻辑)   │
├─────────────────────────────────────┤
│           基础支撑层                  │
│  连接管理、事务管理、缓存、配置加载(底层) │
└─────────────────────────────────────┘

各层核心职责(面试必记):

  • 接口层:提供给开发者的 API(SqlSession、Mapper 接口),接收调用请求,传递给数据处理层;
  • 数据处理层:MyBatis 的核心,负责 SQL 查找、解析、执行和结果映射,完成一次数据库操作;
  • 基础支撑层:提供底层通用能力(连接池、缓存、配置解析),为上层提供支撑。

法 2:四大核心组件的协作(MyBatis 的 "执行引擎")

MyBatis 的 SQL 执行流程,围绕四个核心组件展开,各司其职、高效协作:

表格

组件 核心职责 通俗类比
Executor(执行器) 调度执行流程,维护缓存,是核心调度者 项目经理
StatementHandler 封装 JDBC Statement,设置参数、执行 SQL 施工队
ParameterHandler 处理参数映射,把 Java 参数转 JDBC 参数 材料员
ResultSetHandler 处理结果集映射,把 JDBC 结果转 Java 对象 质检员

完整协作流程(面试必背):

  1. Executor 接收到查询请求,先查缓存(一级 / 二级);
  2. 缓存未命中,交给 StatementHandler 处理;
  3. StatementHandler 通过 ParameterHandler 设置 SQL 参数;
  4. 执行 SQL,得到 ResultSet;
  5. ResultSetHandler 将 ResultSet 映射成 Java 对象;
  6. 返回结果,同时存入缓存(一级缓存)。

法 3:动态代理实现 Mapper 接口(MyBatis 的 "黑科技")

MyBatis 最神奇的地方:只定义 Mapper 接口,不用写实现类,却能直接调用方法

✅ 实战示例:

java

运行

ini 复制代码
// 只定义接口,无实现类
public interface UserMapper {
    User selectUserById(Long id);
}

// 直接调用,框架自动生成实现
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectUserById(1L);

实现原理(动态代理,面试高频):

  1. sqlSession.getMapper()时,MyBatis 通过MapperProxyFactory创建 Mapper 接口的动态代理对象;
  2. 代理对象的invoke方法拦截所有接口方法调用;
  3. 根据方法名 + 参数类型,找到对应的MappedStatement(SQL 封装对象);
  4. 调用 SqlSession 的对应方法执行 SQL;
  5. 返回映射后的 Java 对象。

法 4:MappedStatement------SQL 的 "封装体"

MyBatis 将 XML / 注解中的每个 SQL 语句(<select>/<insert>等),封装成MappedStatement对象,存储在内存中,执行时直接取用。

MappedStatement 核心属性:

java

运行

arduino 复制代码
public class MappedStatement {
    private String id;                    // SQL唯一标识(namespace+id)
    private SqlSource sqlSource;           // SQL源(包含原始SQL和参数信息)
    private StatementType statementType;   // 语句类型(STATEMENT/PREPARED/CALLABLE)
    private ResultMap resultMap;           // 结果集映射配置
    private SqlCommandType sqlCommandType; // SQL类型(SELECT/UPDATE/INSERT/DELETE)
}

关键流程:

  • 初始化时:解析 XML / 注解,将每个 SQL 标签转为MappedStatement,存入Configuration(全局配置);
  • 执行时:根据statementId(如com.example.mapper.UserMapper.selectUserById)找到对应的MappedStatement,获取 SQL 和映射规则。

第四步:术的层面 ------MyBatis 的具体技术实现(懂术,能解决实际问题)

"术" 是具体的技术技巧,回答 "用什么工具实现",掌握这些,遇到 MyBatis 问题(动态 SQL 不生效、缓存异常等),能快速定位、解决。

术 1:配置解析与初始化流程(MyBatis 的 "启动流程")

MyBatis 的启动过程,本质是 "解析配置→构建全局上下文→初始化核心组件",简化流程如下:

plaintext

markdown 复制代码
1. 读取核心配置文件(mybatis-config.xml)
2. 创建XMLConfigBuilder,解析配置
   ├── 解析<environments>(数据源、事务管理器)
   ├── 解析<typeAliases>(类型别名,简化配置)
   ├── 解析<mappers>(映射文件路径)
   └── 构建Configuration对象(全局上下文)
3. 解析Mapper.xml文件
   ├── 每个SQL标签解析为MappedStatement
   ├── 每个<resultMap>解析为ResultMap对象
   └── 存入Configuration
4. 创建SqlSessionFactory(会话工厂)
5. 通过SqlSessionFactory获取SqlSession(操作入口)

✅ 关键点:Configuration是 MyBatis 的 "大脑",所有配置、映射规则、SQL 封装体都存在这里。

术 2:动态 SQL 的实现原理(MyBatis 的 "灵活利器")

动态 SQL(<if>/<where>/<foreach>)是 MyBatis 的核心亮点,能根据参数动态拼接 SQL,实现复杂查询。

✅ 实战示例:

xml

bash 复制代码
<select id="findUsers" resultType="User">
    SELECT * FROM users
    <where>
        <if test="name != null">AND name = #{name}</if>
        <if test="age != null">AND age = #{age}</if>
    </where>
</select>

实现原理(组合模式):

  1. 解析 XML 时,动态标签(<if>/<where>)被解析为SqlNode对象树(IfSqlNode/WhereSqlNode等);
  2. SqlSource管理这些SqlNode,负责动态拼接;
  3. 执行时,SqlSource根据传入参数,遍历SqlNode树,动态生成完整 SQL;
  4. 最终生成BoundSql对象(包含完整 SQL + 参数映射),交给 StatementHandler 执行。

术 3:TypeHandler------ 类型转换的 "翻译官"

Java 类型和 JDBC 类型的转换,由TypeHandler(类型处理器)负责,是 MyBatis 实现 ORM 映射的核心。

核心接口:

java

运行

csharp 复制代码
public interface TypeHandler<T> {
    // Java → JDBC(设置参数时)
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType);
    
    // JDBC → Java(获取结果时)
    T getResult(ResultSet rs, String columnName);
    T getResult(ResultSet rs, int columnIndex);
}

关键说明:

  • 内置 TypeHandler:MyBatis 提供了常用类型的处理器(如StringTypeHandler/IntegerTypeHandler);
  • 自定义 TypeHandler:支持自定义(如 JSON 字段、枚举类型),只需实现TypeHandler接口并配置。

术 4:缓存机制 ------ 一级缓存与二级缓存(性能优化核心)

MyBatis 内置两级缓存,减少数据库查询,提升性能:

表格

缓存级别 作用范围 默认状态 触发清空条件
一级缓存 SqlSession 级别(会话内) 开启(无需配置) 执行 update/delete/insert、commit、close、clearCache
二级缓存 Mapper 级别(跨会话) 关闭(需手动配置) 执行增删改操作、缓存过期、手动清空

✅ 开启二级缓存(Mapper.xml 中配置):

xml

xml 复制代码
<!-- 开启二级缓存:LRU淘汰策略,60秒过期,最多存512条,只读 -->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

术 5:插件的拦截机制(MyBatis 的 "扩展利器")

MyBatis 允许通过插件拦截四大核心对象的方法,插入自定义逻辑(如分页、日志、权限控制)。

核心原理:

  • 基于 JDK 动态代理,拦截Executor/StatementHandler/ParameterHandler/ResultSetHandler的方法;
  • 分页插件(PageHelper)、通用 Mapper 等,都是基于这个机制实现。

✅ 核心价值:无需修改 MyBatis 源码,即可扩展核心功能,灵活性拉满。


第五步:器的层面 ------MyBatis 生态的工具箱(懂器,能高效开发)

"器" 是 MyBatis 的具体工具和组件,回答 "有哪些东西可以用",这些工具覆盖开发、测试、优化全流程,开箱即用,大幅提升效率。

器 1:MyBatis 核心依赖(基础必备)

表格

依赖 说明
mybatis-x.x.x.jar MyBatis 核心框架,包含所有核心类
数据库驱动 MySQL/Oracle/PostgreSQL 等对应的 JDBC 驱动(如 mysql-connector-java)

器 2:MyBatis-Spring 整合包(Spring 项目必备)

将 MyBatis 无缝集成到 Spring 中,支持 Spring 容器管理 Mapper、事务统一管理:

xml

xml 复制代码
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>3.0.0</version>
</dependency>

核心功能:

  • SqlSessionFactoryBean:Spring 中创建 SqlSessionFactory;
  • MapperFactoryBean:将 Mapper 接口注入 Spring 容器;
  • 支持 Spring 事务管理,与@Transactional注解无缝兼容。

器 3:MyBatis-Plus------MyBatis 的 "增强神器"(国产之光)

MyBatis-Plus是 MyBatis 的增强工具,"只做增强,不做改变",大幅降低开发量:

核心功能(开发必备):

  • 通用 CRUD:继承BaseMapper,自动获得增删改查方法,无需写 SQL;
  • 条件构造器:用代码构造查询条件,替代动态 SQL;
  • 内置分页插件:一行代码实现分页,无需依赖 PageHelper;
  • 代码生成器:根据数据库表自动生成实体类、Mapper、Service。

✅ 实战示例(通用 CRUD):

java

运行

php 复制代码
// 继承BaseMapper,自动获得CRUD方法
public interface UserMapper extends BaseMapper<User> {}

// 条件构造器查询(无需写XML)
List<User> users = userMapper.selectList(
    new LambdaQueryWrapper<User>()
        .eq(User::getAge, 18)  // 年龄=18
        .like(User::getName, "张")  // 姓名含"张"
);

器 4:MyBatis Generator------ 代码生成器(解放双手)

MyBatis 官方提供的代码生成器,根据数据库表自动生成:

  • 实体类(POJO);
  • Mapper 接口;
  • XML 映射文件(包含基础 CRUD SQL)。

✅ 核心价值:减少重复代码编写,提升开发效率,规范代码格式。

器 5:PageHelper------ 分页插件(经典工具)

基于 MyBatis 插件机制实现的分页工具,一行代码实现分页:

java

运行

ini 复制代码
// 分页只需一行代码
PageHelper.startPage(1, 10); // 第1页,每页10条
List<User> users = userMapper.selectAll();
PageInfo<User> pageInfo = new PageInfo<>(users); // 分页结果

核心原理:

拦截Executorquery方法,自动在 SQL 后添加LIMIT子句,同时查询总记录数。


第六步:用比喻理解 MyBatis(道法术器全打通,新手也能懂)

很多人觉得 MyBatis 复杂,其实用 "定制化餐厅" 的比喻,就能轻松理解道法术器四层逻辑:

道的层面:餐厅的经营理念(为什么这么做)

  • SQL 与代码分离:菜单(SQL)和厨房(Java 代码)分开,客人(调用者)只看菜单点菜,不用管厨房怎么做;
  • ORM 映射:服务员(MyBatis)把后厨的菜品(数据库记录)翻译成客人能吃的样式(Java 对象);
  • 封装不变,变化定制:餐厅提供桌椅、餐具、服务流程(框架封装),但每道菜的配方(SQL)由厨师(开发者)定制。

法的层面:餐厅的运营流程(怎么做)

  • 三层架构:前台接待(接口层)、后厨烹饪(数据处理层)、库房采购(基础支撑层);
  • 四大组件协作:主厨(Executor)统筹,配菜员(ParameterHandler)备料,炒锅师傅(StatementHandler)做菜,传菜员(ResultSetHandler)摆盘;
  • 动态代理:客人只说 "点第 3 道菜"(调用 Mapper 接口),不用自己做菜,服务员自动通知后厨做好端上来;
  • MappedStatement:每道菜的菜谱(SQL)写成卡片挂墙上,后厨按卡片做菜。

术的层面:餐厅的操作技巧(具体怎么实现)

  • 配置解析:开业前,老板(框架)整理好菜单、员工分工、采购清单(配置文件);
  • 动态 SQL:客人说 "加点辣",厨师(框架)根据要求调整菜谱(动态拼接 SQL);
  • TypeHandler:厨师知道 "三克盐" 在勺子里是多少(Java-JDBC 类型转换);
  • 缓存:同一桌客人连续点同一道菜,后厨直接上上次做好的(一级缓存)。

器的层面:餐厅的工具设备(用什么做)

  • MyBatis 核心:餐厅的基础设施(灶台、餐具);
  • MyBatis-Spring:加盟连锁集团(Spring),统一采购、管理;
  • MyBatis-Plus:升级自动化设备(自动备菜、自动摆盘);
  • PageHelper:自动给每桌配分餐盘(分页功能)。

第七步:MyBatis 的执行流程全景图(面试必背)

结合以上分析,MyBatis 执行一条 SQL 的完整流程如下:

plaintext

markdown 复制代码
1. 加载配置文件 → 构建Configuration全局上下文
2. 通过SqlSessionFactory创建SqlSession(操作入口)
3. 获取Mapper接口的动态代理对象
4. 调用Mapper方法 → 动态代理拦截调用
5. SqlSession根据statementId找到MappedStatement
6. Executor执行查询
   ├── 检查一级缓存 → 命中则返回
   ├── 未命中 → 通过StatementHandler创建Statement
   ├── ParameterHandler设置SQL参数
   ├── 执行SQL,得到ResultSet
   ├── ResultSetHandler映射为Java对象
   └── 存入一级缓存
7. 返回Java对象
8. 关闭SqlSession(释放连接)

第八步:MyBatis 的本质是什么?(第一性原理总结)

用第一性原理来看,MyBatis 的本质是:一个轻量级持久层框架,通过 SQL 与代码分离、封装 JDBC 样板代码、实现 ORM 映射,在保留开发者对 SQL 完全控制权的前提下,大幅简化数据库操作,提升开发效率

它的核心价值,就是 "平衡"------ 平衡 "自动化" 和 "控制权":既像 Hibernate 一样减少重复劳动,又不像 Hibernate 那样屏蔽 SQL,完美适配 Java 开发者的需求。

从道法术器四个层次,总结 MyBatis 的核心:

表格

层次 核心内容 一句话总结
SQL 与代码分离、ORM 映射、封装不变变化定制 核心思想:保留 SQL 控制权,解放重复劳动
三层架构、四大组件协作、动态代理、MappedStatement 实现路径:组件分工,高效协作
配置解析、动态 SQL、TypeHandler、缓存、插件 技术细节:具体实现技巧
MyBatis-Plus、MyBatis-Spring、PageHelper 工具支撑:提升开发效率

✅ 关键提醒:MyBatis 不是替代 JDBC,而是 "封装" JDBC------ 它底层依然使用 JDBC 操作数据库,但把繁琐的细节全部封装,让开发者只关注核心的 SQL 和业务逻辑。这也是它能成为 Java 持久层框架 "事实标准" 的根本原因。


总结:MyBatis 道法术器速查表(收藏备用,面试 / 开发直接翻)

表格

层次 核心内容 核心价值
SQL 与代码分离、ORM 映射、封装不变变化定制 理解设计理念,知道 "为什么这么做"
三层架构、四大组件、动态代理、MappedStatement 掌握核心方法,知道 "怎么做"
配置解析、动态 SQL、TypeHandler、缓存、插件 解决实际问题,掌握 "具体实现"
MyBatis-Plus、MyBatis-Spring、PageHelper 用好生态工具,提升开发效率

🔥 互动话题

学 MyBatis 时,你最头疼的问题是什么?

  • 动态 SQL 拼接出错,条件判断不生效
  • 一级缓存导致数据不一致,排查无方向
  • 结果映射不匹配,字段值为 null
  • 分页插件使用不当,总记录数错误
  • 面试被问 MyBatis 底层原理,答不上来

评论区留下你的痛点,我会挑高频问题,下期专门出《MyBatis 常见坑排查实战》,手把手教你解决,还会附赠 MyBatis 面试高频题合集,帮你轻松应对面试!

相关推荐
隔壁小邓2 小时前
分布式事务
java·后端
啦啦啦_99992 小时前
1. AI 学习目录
java·人工智能
不懂英语的程序猿2 小时前
【Java工具类】Java提取最新错误日志(附 AI 对接思路)
java
indexsunny2 小时前
互联网大厂Java面试实录:从Spring Boot到微服务架构的深度剖析
java·spring boot·redis·kafka·microservices·互联网大厂·面试经验
格鸰爱童话2 小时前
向AI学习项目技能(二)
java·人工智能·python·学习
@yanyu6662 小时前
第一个前后端分离项目
java·vue.js·springboot
一叶飘零_sweeeet2 小时前
垃圾回收核心算法:从底层逻辑到生产环境架构选型指南
java·垃圾回收算法
minji...2 小时前
Linux 基础IO (三) (用户缓冲区/内核缓冲区深刻理解)
java·linux·运维·服务器·c++·算法
无心水2 小时前
【常见错误】1、Java并发工具类四大坑:从ThreadLocal到ConcurrentHashMap,你踩过几个?
java·开发语言·后端·架构·threadlocal·concurrent·java并发四大坑